MATSIM
SwissRailRaptorCore.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 package ch.sbb.matsim.routing.pt.raptor;
21 
31 import org.matsim.api.core.v01.Id;
39 import org.matsim.vehicles.Vehicle;
40 
41 import java.util.*;
42 
50 public class SwissRailRaptorCore {
51  private final SwissRailRaptorData data;
52 
53  private final PathElement[] arrivalPathPerRouteStop;
54  private final double[] egressCostsPerRouteStop;
55  private final double[] leastArrivalCostAtRouteStop;
56  private final double[] leastArrivalCostAtStop;
57  private final BitSet improvedRouteStopIndices;
58  private final BitSet reachedRouteStopIndices;
59  private final BitSet improvedStops;
60  private final BitSet destinationRouteStopIndices;
61  private double bestArrivalCost = Double.POSITIVE_INFINITY;
62  private final PathElement[] arrivalPathPerStop;
63  private final PathElement[] tmpArrivalPathPerStop; // only used to ensure parallel update
64  private final BitSet tmpImprovedStops; // only used to ensure parallel update
65  private final boolean useCapacityConstraints;
66  private final boolean useAdaptiveTransferCalculation;
70 
71  private final static int TIME_UNDEFINED = Integer.MIN_VALUE;
72 
73  SwissRailRaptorCore(SwissRailRaptorData data, RaptorInVehicleCostCalculator inVehicleCostCalculator, RaptorTransferCostCalculator transferCostCalculator) {
74  this.data = data;
75  this.arrivalPathPerRouteStop = new PathElement[data.countRouteStops];
76  this.egressCostsPerRouteStop = new double[data.countRouteStops];
77  this.leastArrivalCostAtRouteStop = new double[data.countRouteStops];
78  this.leastArrivalCostAtStop = new double[data.countStops];
79  this.improvedRouteStopIndices = new BitSet(this.data.countRouteStops);
80  this.reachedRouteStopIndices = new BitSet(this.data.countRouteStops);
81  this.destinationRouteStopIndices = new BitSet(this.data.countRouteStops);
82  this.improvedStops = new BitSet(this.data.countStops);
83  this.arrivalPathPerStop = new PathElement[this.data.countStops];
84  this.tmpArrivalPathPerStop = new PathElement[this.data.countStops];
85  this.tmpImprovedStops = new BitSet(this.data.countStops);
86  this.useCapacityConstraints = this.data.config.isUseCapacityConstraints();
87  this.useAdaptiveTransferCalculation = this.data.config.getTransferCalculation().equals(RaptorTransferCalculation.Adaptive);
88  this.inVehicleCostCalculator = inVehicleCostCalculator;
89  this.transferCostCalculator = transferCostCalculator;
90  this.routeSegmentIterator = new RouteSegmentIteratorImpl(this.data);
91  }
92 
93  private void reset() {
94  Arrays.fill(this.arrivalPathPerRouteStop, null);
95  Arrays.fill(this.egressCostsPerRouteStop, Double.POSITIVE_INFINITY);
96  Arrays.fill(this.arrivalPathPerStop, null);
97  Arrays.fill(this.leastArrivalCostAtRouteStop, Double.POSITIVE_INFINITY);
98  Arrays.fill(this.leastArrivalCostAtStop, Double.POSITIVE_INFINITY);
99  this.improvedStops.clear();
100  this.improvedRouteStopIndices.clear();
101  this.reachedRouteStopIndices.clear();
102  this.destinationRouteStopIndices.clear();
103  this.bestArrivalCost = Double.POSITIVE_INFINITY;
104  }
105 
106  public RaptorRoute calcLeastCostRoute(double depTime, Facility fromFacility, Facility toFacility, List<InitialStop> accessStops, List<InitialStop> egressStops, RaptorParameters parameters, Person person) {
107  final int maxTransfers = 20; // sensible defaults, could be made configurable if there is a need for it.
108  final int maxTransfersAfterFirstArrival = 2;
109 
110  reset();
111  CachingTransferProvider transferProvider = this.data.new CachingTransferProvider();
112 
113  // Using a LinkedHashMap instead of a regular HashMap here is necessary to have a deterministic behaviour
114  Map<TransitStopFacility, InitialStop> destinationStops = new LinkedHashMap<>();
115 
116  // go through all egressStops; check if already in destinationStops; if so, check if current cost is smaller; if so, then replace. This can
117  // presumably happen when the same stop can be reached at lower cost by a different egress mode. (*)
118  for (InitialStop egressStop : egressStops) {
119  InitialStop alternative = destinationStops.get(egressStop.stop);
120  if (alternative == null || egressStop.accessCost < alternative.accessCost) {
121  destinationStops.put(egressStop.stop, egressStop);
122  }
123  }
124  // ??:
125  for (InitialStop egressStop : destinationStops.values()) {
126  int[] routeStopIndices = this.data.routeStopsPerStopFacility.get(egressStop.stop);
127  if (routeStopIndices != null) {
128  for (int routeStopIndex : routeStopIndices) {
129  this.destinationRouteStopIndices.set(routeStopIndex); // set bit at index position to true
130  this.egressCostsPerRouteStop[routeStopIndex] = egressStop.accessCost; // set egress costs from given stop
131  // presumably, the routeStops are the stops for the different routes that stop at the same stopFacility
132  }
133  }
134  }
135 
136  // same as (*) for access stops:
137  // Also, using a LinkedHashMap instead of a regular HashMap here is necessary to have a deterministic behaviour
138  Map<TransitStopFacility, InitialStop> initialStops = new LinkedHashMap<>();
139  for (InitialStop accessStop : accessStops) {
140  InitialStop alternative = initialStops.get(accessStop.stop);
141  if (alternative == null || accessStop.accessCost < alternative.accessCost) {
142  initialStops.put(accessStop.stop, accessStop);
143  }
144  }
145 
146  boolean hasIntermodalAccess = false;
147  // go through initial stops ...
148  for (InitialStop stop : initialStops.values()) {
149  // ... retrieve all route stops ...
150  int[] routeStopIndices = this.data.routeStopsPerStopFacility.get(stop.stop);
151  // ... go through them ...
152  for (int routeStopIndex : routeStopIndices) {
153  // ... set arrival time and arrival cost accordingly ...
154  int arrivalTime = (int) (depTime + stop.accessTime);
155  double arrivalCost = stop.accessCost;
156 
157  RRouteStop routeStop = this.data.routeStops[routeStopIndex];
158 
159  boolean isIntermodalAccess = stop.planElements != null;
160  // (intermodal access is if there are planElements that describe the intermodal access, which depends, I think, on which constructor was
161  // called (since also non-intermodal access has a leg). kai, jul'19
162 
163  if (!isIntermodalAccess && routeStop.routeStop == routeStop.route.getStops().get(routeStop.route.getStops().size() - 1)) {
164  // this is the last stop of a route, doesn't make sense to start here
165  // if it's intermodal, we still start here, as we might transfer to another close-by but non-intermodal stop.
166  continue;
167  }
168  if (!routeStop.routeStop.isAllowBoarding()) {
169  continue;
170  }
171 
172  RRoute route = this.data.routes[routeStop.transitRouteIndex];
173  int depOffset = routeStop.departureOffset;
174 
175  int departureIndex = findNextDepartureIndex(route, routeStop, arrivalTime);
176  if (departureIndex >= 0) {
177  int nextDepartureTimeAtStop = this.data.departures[departureIndex] + depOffset;
178  int waitingTime = nextDepartureTimeAtStop - arrivalTime;
179  double waitingCost = waitingTime * -parameters.getMarginalUtilityOfWaitingPt_utl_s();
180 
181  RRouteStop toRouteStop = this.data.routeStops[routeStopIndex];
182  PathElement pe = new PathElement(null, toRouteStop, TIME_UNDEFINED, nextDepartureTimeAtStop, nextDepartureTimeAtStop, arrivalTime, arrivalCost, 0, stop.distance, 0, true, null, stop);
183 
184  /* okay, the following is not very nice...
185  * we want to find the least-cost access leg including the waiting time
186  * until the next departure. But that waiting time should not be included anywhere else,
187  * just to figure out which is the best access way.
188  * So we calculate that total cost, including the waiting time, and use that
189  * to compare the different access legs. We write these total costs to
190  * this.leastArrivalCostAtRouteStop[], although it should only contain the arrival
191  * cost. This works because updates to this value are calculated based on the
192  * time and cost in arrivalPathPerRouteStop, so the additional waitingCost at that
193  * first stop gets ignored/overwritten in the further run of the routing algorithm.
194  */
195  double xCost = arrivalCost + waitingCost;
196 
197  if (xCost < this.leastArrivalCostAtRouteStop[routeStopIndex]) {
198  this.arrivalPathPerRouteStop[routeStopIndex] = pe;
199  this.leastArrivalCostAtRouteStop[routeStopIndex] = xCost;
200  this.improvedRouteStopIndices.set(routeStopIndex);
201  if (xCost < this.leastArrivalCostAtStop[toRouteStop.stopFacilityIndex]) {
202  this.improvedStops.set(toRouteStop.stopFacilityIndex);
203  this.arrivalPathPerStop[toRouteStop.stopFacilityIndex] = pe;
204  this.leastArrivalCostAtStop[toRouteStop.stopFacilityIndex] = xCost;
205  }
206  }
207  } else if (isIntermodalAccess) {
208  // there is no more departure, but we start here by intermodal access, so still register to allow transfers to other (non-)intermodal stops.
209  RRouteStop toRouteStop = this.data.routeStops[routeStopIndex];
210  PathElement pe = new PathElement(null, toRouteStop, TIME_UNDEFINED, TIME_UNDEFINED, TIME_UNDEFINED,arrivalTime, arrivalCost, 0, stop.distance, 0, true, null, stop);
211 
212  /* okay, the following is not very nice...
213  * ... see long comment above, it's the same
214  */
215  if (arrivalCost < this.leastArrivalCostAtRouteStop[routeStopIndex]) {
216  hasIntermodalAccess = true;
217  this.arrivalPathPerRouteStop[routeStopIndex] = pe;
218  this.leastArrivalCostAtRouteStop[routeStopIndex] = arrivalCost;
219  this.improvedRouteStopIndices.set(routeStopIndex);
220  if (arrivalCost < this.leastArrivalCostAtStop[toRouteStop.stopFacilityIndex]) {
221  this.improvedStops.set(toRouteStop.stopFacilityIndex);
222  this.arrivalPathPerStop[toRouteStop.stopFacilityIndex] = pe;
223  this.leastArrivalCostAtStop[toRouteStop.stopFacilityIndex] = arrivalCost;
224  }
225  }
226  }
227  }
228  }
229 
230  if (hasIntermodalAccess) {
231  // allow transfering from the initial stop to another one if we have intermodal access,
232  // as not all stops might be intermodal
233 
234  // handleTransfers clears improvedRouteStopIndices, which is correct during rounds
235  // but it loses the initial route stop indices directly after initialization.
236  // so keep a copy and restore it
237  BitSet initialRouteStopIndices = new BitSet();
238  initialRouteStopIndices.or(this.improvedRouteStopIndices);
239 
240  handleTransfers(true, parameters, transferProvider);
241  this.improvedRouteStopIndices.or(initialRouteStopIndices);
242  }
243 
244  int allowedTransfersLeft = maxTransfersAfterFirstArrival;
245  // the main loop
246  for (int k = 0; k <= maxTransfers; k++) {
247  // first stage (according to paper) is to set earliestArrivalTime_k(stop) = earliestArrivalTime_k-1(stop)
248  // but because we re-use the earliestArrivalTime-array, we don't have to do anything.
249 
250  // second stage: process routes
251  exploreRoutes(parameters, person, transferProvider);
252 
253  PathElement leastCostPath = findLeastCostArrival(destinationStops);
254 
255  if (leastCostPath != null) {
256  if (allowedTransfersLeft == 0) {
257  break;
258  }
259  allowedTransfersLeft--;
260  }
261 
262  if (this.improvedStops.isEmpty()) {
263  break;
264  }
265 
266  // third stage (according to paper): handle footpaths / transfers
267  handleTransfers(true, parameters, transferProvider);
268 
269  // final stage: check stop criterion
270  if (this.improvedRouteStopIndices.isEmpty()) {
271  break;
272  }
273  }
274 
275  // create RaptorRoute based on PathElements
276  PathElement leastCostPath = findLeastCostArrival(destinationStops);
277  RaptorRoute raptorRoute = createRaptorRoute(fromFacility, toFacility, leastCostPath, depTime);
278  return raptorRoute;
279  }
280 
281  public List<RaptorRoute> calcRoutes(double earliestDepTime, double desiredDepTime, double latestDepTime, Facility fromFacility, Facility toFacility, List<InitialStop> accessStops, List<InitialStop> egressStops, RaptorParameters parameters, Person person) {
282  List<RaptorRoute> foundRoutes = new ArrayList<>();
283  int maxTransfers = 20; // sensible defaults, could be made configurable if there is a need for it.
284  final int maxTransfersAfterFirstArrival = 2;
285  Map<PathElement, InitialStop> initialStopsPerStartPath = new HashMap<>();
286 
287  reset();
288 
289  CachingTransferProvider transferProvider = this.data.new CachingTransferProvider();
290  double marginalUtilityOfWaitingPt_utl_s = parameters.getMarginalUtilityOfWaitingPt_utl_s();
291 
292  PathElement lastFoundBestPath = null;
293 
294  /* the original algorithm works with time. Starting with the latest departure,
295  * it's easy to go backwards in time and potentially improve already visited stops when
296  * arriving there earlier. In our case, we operate with cost. The cost of two departures
297  * along the same route at different times is the same, breaking the algorithm.
298  * In order to fix it, we have to make the cost behave the same way as time does in
299  * the original algorithm. Thus, for each handled departure, we add an additional cost,
300  * named "costOffset", corresponding to the additional waiting time for this departure
301  * compared to the earliest departure time. This way, cost should behave very similar to
302  * time: an earlier departure will not lead to smaller costs if the final arrival is at
303  * the same time when the additional time is just spent waiting somewhere at a transfer.
304  * The same connection at an earlier time, resulting in an earlier arrival, will indeed
305  * be found as an improved solution, although when the costOffset is subtracted again, it will
306  * have the same cost. This allows us to filter and score the different routes afterwards.
307  */
308 
309  List<DepartureAtRouteStop> departures = new ArrayList<>();
310  for (InitialStop accessStop : accessStops) {
311  double earliestTimeAtStop = earliestDepTime + accessStop.accessTime;
312  double latestTimeAtStop = latestDepTime + accessStop.accessTime;
313  TransitStopFacility stop = accessStop.stop;
314  int[] routeStopIndices = this.data.routeStopsPerStopFacility.get(stop);
315  if (routeStopIndices != null) {
316  for (int routeStopIndex : routeStopIndices) {
317  RRouteStop routeStop = this.data.routeStops[routeStopIndex];
318  if (routeStop.routeStop == routeStop.route.getStops().get(routeStop.route.getStops().size() - 1)) {
319  // this is the last stop of a route
320  continue;
321  }
322  if (!routeStop.routeStop.isAllowBoarding()) {
323  continue;
324  }
325  RRoute route = this.data.routes[routeStop.transitRouteIndex];
326  int depOffset = routeStop.departureOffset;
327  for (int depIndex = route.indexFirstDeparture; depIndex < route.indexFirstDeparture + route.countDepartures; depIndex++) {
328  int depTimeAtStart = this.data.departures[depIndex];
329  int depTimeAtStop = depTimeAtStart + depOffset;
330  if (depTimeAtStop >= earliestTimeAtStop && depTimeAtStop <= latestTimeAtStop) {
331  double costOffset = (depTimeAtStop - earliestTimeAtStop) * marginalUtilityOfWaitingPt_utl_s;
332  departures.add(new DepartureAtRouteStop(routeStop, routeStopIndex, depIndex, depTimeAtStop, costOffset, accessStop));
333  }
334  }
335  }
336  }
337  }
338  departures.sort((d1, d2) -> {
339  // sort the departures by cost, not by time as in the original algorithm
340  double c1 = d1.costOffset + d1.accessStop.accessCost;
341  double c2 = d2.costOffset + d2.accessStop.accessCost;
342  int cmp = Double.compare(c1, c2);
343  if (cmp == 0) {
344  cmp = Integer.compare(d1.departureIndex, d2.departureIndex);
345  }
346  return -cmp; // negate, we want to order from biggest to smallest
347  });
348 
349  Map<TransitStopFacility, InitialStop> destinationStops = new HashMap<>();
350  for (InitialStop egressStop : egressStops) {
351  InitialStop alternative = destinationStops.get(egressStop.stop);
352  if (alternative == null || egressStop.accessCost < alternative.accessCost) {
353  destinationStops.put(egressStop.stop, egressStop);
354  }
355  }
356  for (InitialStop egressStop : destinationStops.values()) {
357  int[] routeStopIndices = this.data.routeStopsPerStopFacility.get(egressStop.stop);
358  if (routeStopIndices != null) {
359  for (int routeStopIndex : routeStopIndices) {
360  this.destinationRouteStopIndices.set(routeStopIndex);
361  this.egressCostsPerRouteStop[routeStopIndex] = egressStop.accessCost;
362  }
363  }
364  }
365 
366  for (DepartureAtRouteStop depAtRouteStop : departures) {
367  this.improvedStops.clear();
368  this.improvedRouteStopIndices.clear();
369  this.bestArrivalCost = Double.POSITIVE_INFINITY;
370  { // initialization for this departure Time
371  int arrivalTime = depAtRouteStop.depTime;
372  double arrivalCost = depAtRouteStop.accessStop.accessCost + depAtRouteStop.costOffset;
373  RRouteStop toRouteStop = depAtRouteStop.routeStop;
374  int routeStopIndex = depAtRouteStop.routeStopIndex;
375  PathElement pe = new PathElement(null, toRouteStop, depAtRouteStop.depTime, depAtRouteStop.depTime, depAtRouteStop.depTime, arrivalTime, arrivalCost, 0, depAtRouteStop.accessStop.distance, 0, true, null, depAtRouteStop.accessStop);
376  this.arrivalPathPerRouteStop[routeStopIndex] = pe;
377  this.leastArrivalCostAtRouteStop[routeStopIndex] = arrivalCost;
378  this.arrivalPathPerStop[toRouteStop.stopFacilityIndex] = pe;
379  this.leastArrivalCostAtStop[toRouteStop.stopFacilityIndex] = arrivalCost;
380  this.improvedRouteStopIndices.set(routeStopIndex);
381  initialStopsPerStartPath.put(pe, depAtRouteStop.accessStop);
382  }
383 
384  // the main loop
385  for (int k = 0; k <= maxTransfers; k++) {
386  // first stage (according to paper) is to set earliestArrivalTime_k(stop) = earliestArrivalTime_k-1(stop)
387  // but because we re-use the earliestArrivalTime-array, we don't have to do anything.
388 
389  // second stage: process routes
390  exploreRoutes(parameters, person, transferProvider);
391 
392  PathElement leastCostPath = findLeastCostArrival(destinationStops);
393  if (leastCostPath != null && (lastFoundBestPath == null || leastCostPath.comingFrom != lastFoundBestPath.comingFrom)) {
394  lastFoundBestPath = leastCostPath;
395 
396  double depTime = calculateOptimalDepartureTime(leastCostPath, initialStopsPerStartPath);
397  leastCostPath.arrivalTravelCost -= depAtRouteStop.costOffset;
398  RaptorRoute raptorRoute = createRaptorRoute(fromFacility, toFacility, leastCostPath, depTime);
399  leastCostPath.arrivalTravelCost += depAtRouteStop.costOffset;
400  foundRoutes.add(raptorRoute);
401 
402  int optimizedTransferLimit = leastCostPath.transferCount + maxTransfersAfterFirstArrival;
403  if (optimizedTransferLimit < maxTransfers) {
404  maxTransfers = optimizedTransferLimit;
405  }
406  if (k == maxTransfers) {
407  break; // no use to handle transfers
408  }
409  }
410 
411  if (this.improvedStops.isEmpty()) {
412  break;
413  }
414 
415  // third stage (according to paper): handle footpaths / transfers
416  handleTransfers(false, parameters, transferProvider);
417 
418  // final stage: check stop criterion
419  if (this.improvedRouteStopIndices.isEmpty()) {
420  break;
421  }
422  }
423  }
424 
425  List<RaptorRoute> routes = filterRoutes(foundRoutes);
426  return routes;
427  }
428 
429  private double calculateOptimalDepartureTime(PathElement leastCostPath, Map<PathElement, InitialStop> initialStopsPerStartPath) {
430  PathElement firstPE = leastCostPath;
431  while (firstPE.comingFrom != null) {
432  firstPE = firstPE.comingFrom;
433  }
434  double depTime = firstPE.arrivalTime;
435  // currently, firstPE.arrivalTime is exactly the time of departure at that stop
436  // let's add some time for safety reasons and to add some realism
437  depTime -= this.data.config.getMinimalTransferTime();
438  // for more realism, a (random) value from a distribution could be taken instead of a fixed value
439  InitialStop accessStop = initialStopsPerStartPath.get(firstPE);
440  depTime -= accessStop.accessTime; // take access time into account
441  return Math.floor(depTime);
442  }
443 
444  private List<RaptorRoute> filterRoutes(List<RaptorRoute> allRoutes) {
445  // first, eliminate duplicates
446  allRoutes.sort((r1, r2) -> {
447  int cmp = Integer.compare(r1.getNumberOfTransfers(), r2.getNumberOfTransfers());
448  if (cmp == 0) {
449  cmp = Double.compare(r1.getDepartureTime(), r2.getDepartureTime());
450  }
451  if (cmp == 0) {
452  cmp = Double.compare(r1.getTravelTime(), r2.getTravelTime());
453  }
454  return cmp;
455  });
456  List<RaptorRoute> uniqueRoutes = new ArrayList<>();
457  int lastTransferCount = -1;
458  double lastDepTime = Double.NaN;
459  double lastTravelTime = Double.NaN;
460  for (RaptorRoute route : allRoutes) {
461  if (route.getNumberOfTransfers() != lastTransferCount
462  || route.getDepartureTime() != lastDepTime
463  || route.getTravelTime() != lastTravelTime) {
464  uniqueRoutes.add(route);
465  lastTransferCount = route.getNumberOfTransfers();
466  lastDepTime = route.getDepartureTime();
467  lastTravelTime = route.getTravelTime();
468  }
469  }
470 
471  // now search for non-dominant routes
472  List<RaptorRoute> routesToKeep = new ArrayList<>();
473  for (RaptorRoute route1 : uniqueRoutes) {
474  boolean addRoute1 = true;
475  for (RaptorRoute route2 : uniqueRoutes) {
476  if (route1 != route2) {
477  // check if route2 dominates route1
478  double arrTime1 = route1.getDepartureTime() + route1.getTravelTime();
479  double arrTime2 = route2.getDepartureTime() + route2.getTravelTime();
480  if (route2.getNumberOfTransfers() <=route1.getNumberOfTransfers()
481  && route2.getDepartureTime() >= route1.getDepartureTime()
482  && arrTime2 <= arrTime1) {
483  addRoute1 = false;
484  break;
485  }
486  }
487  }
488  if (addRoute1) {
489  routesToKeep.add(route1);
490  }
491  }
492  return routesToKeep;
493  }
494 
495  public Map<Id<TransitStopFacility>, TravelInfo> calcLeastCostTree(double depTime, Collection<InitialStop> startStops, RaptorParameters parameters, Person person) {
496  return this.calcLeastCostTree(depTime, startStops, parameters, person, null);
497  }
498 
499  public Map<Id<TransitStopFacility>, TravelInfo> calcLeastCostTree(double depTime, Collection<InitialStop> startStops, RaptorParameters parameters, Person person, RaptorObserver observer) {
500  reset();
501 
502  CachingTransferProvider transferProvider = this.data.new CachingTransferProvider();
503  BitSet initialRouteStopIndices = new BitSet();
504  BitSet initialStopIndices = new BitSet();
505  for (InitialStop stop : startStops) {
506  int[] routeStopIndices = this.data.routeStopsPerStopFacility.get(stop.stop);
507  for (int routeStopIndex : routeStopIndices) {
508  boolean useStop = true;
509  RRouteStop routeStop = this.data.routeStops[routeStopIndex];
510  if (!routeStop.routeStop.isAllowBoarding()) {
511  useStop = false;
512  }
513  if (useStop && parameters.isExactDeparturesOnly()) {
514  int routeIndex = routeStop.transitRouteIndex;
515  RRoute route = this.data.routes[routeIndex];
516  int currentDepartureIndex = findNextDepartureIndex(route, routeStop, (int) depTime);
517  if (currentDepartureIndex >= 0) {
518  int firstDepartureTime = this.data.departures[currentDepartureIndex];
519  int stopDepartureTime = firstDepartureTime + routeStop.departureOffset;
520  useStop = Math.abs(depTime - stopDepartureTime) < 1e-5;
521  } else {
522  useStop = false;
523  }
524  }
525  if (useStop) {
526  int arrivalTime = (int) (depTime + stop.accessTime);
527  double arrivalCost = stop.accessCost;
528  RRouteStop toRouteStop = this.data.routeStops[routeStopIndex];
529  PathElement pe = new PathElement(null, toRouteStop, TIME_UNDEFINED, TIME_UNDEFINED, TIME_UNDEFINED, arrivalTime, arrivalCost, 0, stop.distance, 0, true, null, stop);
530  this.arrivalPathPerRouteStop[routeStopIndex] = pe;
531  this.arrivalPathPerStop[toRouteStop.stopFacilityIndex] = pe;
532  this.leastArrivalCostAtRouteStop[routeStopIndex] = arrivalCost;
533  this.leastArrivalCostAtStop[toRouteStop.stopFacilityIndex] = arrivalCost;
534  this.improvedRouteStopIndices.set(routeStopIndex);
535  // this is special: make sure we can transfer even at the start stop
536  initialRouteStopIndices.set(routeStopIndex);
537  initialStopIndices.set(toRouteStop.stopFacilityIndex);
538  }
539  }
540  }
541 
542  // the main loop
543  int maxTransfers = parameters.getMaxTransfers();
544  int transfers = 0;
545  while (true) {
546  // first stage (according to paper) is to set earliestArrivalTime_k(stop) = earliestArrivalTime_k-1(stop)
547  // but because we re-use the earliestArrivalTime-array, we don't have to do anything.
548 
549  // second stage: process routes
550  exploreRoutes(parameters, person, transferProvider);
551 
552  if (this.improvedStops.isEmpty()) {
553  break;
554  }
555 
556  if (initialRouteStopIndices != null) {
557  this.improvedRouteStopIndices.or(initialRouteStopIndices);
558  this.improvedStops.or(initialStopIndices);
559  initialRouteStopIndices = null;
560  initialStopIndices = null;
561  }
562 
563  if (transfers > maxTransfers) {
564  break;
565  }
566 
567  if (observer != null) {
568  for (int stopIndex = this.improvedStops.nextSetBit(0); stopIndex >= 0; stopIndex = this.improvedStops.nextSetBit(stopIndex + 1)) {
569  PathElement fromPE = this.arrivalPathPerStop[stopIndex];
570  observeArrival(fromPE, observer);
571  }
572  }
573 
574  // third stage (according to paper): handle footpaths / transfers
575  handleTransfers(true, parameters, transferProvider);
576  transfers++;
577 
578  // final stage: check stop criterion
579  if (this.improvedRouteStopIndices.isEmpty()) {
580  break;
581  }
582 
583  if (observer != null) {
584  for (int stopIndex = this.tmpImprovedStops.nextSetBit(0); stopIndex >= 0; stopIndex = this.tmpImprovedStops.nextSetBit(stopIndex + 1)) {
585  PathElement fromPE = this.arrivalPathPerStop[stopIndex];
586  observeArrival(fromPE, observer);
587  }
588  }
589 
590  }
591 
592  // collect information for each stop
593  Map<Id<TransitStopFacility>, TravelInfo> result = new HashMap<>();
594  for (Map.Entry<TransitStopFacility, Integer> e : this.data.stopFacilityIndices.entrySet()) {
595  TransitStopFacility stop = e.getKey();
596  int index = e.getValue();
597  PathElement destination = this.arrivalPathPerStop[index];
598  if (destination != null) {
599  TravelInfo ti = getTravelInfo(destination, parameters);
600  result.put(stop.getId(), ti);
601  }
602  }
603  return result;
604  }
605 
606  private void observeArrival(PathElement pe, RaptorObserver observer) {
607  PathElement backpointer = pe.comingFrom;
608  if (backpointer != null) {
609  while (backpointer.comingFrom != null) {
610  backpointer = backpointer.comingFrom;
611  }
612  TransitStopFacility departureStopFacility = backpointer.toRouteStop.routeStop.getStopFacility();
613  if (departureStopFacility != null) {
614  TransitStopFacility arrivalStopFacility = pe.toRouteStop.routeStop.getStopFacility();
615  observer.arrivedAtStop(pe.firstDepartureTime, arrivalStopFacility, pe.arrivalTime, pe.transferCount, () -> createRaptorRoute(departureStopFacility, arrivalStopFacility, pe, pe.firstDepartureTime));
616  }
617  }
618  }
619 
620  private TravelInfo getTravelInfo(PathElement destination, RaptorParameters parameters) {
621  PathElement firstStage = destination;
622  PathElement secondStage = null;
623  while (firstStage.comingFrom != null) {
624  secondStage = firstStage;
625  firstStage = firstStage.comingFrom;
626  }
627  int arrivalTimeAtLastStop = destination.arrivalTime;
628  int departureTimeAtFirstStop = destination.firstDepartureTime;
629  if (departureTimeAtFirstStop == TIME_UNDEFINED) {
630  // a trip with no actual pt-leg, likely the start-location
631  departureTimeAtFirstStop = arrivalTimeAtLastStop;
632  }
633  double accessTime = firstStage.initialStop.accessTime;
634  double accessCost = firstStage.initialStop.accessCost;
635 
636  double waitingTime = departureTimeAtFirstStop - firstStage.arrivalTime;
637  double waitingCost = waitingTime * -parameters.getMarginalUtilityOfWaitingPt_utl_s();
638 
639  double travelCost = destination.arrivalTravelCost - firstStage.arrivalTravelCost - waitingCost;
640  int transferCount = destination.transferCount;
641  if (destination.isTransfer && transferCount > 0) {
642  transferCount--; // do not count this as transfer, as the router would merge it with the egress walk
643  }
644  if (secondStage != null && secondStage.isTransfer && transferCount > 0) {
645  transferCount--; // the first "leg" is a transfer, do not count it as such as the router would merge it with the access walk
646  }
647  Id<TransitStopFacility> departureStopId = firstStage.toRouteStop.routeStop.getStopFacility().getId();
648  return new TravelInfo(departureStopId, departureTimeAtFirstStop, arrivalTimeAtLastStop, travelCost, accessTime, accessCost, transferCount, waitingTime, waitingCost, destination);
649  }
650 
651  private void exploreRoutes(RaptorParameters parameters, Person person, CachingTransferProvider transferProvider) {
652  this.improvedStops.clear();
653  this.reachedRouteStopIndices.clear();
654 
655  double marginalUtilityOfWaitingPt_utl_s = parameters.getMarginalUtilityOfWaitingPt_utl_s();
656  boolean useTransportModeUtilities = parameters.isUseTransportModeUtilities();
657 
658  int routeIndex = -1;
659  for (int firstRouteStopIndex = this.improvedRouteStopIndices.nextSetBit(0); firstRouteStopIndex >= 0; firstRouteStopIndex = this.improvedRouteStopIndices.nextSetBit(firstRouteStopIndex+1)) {
660  RRouteStop firstRouteStop = this.data.routeStops[firstRouteStopIndex];
661  if (firstRouteStop.transitRouteIndex == routeIndex) {
662  continue; // we've handled this route already
663  }
664  int tmpRouteIndex = firstRouteStop.transitRouteIndex;
665 
666  // for each relevant route, step along route and look for new/improved connections
667  RRoute route = this.data.routes[tmpRouteIndex];
668 
669  // firstRouteStop is the first RouteStop in the route we can board in this round
670  // figure out which departure we can take
671  PathElement boardingPE = this.arrivalPathPerRouteStop[firstRouteStopIndex];
672  int agentFirstArrivalTime = boardingPE.arrivalTime;
673  int currentBoardingRouteStopIndex = firstRouteStopIndex;
674  int currentDepartureIndex = findNextDepartureIndex(route, firstRouteStop, agentFirstArrivalTime);
675  if (currentDepartureIndex >= 0) {
676  Vehicle currentVehicle = this.data.departureVehicles[currentDepartureIndex];
677  int currentDepartureTime = this.data.departures[currentDepartureIndex];
678  int currentAgentBoardingTime;
679  double currentTravelCostWhenBoarding;
680  double currentTransferCostWhenBoarding;
681  {
682  int vehicleArrivalTime = currentDepartureTime + firstRouteStop.arrivalOffset;
683  currentAgentBoardingTime = Math.max(agentFirstArrivalTime, vehicleArrivalTime);
684  int waitingTime = currentAgentBoardingTime - agentFirstArrivalTime;
685  double waitingCost = -marginalUtilityOfWaitingPt_utl_s * waitingTime;
686  currentTravelCostWhenBoarding = boardingPE.arrivalTravelCost + waitingCost;
687  currentTransferCostWhenBoarding = boardingPE.arrivalTransferCost;
688  }
689 
690  if ((currentTravelCostWhenBoarding + currentTransferCostWhenBoarding) > this.bestArrivalCost) {
691  continue;
692  }
693  routeIndex = tmpRouteIndex;
694  int firstDepartureTime = (boardingPE.firstDepartureTime == TIME_UNDEFINED) ? currentAgentBoardingTime : boardingPE.firstDepartureTime;
695 
696  double marginalUtilityOfTravelTime_utl_s = parameters.getMarginalUtilityOfTravelTime_utl_s(
697  !useTransportModeUtilities ? boardingPE.toRouteStop.mode : boardingPE.toRouteStop.route.getTransportMode());
698  transferProvider.reset(boardingPE.transfer);
699 
700  for (int toRouteStopIndex = firstRouteStopIndex + 1; toRouteStopIndex < route.indexFirstRouteStop + route.countRouteStops; toRouteStopIndex++) {
701  RRouteStop toRouteStop = this.data.routeStops[toRouteStopIndex];
702  if (!toRouteStop.routeStop.isAllowAlighting()) {
703  continue;
704  }
705  this.routeSegmentIterator.reset(currentDepartureIndex, currentAgentBoardingTime, currentBoardingRouteStopIndex, toRouteStopIndex);
706  int arrivalTime = currentDepartureTime + toRouteStop.arrivalOffset;
707  int inVehicleTime = arrivalTime - currentAgentBoardingTime;
708  double inVehicleCost = this.inVehicleCostCalculator.getInVehicleCost(inVehicleTime, marginalUtilityOfTravelTime_utl_s, person, currentVehicle, parameters, routeSegmentIterator);
709  double arrivalTravelCost = currentTravelCostWhenBoarding + inVehicleCost;
710  double arrivalTransferCost = (boardingPE.firstDepartureTime != TIME_UNDEFINED) ? (currentTransferCostWhenBoarding + this.transferCostCalculator.calcTransferCost(boardingPE,transferProvider, data.config, parameters, arrivalTime - firstDepartureTime, boardingPE.transferCount, boardingPE.arrivalTransferCost, boardingPE.arrivalTime)) : 0;
711  double previousArrivalCost = this.leastArrivalCostAtRouteStop[toRouteStopIndex];
712  double totalArrivalCost = arrivalTravelCost + arrivalTransferCost;
713  if (totalArrivalCost <= previousArrivalCost) {
714  double distance = toRouteStop.distanceAlongRoute - boardingPE.toRouteStop.distanceAlongRoute;
715  PathElement pe = new PathElement(boardingPE, toRouteStop, firstDepartureTime, currentAgentBoardingTime, currentDepartureTime + firstRouteStop.departureOffset, arrivalTime, arrivalTravelCost, arrivalTransferCost, distance, boardingPE.transferCount, false, null, null);
716  this.arrivalPathPerRouteStop[toRouteStopIndex] = pe;
717  this.leastArrivalCostAtRouteStop[toRouteStopIndex] = totalArrivalCost;
718  if (totalArrivalCost <= this.leastArrivalCostAtStop[toRouteStop.stopFacilityIndex]) {
719  this.leastArrivalCostAtStop[toRouteStop.stopFacilityIndex] = totalArrivalCost;
720  this.arrivalPathPerStop[toRouteStop.stopFacilityIndex] = pe;
721  this.improvedStops.set(toRouteStop.stopFacilityIndex);
722  checkForBestArrival(toRouteStopIndex, totalArrivalCost);
723  }
724  } else /*if (previousArrivalCost < arrivalCost)*/ {
725  // looks like we could reach this stop with better cost from somewhere else
726  // check if we can depart also with better cost, if yes, switch to this connection
727  PathElement alternativeBoardingPE = this.arrivalPathPerRouteStop[toRouteStopIndex];
728  int alternativeAgentFirstArrivalTime = alternativeBoardingPE.arrivalTime;
729  int alternativeDepartureIndex = findNextDepartureIndex(route, toRouteStop, alternativeAgentFirstArrivalTime);
730  if (alternativeDepartureIndex >= 0) {
731  int alternativeDepartureTime = this.data.departures[alternativeDepartureIndex];
732  int alternativeVehicleArrivalTime = alternativeDepartureTime + toRouteStop.arrivalOffset;
733  int alternativeAgentBoardingTime = Math.max(alternativeAgentFirstArrivalTime, alternativeVehicleArrivalTime);
734  int alternativeWaitingTime = alternativeAgentBoardingTime - alternativeAgentFirstArrivalTime;
735  double alternativeWaitingCost = -marginalUtilityOfWaitingPt_utl_s * alternativeWaitingTime;
736  double alternativeTravelCostWhenBoarding = alternativeBoardingPE.arrivalTravelCost + alternativeWaitingCost;
737  double alternativeTotalCostWhenBoarding = alternativeTravelCostWhenBoarding + alternativeBoardingPE.arrivalTransferCost;
738  if (alternativeTotalCostWhenBoarding < totalArrivalCost) {
739  currentDepartureIndex = alternativeDepartureIndex;
740  currentVehicle = this.data.departureVehicles[currentDepartureIndex];
741  currentDepartureTime = alternativeDepartureTime;
742  if (alternativeBoardingPE.isTransfer) {
743  firstRouteStop = this.data.routeStops[toRouteStopIndex];
744  } else {
745  // we improve to a line/route we entered at some earlier stop, do not create a new transfer for this,
746  // but set the boarding info back to the original boarding of this route
747  alternativeBoardingPE = alternativeBoardingPE.comingFrom;
748  alternativeAgentFirstArrivalTime = alternativeBoardingPE.arrivalTime;
749  alternativeVehicleArrivalTime = alternativeDepartureTime + alternativeBoardingPE.toRouteStop.arrivalOffset;
750  alternativeAgentBoardingTime = Math.max(alternativeAgentFirstArrivalTime, alternativeVehicleArrivalTime);
751 
752  alternativeWaitingTime = alternativeAgentBoardingTime - alternativeAgentFirstArrivalTime;
753  alternativeWaitingCost = -marginalUtilityOfWaitingPt_utl_s * alternativeWaitingTime;
754  alternativeTravelCostWhenBoarding = alternativeBoardingPE.arrivalTravelCost + alternativeWaitingCost;
755 
756  firstRouteStop = alternativeBoardingPE.toRouteStop;
757  }
758  currentAgentBoardingTime = alternativeAgentBoardingTime;
759  currentTravelCostWhenBoarding = alternativeTravelCostWhenBoarding;
760  currentTransferCostWhenBoarding = alternativeBoardingPE.arrivalTransferCost;
761  boardingPE = alternativeBoardingPE;
762  firstDepartureTime = (boardingPE.firstDepartureTime == TIME_UNDEFINED) ? currentAgentBoardingTime : boardingPE.firstDepartureTime;
763  currentBoardingRouteStopIndex = alternativeBoardingPE.toRouteStop.index;
764  }
765  }
766  }
767  firstRouteStopIndex = toRouteStopIndex; // we've handled this route stop, so we can skip it in the outer loop
768  }
769  }
770  }
771  }
772 
773  private void checkForBestArrival(int routeStopIndex, double arrivalCost) {
774  if (this.destinationRouteStopIndices.get(routeStopIndex)) {
775  // this is a destination stop
776  double totalCost = arrivalCost + this.egressCostsPerRouteStop[routeStopIndex];
777  if (totalCost < this.bestArrivalCost) {
778  this.bestArrivalCost = totalCost;
779  }
780  }
781  }
782 
783  private int findNextDepartureIndex(RRoute route, RRouteStop routeStop, int time) {
784  if (this.useCapacityConstraints) {
785  return findNextDepartureIndexWithConstraints(route, routeStop, time);
786  }
787  int depTimeAtRouteStart = time - routeStop.departureOffset;
788  int fromIndex = route.indexFirstDeparture;
789  int toIndex = fromIndex + route.countDepartures;
790  int pos = Arrays.binarySearch(this.data.departures, fromIndex, toIndex, depTimeAtRouteStart);
791  if (pos < 0) {
792  // binarySearch returns (-(insertion point) - 1) if the element was not found, which will happen most of the times.
793  // insertion_point points to the next larger element, which is the next departure in our case
794  // This can be transformed as follows:
795  // retval = -(insertion point) - 1
796  // ==> insertion point = -(retval+1) .
797  pos = -(pos + 1);
798  }
799  if (pos >= toIndex) {
800  // there is no later departure time
801  return -1;
802  }
803  return pos;
804  }
805 
806  private int findNextDepartureIndexWithConstraints(RRoute route, RRouteStop routeStop, int time) {
807  return this.data.occupancyData.getNextAvailableDeparture(this.data, routeStop, time);
808  }
809 
810  private void handleTransfers(boolean strict, RaptorParameters raptorParams, CachingTransferProvider transferProvider) {
811  this.improvedRouteStopIndices.clear();
812  this.tmpImprovedStops.clear();
813 
814  double margUtilityTransitWalk = raptorParams.getMarginalUtilityOfTravelTime_utl_s(TransportMode.walk); // replaced TransportMode.transit_walk with walk
815 
816  for (int stopIndex = this.improvedStops.nextSetBit(0); stopIndex >= 0; stopIndex = this.improvedStops.nextSetBit(stopIndex + 1)) {
817  PathElement fromPE = this.arrivalPathPerStop[stopIndex];
818  int arrivalTime = fromPE.arrivalTime;
819  double arrivalTravelCost = fromPE.arrivalTravelCost;
820  double arrivalTransferCost = fromPE.arrivalTransferCost;
821  double totalArrivalCost = arrivalTravelCost + arrivalTransferCost;
822  if (totalArrivalCost > this.bestArrivalCost) {
823  continue;
824  }
825  RRouteStop fromRouteStop = fromPE.toRouteStop; // this is the route stop we arrive with least cost at stop
826 
827  final int firstTransferIndex;
828  final int lastTransferIndex;
829  final RTransfer[] transfers;
830 
831  if (!useAdaptiveTransferCalculation) {
832  // efficient lookup from the precomputed transfer candidates
833  transfers = this.data.transfers;
834  firstTransferIndex = fromRouteStop.indexFirstTransfer;
835  lastTransferIndex = firstTransferIndex + fromRouteStop.countTransfers;
836  } else {
837  // more costly calculation and caching of transfer canddiates
838  transfers = this.data.calculateTransfers(fromRouteStop);
839  firstTransferIndex = 0;
840  lastTransferIndex = transfers.length;
841  }
842 
843  for (int transferIndex = firstTransferIndex; transferIndex < lastTransferIndex; transferIndex++) {
844  RTransfer transfer = transfers[transferIndex];
845 
846  int toRouteStopIndex = transfer.toRouteStop;
847  transferProvider.reset(transfer);
848  int newArrivalTime = arrivalTime + transfer.transferTime;
849  double newArrivalTravelCost = arrivalTravelCost - transfer.transferTime * margUtilityTransitWalk;
850  double newArrivalTransferCost = (fromPE.firstDepartureTime != TIME_UNDEFINED) ? (arrivalTransferCost + this.transferCostCalculator.calcTransferCost(fromPE, transferProvider, data.config, raptorParams, newArrivalTime - fromPE.firstDepartureTime, fromPE.transferCount + 1, arrivalTransferCost, arrivalTime)) : 0;
851  double newTotalArrivalCost = newArrivalTravelCost + newArrivalTransferCost;
852  double prevLeastArrivalCost = this.leastArrivalCostAtRouteStop[toRouteStopIndex];
853  if (newTotalArrivalCost < prevLeastArrivalCost || (!strict && newTotalArrivalCost <= prevLeastArrivalCost)) {
854  RRouteStop toRouteStop = this.data.routeStops[toRouteStopIndex];
855  PathElement pe = new PathElement(fromPE, toRouteStop, fromPE.firstDepartureTime, TIME_UNDEFINED, TIME_UNDEFINED, newArrivalTime, newArrivalTravelCost, newArrivalTransferCost, transfer.transferDistance, fromPE.transferCount + 1, true, transfer, null);
856  this.arrivalPathPerRouteStop[toRouteStopIndex] = pe;
857  this.leastArrivalCostAtRouteStop[toRouteStopIndex] = newTotalArrivalCost;
858  this.improvedRouteStopIndices.set(toRouteStopIndex);
859  int toStopFacilityIndex = toRouteStop.stopFacilityIndex;
860  prevLeastArrivalCost = this.leastArrivalCostAtStop[toStopFacilityIndex];
861  if (newTotalArrivalCost < prevLeastArrivalCost || (!strict && newTotalArrivalCost <= prevLeastArrivalCost)) {
862  // store it in tmp only. We don't want that this PE is used by a stop processed later in the same round. ("parallel update")
863  this.leastArrivalCostAtStop[toStopFacilityIndex] = newTotalArrivalCost;
864  this.tmpArrivalPathPerStop[toStopFacilityIndex] = pe;
865  this.tmpImprovedStops.set(toStopFacilityIndex);
866  }
867  }
868  }
869  }
870  // "parallel update". now copy over the newly improved data after all transfers were handled
871  for (int stopIndex = this.tmpImprovedStops.nextSetBit(0); stopIndex >= 0; stopIndex = this.tmpImprovedStops.nextSetBit(stopIndex + 1)) {
872  PathElement pe = this.tmpArrivalPathPerStop[stopIndex];
873  this.arrivalPathPerStop[stopIndex] = pe;
874  }
875  }
876 
877  private PathElement findLeastCostArrival(Map<TransitStopFacility, InitialStop> destinationStops) {
878  double leastCost = Double.POSITIVE_INFINITY;
879  double leastCostFeederOnly = Double.POSITIVE_INFINITY;
880  PathElement leastCostPath = null;
881  PathElement leastCostPathFeederOnly = null;
882 
884  boolean checkBothPtAndPurelyIntermodalRoutes = intermodalLegOnlyHandling.equals(SwissRailRaptorConfigGroup.IntermodalLegOnlyHandling.avoid) || intermodalLegOnlyHandling.equals(SwissRailRaptorConfigGroup.IntermodalLegOnlyHandling.forbid);
885  for (Map.Entry<TransitStopFacility, InitialStop> e : destinationStops.entrySet()) {
886  TransitStopFacility stop = e.getKey();
887  int stopIndex = this.data.stopFacilityIndices.get(stop);
888  PathElement pe = this.arrivalPathPerStop[stopIndex];
889  if (pe!=null) {
890  InitialStop egressStop = e.getValue();
891  int arrivalTime = (int) (pe.arrivalTime + egressStop.accessTime);
892  double arrivalTravelCost = pe.arrivalTravelCost + egressStop.accessCost;
893  double totalCost = arrivalTravelCost + pe.arrivalTransferCost;
894  if ((totalCost < leastCost) || (totalCost == leastCost && pe.transferCount < leastCostPath.transferCount)) {
895  PathElement egressLegCandidate = new PathElement(pe, null, pe.firstDepartureTime, TIME_UNDEFINED, TIME_UNDEFINED, arrivalTime, arrivalTravelCost, pe.arrivalTransferCost, egressStop.distance, pe.transferCount, true, null, egressStop);
896 
897  if (pe.comingFrom == null && checkBothPtAndPurelyIntermodalRoutes) {
898  if (totalCost < leastCostFeederOnly) {
899  leastCostFeederOnly = totalCost;
900  leastCostPathFeederOnly = egressLegCandidate;
901  }
902  } else {
903  // this is the egress leg
904  leastCostPath = egressLegCandidate;
905  leastCost = totalCost;
906  }
907  }
908 
909  }
910 
911  }
912 
913  if (checkBothPtAndPurelyIntermodalRoutes){
914  if (Double.isInfinite(leastCost)){
915  return leastCostPathFeederOnly;
916  }
917  }
918  return leastCostPath;
919  }
920 
921  private static RaptorRoute createRaptorRoute(Facility fromFacility, Facility toFacility, PathElement destinationPathElement, double departureTime) {
922  ArrayList<PathElement> pes = new ArrayList<>();
923  double arrivalCost = Double.POSITIVE_INFINITY;
924  if (destinationPathElement != null) {
925  arrivalCost = destinationPathElement.arrivalTravelCost + destinationPathElement.arrivalTransferCost;
926  PathElement pe = destinationPathElement;
927  while (pe.comingFrom != null) {
928  pes.add(pe);
929  pe = pe.comingFrom;
930  }
931  pes.add(pe);
932  }
933  Collections.reverse(pes);
934 
935  RaptorRoute raptorRoute = new RaptorRoute(fromFacility, toFacility, arrivalCost);
936  double time = departureTime;
937  TransitStopFacility fromStop = null;
938  int peCount = pes.size();
939  int i = -1;
940  for (PathElement pe : pes) {
941  i++;
942  TransitStopFacility toStop = pe.toRouteStop == null ? null : pe.toRouteStop.routeStop.getStopFacility();
943  double travelTime = pe.arrivalTime - time;
944  if (pe.initialStop != null && pe.initialStop.planElements != null) {
945  raptorRoute.addPlanElements(time, travelTime, pe.initialStop.planElements);
946  } else if (pe.isTransfer) {
947  // add (peCount > 2 || peCount == 2 && !pes.get(0).isTransfer) && to catch case of only access and egress
948  // legs without a real leg in between which was previously caught above by
949  // pes.size() == 2 && pes.get(0).isTransfer && pes.get(1).isTransfer
950  //
951  // in case of peCount < 2 there should be no effect, because peCount-2 < 0 and i will be 0, so i!=peCount - 2
952  if ((peCount > 2 || peCount == 2 && !pes.get(0).isTransfer) && i == peCount - 2 && pes.get(i + 1).isTransfer && (!isIntermodal(pes.get(i + 1).initialStop))) {
953  // the second last element and the last elements are transfers,
954  // in this case the we skip the second last element so there is only one walk leg attached
955  // This merge can only work if it is not intermodal...
956  continue;
957  }
958  String mode = TransportMode.walk;
959  raptorRoute.addNonPt(fromStop, toStop, time, travelTime, pe.distance, mode);
960  } else {
961  TransitLine line = pe.toRouteStop.line;
962  TransitRoute route = pe.toRouteStop.route;
963  raptorRoute.addPt(fromStop, toStop, line, route, pe.toRouteStop.mode, time, pe.boardingTime, pe.vehDepartureTime, pe.arrivalTime, pe.distance);
964  }
965  time = pe.arrivalTime;
966  fromStop = toStop;
967  }
968  return raptorRoute;
969  }
970 
971  private static boolean isIntermodal(InitialStop initialStop) {
972  return initialStop != null && initialStop.planElements != null;
973  }
974 
975  static class PathElement {
976  final PathElement comingFrom;
977  final RRouteStop toRouteStop;
978  final int firstDepartureTime; // the departure time at the start stop
979  final int boardingTime;
980  final int vehDepartureTime;
981  final int arrivalTime;
982  double arrivalTravelCost;
983  double arrivalTransferCost;
984  final double distance;
985  final int transferCount;
986  final boolean isTransfer;
987  final RTransfer transfer;
988  final InitialStop initialStop;
989 
990  PathElement(PathElement comingFrom, RRouteStop toRouteStop, int firstDepartureTime, int boardingTime, int vehDeparturetime, int arrivalTime, double arrivalTravelCost, double arrivalTransferCost, double distance, int transferCount, boolean isTransfer, RTransfer transfer, InitialStop initialStop) {
991  this.comingFrom = comingFrom;
992  this.toRouteStop = toRouteStop;
993  this.firstDepartureTime = firstDepartureTime;
994  this.boardingTime = boardingTime;
995  this.vehDepartureTime = vehDeparturetime;
996  this.arrivalTime = arrivalTime;
997  this.arrivalTravelCost = arrivalTravelCost;
998  this.arrivalTransferCost = arrivalTransferCost;
999  this.distance = distance;
1000  this.transferCount = transferCount;
1001  this.isTransfer = isTransfer;
1002  this.transfer = transfer;
1003  this.initialStop = initialStop;
1004  }
1005  }
1006 
1007  private static class DepartureAtRouteStop {
1008  final RRouteStop routeStop;
1009  final InitialStop accessStop;
1010  final int departureIndex;
1011  final int routeStopIndex;
1012  final int depTime;
1013  final double costOffset;
1014 
1015  DepartureAtRouteStop(RRouteStop routeStop, int routeStopIndex, int departureIndex, int depTime, double costOffset, InitialStop accessStop) {
1016  this.routeStop = routeStop;
1017  this.routeStopIndex = routeStopIndex;
1018  this.departureIndex = departureIndex;
1019  this.depTime = depTime;
1020  this.costOffset = costOffset;
1021  this.accessStop = accessStop;
1022  }
1023  }
1024 
1025  public static final class TravelInfo {
1027  public final int transferCount;
1028 
1030  public final double ptDepartureTime;
1032  public final double ptArrivalTime;
1034  public final double ptTravelTime;
1036  public final double travelCost;
1037 
1039  public final double accessTime;
1040  public final double accessCost;
1041 
1043  public final double waitingTime;
1045  public final double waitingCost;
1046 
1047  private final PathElement destinationPath;
1048 
1049  TravelInfo(Id<TransitStopFacility> departureStop, double departureTime, double arrivalTime, double travelCost, double accessTime, double accessCost, int transferCount, double waitingTime, double waitingCost, PathElement destinationPath) {
1050  this.departureStop = departureStop;
1051  this.ptDepartureTime = departureTime;
1052  this.ptArrivalTime = arrivalTime;
1053  this.ptTravelTime = arrivalTime - departureTime;
1054  this.travelCost = travelCost;
1055  this.accessTime = accessTime;
1056  this.accessCost = accessCost;
1057  this.transferCount = transferCount;
1058  this.waitingTime = waitingTime;
1059  this.waitingCost = waitingCost;
1060  this.destinationPath = destinationPath;
1061  }
1062 
1064  PathElement firstPath = this.destinationPath;
1065  while (firstPath.comingFrom != null) {
1066  firstPath = firstPath.comingFrom;
1067  }
1068 
1069  Facility fromFacility = firstPath.toRouteStop.routeStop.getStopFacility();
1070  Facility toFacility = this.destinationPath.toRouteStop.routeStop.getStopFacility();
1071  return createRaptorRoute(fromFacility, toFacility, this.destinationPath, firstPath.arrivalTime);
1072  }
1073 
1074  public boolean isWalkOnly() {
1075  if (this.destinationPath.comingFrom == null) {
1076  return true;
1077  }
1078  PathElement pe = this.destinationPath;
1079  while (pe != null) {
1080  if (!pe.isTransfer) {
1081  return false;
1082  }
1083  pe = pe.comingFrom;
1084  }
1085  return true;
1086  }
1087  }
1088 
1089  private static class RouteSegmentIteratorImpl implements RouteSegmentIterator {
1090 
1092  private int boardingTime;
1093  private int fromRouteStopIndex;
1094  private int toRouteStopIndex;
1095 
1096  private int routeDepartureTime;
1098 
1099  private double currentInVehicleTime = -1;
1100  private double currentPassengerCount = -1;
1101  private double currentTimeOfDay = -1;
1102  private Id<Departure> currentDepartureId = null;
1103 
1105  this.data = data;
1106  }
1107 
1108  void reset(int departureIndex, int boardingTime, int fromRouteStopIndex, int toRouteStopIndex) {
1109  this.boardingTime = boardingTime;
1110  this.fromRouteStopIndex = fromRouteStopIndex;
1111  this.toRouteStopIndex = toRouteStopIndex;
1112 
1113  this.routeDepartureTime = this.data.departures[departureIndex];
1114  this.currentRouteStopIndex = fromRouteStopIndex;
1115  this.currentInVehicleTime = -1;
1116  this.currentPassengerCount = -1;
1117  this.currentTimeOfDay = -1;
1118  this.currentDepartureId = this.data.departureIds[departureIndex];
1119  }
1120 
1121  @Override
1122  public boolean hasNext() {
1123  return this.currentRouteStopIndex < this.toRouteStopIndex;
1124  }
1125 
1126  @Override
1127  public void next() {
1128  if (this.currentRouteStopIndex >= this.toRouteStopIndex) {
1129  throw new NoSuchElementException();
1130  }
1131 
1132  int departureRouteStopIndex = this.currentRouteStopIndex;
1133  this.currentRouteStopIndex++;
1134  int nextRouteStopIndex = this.currentRouteStopIndex;
1135  RRouteStop depRouteStop = this.data.routeStops[departureRouteStopIndex];
1136  int startTime = this.routeDepartureTime + depRouteStop.departureOffset;
1137  if (departureRouteStopIndex == this.fromRouteStopIndex) {
1138  startTime = this.boardingTime;
1139  }
1140  RRouteStop nextRouteStop = this.data.routeStops[nextRouteStopIndex];
1141  int endTime = this.routeDepartureTime + nextRouteStop.departureOffset;
1142  if (nextRouteStopIndex == this.toRouteStopIndex) {
1143  endTime = this.routeDepartureTime + nextRouteStop.arrivalOffset;
1144  }
1145  this.currentInVehicleTime = endTime - startTime;
1146  this.currentTimeOfDay = startTime;
1147 
1148  DepartureData depData = this.data.occupancyData.getDepartureData(nextRouteStop.line.getId(), nextRouteStop.route.getId(), depRouteStop.routeStop.getStopFacility().getId(), this.currentDepartureId);
1149  this.currentPassengerCount = depData == null ? 0 : depData.paxCountAtDeparture;
1150  }
1151 
1152  @Override
1153  public double getInVehicleTime() {
1154  return this.currentInVehicleTime;
1155  }
1156 
1157  @Override
1158  public double getPassengerCount() {
1159  return this.currentPassengerCount;
1160  }
1161 
1162  @Override
1163  public double getTimeOfDay() {
1164  return this.currentTimeOfDay;
1165  }
1166  }
1167 }
void arrivedAtStop(double departureTime, TransitStopFacility stopFacility, double arrivalTime, int transferCount, Supplier< RaptorRoute > route)
double getInVehicleCost(double inVehicleTime, double marginalUtility_utl_s, Person person, Vehicle vehicle, RaptorParameters paramters, RouteSegmentIterator iterator)
void observeArrival(PathElement pe, RaptorObserver observer)
RaptorRoute calcLeastCostRoute(double depTime, Facility fromFacility, Facility toFacility, List< InitialStop > accessStops, List< InitialStop > egressStops, RaptorParameters parameters, Person person)
DepartureData getDepartureData(Id< TransitLine > transitLine, Id< TransitRoute > transitRoute, Id< TransitStopFacility > stopFacility, Id< Departure > departure)
SwissRailRaptorConfigGroup.IntermodalLegOnlyHandling getIntermodalLegOnlyHandling()
List< RaptorRoute > filterRoutes(List< RaptorRoute > allRoutes)
int findNextDepartureIndex(RRoute route, RRouteStop routeStop, int time)
PathElement findLeastCostArrival(Map< TransitStopFacility, InitialStop > destinationStops)
void checkForBestArrival(int routeStopIndex, double arrivalCost)
void exploreRoutes(RaptorParameters parameters, Person person, CachingTransferProvider transferProvider)
List< RaptorRoute > calcRoutes(double earliestDepTime, double desiredDepTime, double latestDepTime, Facility fromFacility, Facility toFacility, List< InitialStop > accessStops, List< InitialStop > egressStops, RaptorParameters parameters, Person person)
final RaptorTransferCostCalculator transferCostCalculator
static RaptorRoute createRaptorRoute(Facility fromFacility, Facility toFacility, PathElement destinationPathElement, double departureTime)
Map< Id< TransitStopFacility >, TravelInfo > calcLeastCostTree(double depTime, Collection< InitialStop > startStops, RaptorParameters parameters, Person person, RaptorObserver observer)
Map< Id< TransitStopFacility >, TravelInfo > calcLeastCostTree(double depTime, Collection< InitialStop > startStops, RaptorParameters parameters, Person person)
double calcTransferCost(SwissRailRaptorCore.PathElement currentPE, Supplier< Transfer > transfer, RaptorStaticConfig staticConfig, RaptorParameters raptorParams, int totalTravelTime, int totalTransferCount, double existingTransferCosts, double currentTime)
TravelInfo getTravelInfo(PathElement destination, RaptorParameters parameters)
DepartureData getNextAvailableDeparture(Id< TransitLine > transitLine, Id< TransitRoute > transitRoute, Id< TransitStopFacility > stopFacility, double time)
void handleTransfers(boolean strict, RaptorParameters raptorParams, CachingTransferProvider transferProvider)
int findNextDepartureIndexWithConstraints(RRoute route, RRouteStop routeStop, int time)
double calculateOptimalDepartureTime(PathElement leastCostPath, Map< PathElement, InitialStop > initialStopsPerStartPath)
static boolean isIntermodal(InitialStop initialStop)
final RaptorInVehicleCostCalculator inVehicleCostCalculator