MATSIM
DefaultRaptorStopFinder.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 
22 import java.util.ArrayList;
23 import java.util.Collection;
24 import java.util.Collections;
25 import java.util.HashMap;
26 import java.util.List;
27 import java.util.Map;
28 import java.util.Random;
29 import java.util.stream.Collectors;
30 
31 import jakarta.inject.Inject;
32 import jakarta.inject.Provider;
33 
34 import org.matsim.api.core.v01.Coord;
35 import org.matsim.api.core.v01.Id;
43 import org.matsim.core.config.Config;
56 
59 
63 public class DefaultRaptorStopFinder implements RaptorStopFinder {
64 
66  private final Map<String, RoutingModule> routingModules;
67  private final Random random = MatsimRandom.getLocalInstance();
68 
69  @Inject
70  public DefaultRaptorStopFinder(Config config, RaptorIntermodalAccessEgress intermodalAE, Map<String, Provider<RoutingModule>> routingModuleProviders) {
71  this.intermodalAE = intermodalAE;
72 
74  this.routingModules = new HashMap<>();
75  if (srrConfig.isUseIntermodalAccessEgress()) {
77  String mode = params.getMode();
78  this.routingModules.put(mode, routingModuleProviders.get(mode).get());
79  }
80  }
81  }
82 
83  public DefaultRaptorStopFinder(RaptorIntermodalAccessEgress intermodalAE, Map<String, RoutingModule> routingModules) {
84  this.intermodalAE = intermodalAE;
85  this.routingModules = routingModules;
86  }
87 
88  @Override
89  public List<InitialStop> findStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters, SwissRailRaptorData data, RaptorStopFinder.Direction type) {
90  if (type == Direction.ACCESS) {
91  return findAccessStops(fromFacility, toFacility, person, departureTime, routingAttributes, parameters, data);
92  }
93  if (type == Direction.EGRESS) {
94  return findEgressStops(fromFacility, toFacility, person, departureTime, routingAttributes, parameters, data);
95  }
96  return Collections.emptyList();
97  }
98 
99  private List<InitialStop> findAccessStops(Facility fromFacility, Facility toFacility, Person person, double departureTime,
100  Attributes routingAttributes, RaptorParameters parameters, SwissRailRaptorData data) {
101  SwissRailRaptorConfigGroup srrCfg = parameters.getConfig();
102  if (srrCfg.isUseIntermodalAccessEgress()) {
103  return findIntermodalStops(fromFacility, toFacility, person, departureTime, routingAttributes, Direction.ACCESS, parameters, data);
104  } else {
105  double distanceFactor = data.config.getBeelineWalkDistanceFactor();
106  List<TransitStopFacility> stops = findNearbyStops(fromFacility, parameters, data);
107  List<InitialStop> initialStops = stops.stream().map(stop -> {
108  double beelineDistance = CoordUtils.calcEuclideanDistance(stop.getCoord(), fromFacility.getCoord());
109  double accessTime = TransitScheduleUtils.getStopAccessTime(stop);
110  double travelTime = Math.ceil(beelineDistance / parameters.getBeelineWalkSpeed()) + accessTime;
111  double disutility = travelTime * -parameters.getMarginalUtilityOfTravelTime_utl_s(TransportMode.walk);
112  return new InitialStop(stop, disutility, travelTime, beelineDistance * distanceFactor, TransportMode.walk);
113  }).collect(Collectors.toList());
114  return initialStops;
115  }
116  }
117 
118  private List<InitialStop> findEgressStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters, SwissRailRaptorData data) {
119  SwissRailRaptorConfigGroup srrCfg = parameters.getConfig();
120  if (srrCfg.isUseIntermodalAccessEgress()) {
121  return findIntermodalStops(fromFacility, toFacility, person, departureTime, routingAttributes, Direction.EGRESS, parameters, data);
122  } else {
123  double distanceFactor = data.config.getBeelineWalkDistanceFactor();
124  List<TransitStopFacility> stops = findNearbyStops(toFacility, parameters, data);
125  List<InitialStop> initialStops = stops.stream().map(stop -> {
126  double beelineDistance = CoordUtils.calcEuclideanDistance(stop.getCoord(), toFacility.getCoord());
127  double egressTime = TransitScheduleUtils.getStopEgressTime(stop);
128  double travelTime = Math.ceil(beelineDistance / parameters.getBeelineWalkSpeed()) + egressTime;
129  double disutility = travelTime * -parameters.getMarginalUtilityOfTravelTime_utl_s(TransportMode.walk);
130  return new InitialStop(stop, disutility, travelTime, beelineDistance * distanceFactor, TransportMode.walk);
131  }).collect(Collectors.toList());
132  return initialStops;
133  }
134  }
135 
136  private List<InitialStop> findIntermodalStops(Facility fromFacility, Facility toFacility, Person person, double departureTime,
137  Attributes routingAttributes, Direction direction, RaptorParameters parameters, SwissRailRaptorData data) {
138  SwissRailRaptorConfigGroup srrCfg = parameters.getConfig();
139  Facility facility = Direction.ACCESS == direction ? fromFacility : toFacility;
140  double x = facility.getCoord().getX();
141  double y = facility.getCoord().getY();
142  List<InitialStop> initialStops = new ArrayList<>();
143  switch (srrCfg.getIntermodalAccessEgressModeSelection()) {
144  case CalcLeastCostModePerStop:
146  addInitialStopsForParamSet(fromFacility, toFacility, person, departureTime, routingAttributes, direction, parameters, data, x, y, initialStops, parameterSet);
147  }
148  break;
149  case RandomSelectOneModePerRoutingRequestAndDirection:
150  int counter = 0;
151  do {
152  int rndSelector = random.nextInt(srrCfg.getIntermodalAccessEgressParameterSets().size());
153  addInitialStopsForParamSet(fromFacility, toFacility, person, departureTime, routingAttributes, direction, parameters, data, x, y,
154  initialStops, srrCfg.getIntermodalAccessEgressParameterSets().get(rndSelector));
155  counter++;
156  // try again if no initial stop was found for the parameterset. Avoid infinite loop by limiting number of tries.
157  } while (initialStops.isEmpty() && counter < 2 * srrCfg.getIntermodalAccessEgressParameterSets().size());
158  break;
159  default:
160  throw new RuntimeException(srrCfg.getIntermodalAccessEgressModeSelection() + " : not implemented!");
161  }
162  return initialStops;
163  }
164 
165  private void addInitialStopsForParamSet(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, Direction direction, RaptorParameters parameters, SwissRailRaptorData data, double x, double y, List<InitialStop> initialStops, IntermodalAccessEgressParameterSet paramset) {
166  String mode = paramset.getMode();
167  String linkIdAttribute = paramset.getLinkIdAttribute();
168  String personFilterAttribute = paramset.getPersonFilterAttribute();
169  String personFilterValue = paramset.getPersonFilterValue();
170  String stopFilterAttribute = paramset.getStopFilterAttribute();
171  String stopFilterValue = paramset.getStopFilterValue();
172 
173  Facility facility = Direction.ACCESS == direction ? fromFacility : toFacility;
174  boolean personMatches = true;
175  if (personFilterAttribute != null) {
176  Object attr = person.getAttributes().getAttribute(personFilterAttribute);
177  String attrValue = attr == null ? null : attr.toString();
178  personMatches = personFilterValue.equals(attrValue);
179  }
180 
181  if (personMatches) {
182  QuadTree<TransitStopFacility> filteredStopsQT;
183  if (stopFilterAttribute != null) {
184  data.prepareStopFilterQuadTreeIfNotExistent(stopFilterAttribute, stopFilterValue);
185  filteredStopsQT = data.stopFilterAttribute2Value2StopsQT.get(stopFilterAttribute).get(stopFilterValue);
186  } else {
187  filteredStopsQT = data.stopsQT;
188  }
189 
190  double distance = CoordUtils.calcEuclideanDistance(fromFacility.getCoord(), toFacility.getCoord());
191  double tripBasedSearchRadius = distance * paramset.getShareTripSearchRadius();
192  double searchRadius = Math.min(paramset.getInitialSearchRadius(), paramset.getMaxRadius());
193  searchRadius = Math.min(searchRadius, tripBasedSearchRadius);
194  Collection<TransitStopFacility> stopFacilities = filteredStopsQT.getDisk(x, y, searchRadius);
195  if (stopFacilities.size() < 2) {
196  TransitStopFacility nearestStop = filteredStopsQT.getClosest(x, y);
197  double nearestDistance = CoordUtils.calcEuclideanDistance(facility.getCoord(), nearestStop.getCoord());
198  searchRadius = Math.min(nearestDistance + paramset.getSearchExtensionRadius(), paramset.getMaxRadius());
199  stopFacilities = filteredStopsQT.getDisk(x, y, searchRadius);
200  }
201 
202  for (TransitStopFacility stop : stopFacilities) {
203  Facility stopFacility = stop;
204  if (linkIdAttribute != null) {
205  Object attr = stop.getAttributes().getAttribute(linkIdAttribute);
206  if (attr != null) {
207  stopFacility = new ChangedLinkFacility(stop, Id.create(attr.toString(), Link.class));
208  }
209  }
210 
211  List<? extends PlanElement> routeParts;
212  if (direction == Direction.ACCESS) {
213  RoutingModule module = this.routingModules.get(mode);
214  routeParts = module.calcRoute(DefaultRoutingRequest.of(facility, stopFacility, departureTime, person, routingAttributes));
215  } else { // it's Egress
216  // We don't know the departure time for the egress trip, so just use the original departureTime,
217  // although it is wrong and might result in a wrong traveltime and thus wrong route.
218  RoutingModule module = this.routingModules.get(mode);
219  routeParts = module.calcRoute(DefaultRoutingRequest.of(stopFacility, facility, departureTime, person, routingAttributes));
220  if (routeParts == null) {
221  // the router for the access/egress mode could not find a route, skip that access/egress mode
222  continue;
223  }
224  }
225  if (routeParts == null) {
226  // the router for the access/egress mode could not find a route, skip that access/egress mode
227  continue;
228  }
229  double accessTime = TransitScheduleUtils.getStopAccessTime(stop);
230  double egressTime = TransitScheduleUtils.getStopEgressTime(stop);
231  if ((stopFacility != stop) || accessTime>0.0 || egressTime>0.0) {
232  if (direction == Direction.ACCESS) {
234  Route transferRoute = RouteUtils.createGenericRouteImpl(stopFacility.getLinkId(), stop.getLinkId());
235  transferRoute.setTravelTime(accessTime);
236  transferRoute.setDistance(0);
237  transferLeg.setRoute(transferRoute);
238  transferLeg.setTravelTime(accessTime);
239 
240  List<PlanElement> tmp = new ArrayList<>(routeParts.size() + 1);
241  tmp.addAll(routeParts);
242  tmp.add(transferLeg);
243  routeParts = tmp;
244  } else {
246  Route transferRoute = RouteUtils.createGenericRouteImpl(stop.getLinkId(), stopFacility.getLinkId());
247  transferRoute.setTravelTime(egressTime);
248  transferRoute.setDistance(0);
249  transferLeg.setRoute(transferRoute);
250  transferLeg.setTravelTime(egressTime);
251 
252  List<PlanElement> tmp = new ArrayList<>(routeParts.size() + 1);
253  tmp.add(transferLeg);
254  tmp.addAll(routeParts);
255  routeParts = tmp;
256  }
257  }
258  RaptorIntermodalAccessEgress.RIntermodalAccessEgress accessEgress = this.intermodalAE.calcIntermodalAccessEgress(routeParts, parameters, person, direction);
259  InitialStop iStop = new InitialStop(stop, accessEgress.disutility, accessEgress.travelTime, accessEgress.routeParts);
260  initialStops.add(iStop);
261 
262  if (direction == Direction.EGRESS) {
263  // clear the (wrong) departureTime so users don't get confused
264  // do it only after passing it to RIntermodalAccessEgress, in case this wants to make some use of it.
265  for (PlanElement pe : routeParts) {
266  if (pe instanceof Leg) {
267  ((Leg) pe).setDepartureTimeUndefined();
268  }
269  }
270  }
271  }
272  }
273  }
274 
275  private List<TransitStopFacility> findNearbyStops(Facility facility, RaptorParameters parameters, SwissRailRaptorData data) {
276  double x = facility.getCoord().getX();
277  double y = facility.getCoord().getY();
278  Collection<TransitStopFacility> stopFacilities = data.stopsQT.getDisk(x, y, parameters.getSearchRadius());
279  if (stopFacilities.size() < 2) {
280  TransitStopFacility nearestStop = data.stopsQT.getClosest(x, y);
281  double nearestDistance = CoordUtils.calcEuclideanDistance(facility.getCoord(), nearestStop.getCoord());
282  stopFacilities = data.stopsQT.getDisk(x, y, nearestDistance + parameters.getExtensionRadius());
283  }
284  if (stopFacilities instanceof List) {
285  return (List<TransitStopFacility>) stopFacilities;
286  }
287  return new ArrayList<>(stopFacilities);
288  }
289 
290  private static class ChangedLinkFacility implements Facility, Identifiable<TransitStopFacility> {
291 
293  private final Id<Link> linkId;
294 
295  ChangedLinkFacility(final TransitStopFacility delegate, final Id<Link> linkId) {
296  this.delegate = delegate;
297  this.linkId = linkId;
298  }
299 
300  @Override
301  public Id<Link> getLinkId() {
302  return this.linkId;
303  }
304 
305  @Override
306  public Coord getCoord() {
307  return this.delegate.getCoord();
308  }
309 
310  @Override
311  public Map<String, Object> getCustomAttributes() {
312  return this.delegate.getCustomAttributes();
313  }
314 
315  @Override
317  return this.delegate.getId();
318  }
319  }
320 }
synchronized void prepareStopFilterQuadTreeIfNotExistent(String stopFilterAttribute, String stopFilterValue)
List< IntermodalAccessEgressParameterSet > getIntermodalAccessEgressParameterSets()
void setDistance(final double distance)
static RoutingRequest of(Facility fromFacility, Facility toFacility, double departureTime, Person person, Attributes attributes)
static double calcEuclideanDistance(Coord coord, Coord other)
List< InitialStop > findStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters, SwissRailRaptorData data, RaptorStopFinder.Direction type)
void addInitialStopsForParamSet(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, Direction direction, RaptorParameters parameters, SwissRailRaptorData data, double x, double y, List< InitialStop > initialStops, IntermodalAccessEgressParameterSet paramset)
static< T extends ConfigGroup > T addOrGetModule(Config config, Class< T > moduleClass)
Map< String, Object > getCustomAttributes()
static Route createGenericRouteImpl(Id< Link > startLinkId, Id< Link > endLinkId)
static double getStopAccessTime(TransitStopFacility stopFacility)
static< T > Id< T > create(final long key, final Class< T > type)
Definition: Id.java:68
DefaultRaptorStopFinder(Config config, RaptorIntermodalAccessEgress intermodalAE, Map< String, Provider< RoutingModule >> routingModuleProviders)
List< InitialStop > findEgressStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters, SwissRailRaptorData data)
List< TransitStopFacility > findNearbyStops(Facility facility, RaptorParameters parameters, SwissRailRaptorData data)
List<? extends PlanElement > calcRoute(RoutingRequest request)
static Leg createLeg(String transportMode)
IntermodalAccessEgressModeSelection getIntermodalAccessEgressModeSelection()
List< InitialStop > findAccessStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, RaptorParameters parameters, SwissRailRaptorData data)
T getClosest(final double x, final double y)
Definition: QuadTree.java:130
DefaultRaptorStopFinder(RaptorIntermodalAccessEgress intermodalAE, Map< String, RoutingModule > routingModules)
RIntermodalAccessEgress calcIntermodalAccessEgress(List<? extends PlanElement > legs, RaptorParameters params, Person person, Direction direction)
void setTravelTime(final double seconds)
Collection< T > getDisk(final double x, final double y, final double distance)
Definition: QuadTree.java:142
static double getStopEgressTime(TransitStopFacility stopFacility)
List< InitialStop > findIntermodalStops(Facility fromFacility, Facility toFacility, Person person, double departureTime, Attributes routingAttributes, Direction direction, RaptorParameters parameters, SwissRailRaptorData data)
void setTravelTime(final double travelTime)