MATSIM
SwissRailRaptor.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.* *
3  *
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2023 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package ch.sbb.matsim.routing.pt.raptor;
22 
23 import java.util.ArrayList;
24 import java.util.Collection;
25 import java.util.Collections;
26 import java.util.Iterator;
27 import java.util.List;
28 import java.util.Map;
29 import java.util.function.Supplier;
30 
31 import org.apache.logging.log4j.LogManager;
32 import org.apache.logging.log4j.Logger;
33 import org.matsim.api.core.v01.Id;
37 import org.matsim.core.config.Config;
41 import org.matsim.core.utils.misc.Time;
46 
49 
56 public class SwissRailRaptor implements TransitRouter {
57 
58  private static final Logger log = LogManager.getLogger(SwissRailRaptor.class);
59 
60  private final SwissRailRaptorData data;
61  private final SwissRailRaptorCore raptor;
65 
66  private boolean treeWarningShown = false;
67 
69  RaptorParametersForPerson parametersForPerson,
70  RaptorRouteSelector routeSelector,
71  RaptorStopFinder stopFinder,
72  RaptorInVehicleCostCalculator inVehicleCostCalculator,
73  RaptorTransferCostCalculator transferCostCalculator) {
74  this.data = data;
75  this.raptor = new SwissRailRaptorCore(data, inVehicleCostCalculator, transferCostCalculator);
76  this.parametersForPerson = parametersForPerson;
77  this.defaultRouteSelector = routeSelector;
78  this.stopFinder = stopFinder;
79  }
80 
81  @Override
82  public List<? extends PlanElement> calcRoute(RoutingRequest request) {
83  final Facility fromFacility = request.getFromFacility();
84  final Facility toFacility = request.getToFacility();
85  final double departureTime = request.getDepartureTime();
86  final Person person = request.getPerson();
87  final Attributes routingAttributes = request.getAttributes();
88 
89  RaptorParameters parameters = this.parametersForPerson.getRaptorParameters(person);
90  if (parameters.getConfig().isUseRangeQuery()) {
91  return this.performRangeQuery(fromFacility, toFacility, departureTime, person, routingAttributes, parameters);
92  }
93  List<InitialStop> accessStops = findAccessStops(fromFacility, toFacility, person, departureTime, routingAttributes, parameters);
94  List<InitialStop> egressStops = findEgressStops(fromFacility, toFacility, person, departureTime, routingAttributes, parameters);
95 
96  RaptorRoute foundRoute = this.raptor.calcLeastCostRoute(departureTime, fromFacility, toFacility, accessStops, egressStops, parameters, person);
97  RaptorRoute directWalk = createDirectWalk(fromFacility, toFacility, departureTime, person, parameters);
98 
99  /*
100  * foundRoute.parts.size() == 0 can happen if SwissRasilRaptorCore.createRaptorRoute() finds a trip made up of,
101  * only 2 parts which consists only of an access and an egress leg without any pt leg inbetween.
102  */
103  if (directWalk.getTotalCosts() * parameters.getDirectWalkFactor() < foundRoute.getTotalCosts()) {
104  foundRoute = directWalk;
105  }
107  if (foundRoute == null || (foundRoute.parts.size() == 0 && forbidPurelyIntermodalRoutes)) {
108  if (person == null) {
109  log.debug("No route found for person null: trip from x=" + fromFacility.getCoord().getX() + ",y=" + fromFacility.getCoord().getY() + " departure at " + departureTime + " to x=" + toFacility.getCoord().getX() + ",y=" + toFacility.getCoord().getY());
110  } else {
111  log.debug("No route found for person " + person.getId() + ": trip from x=" + fromFacility.getCoord().getX() + ",y=" + fromFacility.getCoord().getY() + " departure at " + departureTime + " to x=" + toFacility.getCoord().getX() + ",y=" + toFacility.getCoord().getY());
112  }
113  return null;
114  }
115  else if ((hasNoPtLeg(foundRoute.parts))){
116  if (forbidPurelyIntermodalRoutes){
117  return null;
118  }
119  }
120 
121 
122  List<? extends PlanElement> legs = RaptorUtils.convertRouteToLegs(foundRoute, this.data.config.getTransferWalkMargin());
123  return legs;
124  }
125 
126  private boolean hasNoPtLeg(List<RoutePart> parts) {
127  for (RoutePart part : parts) {
128  // if the route part has a TransitLine, it must be a real pt leg
129  if (part.line != null) {
130  return false;
131  }
132  }
133  return true;
134  }
135 
136  private List<? extends PlanElement> performRangeQuery(Facility fromFacility, Facility toFacility, double desiredDepartureTime, Person person, Attributes routingAttributes, RaptorParameters parameters) {
137  SwissRailRaptorConfigGroup srrConfig = parameters.getConfig();
138 
139  String subpopulation = PopulationUtils.getSubpopulation(person);
141 
142  double earliestDepartureTime = desiredDepartureTime - rangeSettings.getMaxEarlierDeparture();
143  double latestDepartureTime = desiredDepartureTime + rangeSettings.getMaxLaterDeparture();
144 
145  if (this.defaultRouteSelector instanceof ConfigurableRaptorRouteSelector selector) {
146 
148 
149  selector.setBetaTransfer(params.getBetaTransfers());
150  selector.setBetaTravelTime(params.getBetaTravelTime());
151  selector.setBetaDepartureTime(params.getBetaDepartureTime());
152  }
153 
154  return this.calcRoute(fromFacility, toFacility, earliestDepartureTime, desiredDepartureTime, latestDepartureTime, person, routingAttributes, this.defaultRouteSelector);
155  }
156 
157  public List<? extends PlanElement> calcRoute(Facility fromFacility, Facility toFacility, double earliestDepartureTime, double desiredDepartureTime, double latestDepartureTime, Person person, Attributes routingAttributes) {
158  return calcRoute(fromFacility, toFacility, earliestDepartureTime, desiredDepartureTime, latestDepartureTime, person, routingAttributes, this.defaultRouteSelector);
159  }
160 
161  public List<? extends PlanElement> calcRoute(Facility fromFacility, Facility toFacility, double earliestDepartureTime, double desiredDepartureTime, double latestDepartureTime, Person person, Attributes routingAttributes, RaptorRouteSelector selector) {
162  RaptorParameters parameters = this.parametersForPerson.getRaptorParameters(person);
163  List<InitialStop> accessStops = findAccessStops(fromFacility, toFacility, person, desiredDepartureTime, routingAttributes, parameters);
164  List<InitialStop> egressStops = findEgressStops(fromFacility, toFacility, person, desiredDepartureTime, routingAttributes, parameters);
165 
166  List<RaptorRoute> foundRoutes = this.raptor.calcRoutes(earliestDepartureTime, desiredDepartureTime, latestDepartureTime, fromFacility, toFacility, accessStops, egressStops, parameters, person);
167  RaptorRoute foundRoute = selector.selectOne(foundRoutes, desiredDepartureTime);
168  RaptorRoute directWalk = createDirectWalk(fromFacility, toFacility, desiredDepartureTime, person, parameters);
169 
170  if (foundRoute == null || foundRoute.parts.size() == 0 || hasNoPtLeg(foundRoute.parts)) {
171  if (person == null) {
172  log.debug("No route found for person null: trip from x=" + fromFacility.getCoord().getX() + ",y=" + fromFacility.getCoord().getY() + " departure at " + desiredDepartureTime + " to x=" + toFacility.getCoord().getX() + ",y=" + toFacility.getCoord().getY());
173  } else {
174  log.debug("No route found for person " + person.getId() + ": trip from x=" + fromFacility.getCoord().getX() + ",y=" + fromFacility.getCoord().getY() + " departure at " + desiredDepartureTime + " to x=" + toFacility.getCoord().getX() + ",y=" + toFacility.getCoord().getY());
175  }
176  return null;
177  }
178  if (directWalk.getTotalCosts() * parameters.getDirectWalkFactor() < foundRoute.getTotalCosts()) {
179  foundRoute = directWalk;
180  }
181  List<? extends PlanElement> legs = RaptorUtils.convertRouteToLegs(foundRoute, this.data.config.getTransferWalkMargin());
182  // TODO adapt the activity end time of the activity right before this trip
183  /* Sadly, it's not that easy to find the previous activity, as we only have from- and to-facility
184  * and the departure time. One would have to search through the person's selectedPlan to find
185  * a matching activity, but what if an agent travels twice a day between from- and to-activity
186  * and it only sets the activity duration, but not the end-time?
187  * One could try to come up with some heuristic, but that would be very error-prone and
188  * not satisfying. The clean solution would be to implement our own PlanRouter which
189  * uses our own TripRouter which would take care of adapting the departure time,
190  * but sadly PlanRouter is hardcoded in several places (e.g. PrepareForSimImpl), so it
191  * cannot easily be replaced. So I fear I currently don't see a simple solution for that.
192  * mrieser / march 2018.
193  */
194  return legs;
195  }
196 
197  public List<RaptorRoute> calcRoutes(Facility fromFacility, Facility toFacility, double earliestDepartureTime, double desiredDepartureTime, double latestDepartureTime, Person person, Attributes routingAttributes) {
198  RaptorParameters parameters = this.parametersForPerson.getRaptorParameters(person);
199  List<InitialStop> accessStops = findAccessStops(fromFacility, toFacility, person, desiredDepartureTime, routingAttributes, parameters);
200  List<InitialStop> egressStops = findEgressStops(fromFacility, toFacility, person, desiredDepartureTime, routingAttributes, parameters);
201 
202  List<RaptorRoute> foundRoutes = this.raptor.calcRoutes(earliestDepartureTime, desiredDepartureTime, latestDepartureTime, fromFacility, toFacility, accessStops, egressStops, parameters, person);
203  RaptorRoute directWalk = createDirectWalk(fromFacility, toFacility, desiredDepartureTime, person, parameters);
204 
205  if (foundRoutes == null) {
206  foundRoutes = new ArrayList<>(1);
207  }
208  Iterator<RaptorRoute> iter = foundRoutes.iterator();
209  while (iter.hasNext()) {
210  RaptorRoute foundRoute = iter.next();
211  if (foundRoute.parts.size() == 0 || hasNoPtLeg(foundRoute.parts)) {
212  iter.remove();
213  }
214  }
215  if (foundRoutes.isEmpty() || directWalk.getTotalCosts() * parameters.getDirectWalkFactor() < foundRoutes.get(0).getTotalCosts()) {
216  foundRoutes.add(directWalk); // add direct walk if it seems plausible
217  }
218  return foundRoutes;
219  }
220 
221  public Map<Id<TransitStopFacility>, SwissRailRaptorCore.TravelInfo> calcTree(TransitStopFacility fromStop, double departureTime, RaptorParameters parameters, Person person) {
222  return this.calcTree(Collections.singletonList(fromStop), departureTime, parameters, person);
223  }
224 
225  public Map<Id<TransitStopFacility>, SwissRailRaptorCore.TravelInfo> calcTree(Collection<TransitStopFacility> fromStops, double departureTime, RaptorParameters parameters, Person person) {
227  log.warn("SwissRailRaptorData was not initialized with full support for tree calculations and may result in unexpected results. Use `RaptorStaticConfig.setOptimization(RaptorOptimization.OneToAllRouting)` to fix this issue.");
228  this.treeWarningShown = true;
229  }
230  List<InitialStop> accessStops = new ArrayList<>();
231  for (TransitStopFacility stop : fromStops) {
232  accessStops.add(new InitialStop(stop, 0, 0, 0, null));
233  }
234  return this.calcLeastCostTree(accessStops, departureTime, parameters, person, null);
235  }
236 
237  public Map<Id<TransitStopFacility>, SwissRailRaptorCore.TravelInfo> calcTree(Facility fromFacility, double departureTime, Person person, Attributes routingAttributes) {
238  RaptorParameters parameters = this.parametersForPerson.getRaptorParameters(person);
239  List<InitialStop> accessStops = findAccessStops(fromFacility, fromFacility, person, departureTime, routingAttributes, parameters);
240  return this.calcLeastCostTree(accessStops, departureTime, parameters, person, null);
241  }
242 
248  public void calcTreesObservable(TransitStopFacility stopFacility, double earliestDepartureTime, double latestStartTime, RaptorParameters parameters, Person person, RaptorObserver observer) {
250  log.warn("SwissRailRaptorData was not initialized with full support for tree calculations and may result in unexpected results. Use `RaptorStaticConfig.setOptimization(RaptorOptimization.OneToAllRouting)` to fix this issue.");
251  this.treeWarningShown = true;
252  }
253  parameters.setExactDeparturesOnly(true);
254 
255  List<InitialStop> accessStops = List.of(new InitialStop(stopFacility, 0, 0, 0, null));
256 
257  int[] routeStopIndices = this.data.routeStopsPerStopFacility.get(stopFacility);
258 
259  List<Double> departureTimes = new ArrayList<>();
260 
261  for (int routeStopIndex : routeStopIndices) {
262  SwissRailRaptorData.RRouteStop routeStop = this.data.routeStops[routeStopIndex];
263  SwissRailRaptorData.RRoute route = this.data.routes[routeStop.transitRouteIndex];
264  int fromIndex = route.indexFirstDeparture;
265  int toIndex = fromIndex + route.countDepartures;
266  for (int depIndex = fromIndex; depIndex < toIndex; depIndex++) {
267  double departureTime = this.data.departures[depIndex] + routeStop.departureOffset;
268  if (departureTime >= earliestDepartureTime && departureTime <= latestStartTime) {
269  departureTimes.add(departureTime);
270  }
271  }
272  }
273  departureTimes.sort(Double::compare);
274  // we might have some departure times multiple times. helper variable to skip those
275  double lastDepTime = -1;
276  for (double departureTime : departureTimes) {
277  if (departureTime > lastDepTime) {
278  calcLeastCostTree(accessStops, departureTime, parameters, person, observer);
279  lastDepTime = departureTime;
280  }
281  }
282  }
283 
284  private Map<Id<TransitStopFacility>, SwissRailRaptorCore.TravelInfo> calcLeastCostTree(Collection<InitialStop> accessStops, double departureTime, RaptorParameters parameters, Person person, RaptorObserver observer) {
285  return this.raptor.calcLeastCostTree(departureTime, accessStops, parameters, person, observer);
286  }
287 
289  return this.data;
290  }
291 
292  private List<InitialStop> findAccessStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters) {
293  return this.stopFinder.findStops(fromFacility, toFacility, person, departureTime, routingAttributes, parameters, this.data, RaptorStopFinder.Direction.ACCESS);
294  }
295 
296  private List<InitialStop> findEgressStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters) {
297  return this.stopFinder.findStops(fromFacility, toFacility, person, departureTime, routingAttributes, parameters, this.data, RaptorStopFinder.Direction.EGRESS);
298  }
299 
300  // TODO: replace with call to FallbackRoutingModule ?!
301  private RaptorRoute createDirectWalk(Facility fromFacility, Facility toFacility, double departureTime, Person person, RaptorParameters parameters) {
302  double beelineDistance = CoordUtils.calcEuclideanDistance(fromFacility.getCoord(), toFacility.getCoord());
303  double walkTime = beelineDistance / parameters.getBeelineWalkSpeed();
304  double walkCost_per_s = -parameters.getMarginalUtilityOfTravelTime_utl_s(TransportMode.walk);
305  double walkCost = walkTime * walkCost_per_s;
306  double beelineDistanceFactor = this.data.config.getBeelineWalkDistanceFactor();
307 
308  RaptorRoute route = new RaptorRoute(fromFacility, toFacility, walkCost);
309  route.addNonPt(null, null, departureTime, walkTime, beelineDistance * beelineDistanceFactor, TransportMode.walk);
310  return route;
311  }
312 
313  public static class Builder {
314  private final SwissRailRaptorData data;
320 
321  public Builder(SwissRailRaptorData data, Config config) {
322  this.data = data;
323  this.parametersForPerson = new DefaultRaptorParametersForPerson(config);
324  }
325 
326  public Builder with(RaptorParametersForPerson parametersForPerson) {
327  this.parametersForPerson = parametersForPerson;
328  return this;
329  }
330 
331  public Builder with(RaptorRouteSelector routeSelector) {
332  this.routeSelector = routeSelector;
333  return this;
334  }
335 
336  public Builder with(RaptorStopFinder stopFinder) {
337  this.stopFinder = stopFinder;
338  return this;
339  }
340 
341  public Builder with(RaptorInVehicleCostCalculator inVehicleCostCalculator) {
342  this.inVehicleCostCalculator = inVehicleCostCalculator;
343  return this;
344  }
345 
346  public Builder with(RaptorTransferCostCalculator transferCostCalculator) {
347  this.transferCostCalculator = transferCostCalculator;
348  return this;
349  }
350 
352  return new SwissRailRaptor(this.data, this.parametersForPerson, this.routeSelector, this.stopFinder, this.inVehicleCostCalculator, this.transferCostCalculator);
353  }
354  }
355 
356  public interface RaptorObserver {
357  void arrivedAtStop(double departureTime, TransitStopFacility stopFacility, double arrivalTime, int transferCount, Supplier<RaptorRoute> route);
358  }
359 
360 }
Builder with(RaptorParametersForPerson parametersForPerson)
List<? extends PlanElement > calcRoute(Facility fromFacility, Facility toFacility, double earliestDepartureTime, double desiredDepartureTime, double latestDepartureTime, Person person, Attributes routingAttributes)
List< InitialStop > findAccessStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters)
static double calcEuclideanDistance(Coord coord, Coord other)
Map< Id< TransitStopFacility >, SwissRailRaptorCore.TravelInfo > calcLeastCostTree(Collection< InitialStop > accessStops, double departureTime, RaptorParameters parameters, Person person, RaptorObserver observer)
List< InitialStop > findEgressStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters)
RaptorRoute createDirectWalk(Facility fromFacility, Facility toFacility, double departureTime, Person person, RaptorParameters parameters)
List< InitialStop > findStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters, SwissRailRaptorData data, Direction type)
RaptorRoute calcLeastCostRoute(double depTime, Facility fromFacility, Facility toFacility, List< InitialStop > accessStops, List< InitialStop > egressStops, RaptorParameters parameters, Person person)
List<? extends PlanElement > calcRoute(RoutingRequest request)
SwissRailRaptorConfigGroup.IntermodalLegOnlyHandling getIntermodalLegOnlyHandling()
List<? extends PlanElement > performRangeQuery(Facility fromFacility, Facility toFacility, double desiredDepartureTime, Person person, Attributes routingAttributes, RaptorParameters parameters)
RangeQuerySettingsParameterSet getRangeQuerySettings(String subpopulation)
List< RaptorRoute > calcRoutes(Facility fromFacility, Facility toFacility, double earliestDepartureTime, double desiredDepartureTime, double latestDepartureTime, Person person, Attributes routingAttributes)
final RaptorParametersForPerson parametersForPerson
Builder with(RaptorInVehicleCostCalculator inVehicleCostCalculator)
void calcTreesObservable(TransitStopFacility stopFacility, double earliestDepartureTime, double latestStartTime, RaptorParameters parameters, Person person, RaptorObserver observer)
Map< Id< TransitStopFacility >, SwissRailRaptorCore.TravelInfo > calcTree(Collection< TransitStopFacility > fromStops, double departureTime, RaptorParameters parameters, Person person)
static String getSubpopulation(HasPlansAndId<?, ?> person)
boolean hasNoPtLeg(List< RoutePart > parts)
Map< Id< TransitStopFacility >, SwissRailRaptorCore.TravelInfo > calcTree(TransitStopFacility fromStop, double departureTime, RaptorParameters parameters, Person person)
List< RaptorRoute > calcRoutes(double earliestDepTime, double desiredDepTime, double latestDepTime, Facility fromFacility, Facility toFacility, List< InitialStop > accessStops, List< InitialStop > egressStops, RaptorParameters parameters, Person person)
Map< Id< TransitStopFacility >, TravelInfo > calcLeastCostTree(double depTime, Collection< InitialStop > startStops, RaptorParameters parameters, Person person)
List<? extends PlanElement > calcRoute(Facility fromFacility, Facility toFacility, double earliestDepartureTime, double desiredDepartureTime, double latestDepartureTime, Person person, Attributes routingAttributes, RaptorRouteSelector selector)
Builder with(RaptorTransferCostCalculator transferCostCalculator)
static List<? extends PlanElement > convertRouteToLegs(RaptorRoute route, double transferWalkMargin)
Builder(SwissRailRaptorData data, Config config)
SwissRailRaptor(SwissRailRaptorData data, RaptorParametersForPerson parametersForPerson, RaptorRouteSelector routeSelector, RaptorStopFinder stopFinder, RaptorInVehicleCostCalculator inVehicleCostCalculator, RaptorTransferCostCalculator transferCostCalculator)
Map< Id< TransitStopFacility >, SwissRailRaptorCore.TravelInfo > calcTree(Facility fromFacility, double departureTime, Person person, Attributes routingAttributes)
RouteSelectorParameterSet getRouteSelector(String subpopulation)
RaptorRoute selectOne(List< RaptorRoute > routes, double desiredDepartureTime)
Builder with(RaptorRouteSelector routeSelector)