MATSIM
NetworkRoutingInclAccessEgressModule.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * *
4  * *********************************************************************** *
5  * *
6  * copyright : (C) 2015 by the members listed in the COPYING, *
7  * LICENSE and WARRANTY file. *
8  * email : info at matsim dot org *
9  * *
10  * *********************************************************************** *
11  * *
12  * This program is free software; you can redistribute it and/or modify *
13  * it under the terms of the GNU General Public License as published by *
14  * the Free Software Foundation; either version 2 of the License, or *
15  * (at your option) any later version. *
16  * See also COPYING, LICENSE and WARRANTY file *
17  * *
18  * *********************************************************************** */
19 
20 package org.matsim.core.router;
21 
23 
24 import java.util.ArrayList;
25 import java.util.Collections;
26 import java.util.List;
27 import java.util.Map;
28 
29 import org.apache.logging.log4j.LogManager;
30 import org.apache.logging.log4j.Logger;
31 import org.matsim.api.core.v01.Coord;
32 import org.matsim.api.core.v01.Id;
33 import org.matsim.api.core.v01.Scenario;
44 import org.matsim.core.config.Config;
47 import org.matsim.core.gbl.Gbl;
59 import org.matsim.vehicles.Vehicle;
61 
62 import javax.annotation.Nullable;
63 
70 public final class NetworkRoutingInclAccessEgressModule implements RoutingModule {
71 
72  private static final Logger log = LogManager.getLogger(NetworkRoutingInclAccessEgressModule.class);
73 
74  private final String mode;
76 
77  private final Network filteredNetwork;
79  private final Scenario scenario;
82  private final Config config;
83 
84  private static boolean hasWarnedAccessEgress = false;
87 
89 
93  @Nullable
94  private final Network invertedNetwork;
95 
97  final String mode,
98  final LeastCostPathCalculator routeAlgo, Scenario scenario, Network filteredNetwork, @Nullable Network invertedNetwork,
99  final RoutingModule accessToNetworkRouter,
100  final RoutingModule egressFromNetworkRouter,
101  final TimeInterpretation timeInterpretation,
102  final MultimodalLinkChooser multimodalLinkChooser) {
103  this.multimodalLinkChooser = multimodalLinkChooser;
104  Gbl.assertNotNull(scenario.getNetwork());
105  Gbl.assertIf(!scenario.getNetwork().getLinks().isEmpty()); // otherwise network for mode probably not defined
106  this.filteredNetwork = filteredNetwork;
107  this.invertedNetwork = invertedNetwork;
108  this.routeAlgo = routeAlgo;
109  this.mode = mode;
110  this.scenario = scenario;
111  this.populationFactory = scenario.getPopulation().getFactory();
112  this.config = scenario.getConfig();
113  this.accessToNetworkRouter = accessToNetworkRouter;
114  this.egressFromNetworkRouter = egressFromNetworkRouter;
115  this.accessEgressType = config.routing().getAccessEgressType();
116  this.timeInterpretation = timeInterpretation;
117  if (accessEgressType.equals(AccessEgressType.none)) {
118  throw new RuntimeException("trying to use access/egress but not switched on in config. "
119  + "currently not supported; there are too many other problems");
121  hasWarnedAccessEgress = true;
122  log.warn("you are using AccessEgressType=" + AccessEgressType.walkConstantTimeToLink +
123  ". That means, access and egress won't get network-routed - even if you specified corresponding RoutingModules for access and egress ");
124  }
125  if (invertedNetwork != null && !(routeAlgo instanceof InvertedLeastPathCalculator)) {
126  throw new IllegalArgumentException("Inverted network must be used with inverted least path calculator.");
127  }
128  }
129 
130  @Override
131  public synchronized List<? extends PlanElement> calcRoute(RoutingRequest request) {
132  // I need this "synchronized" since I want mobsim agents to be able to call this during the mobsim. So when the
133  // mobsim is multi-threaded, multiple agents might call this here at the same time. kai, nov'17
134 
135  final Facility fromFacility = request.getFromFacility();
136  final Facility toFacility = request.getToFacility();
137  final double departureTime = request.getDepartureTime();
138  final Person person = request.getPerson();
139 
140  Gbl.assertNotNull(fromFacility);
141  Gbl.assertNotNull(toFacility);
142 
143  Link accessActLink = multimodalLinkChooser.decideAccessLink(request, filteredNetwork);
144  Link egressActLink = multimodalLinkChooser.decideEgressLink(request, filteredNetwork);
145 
146  double now = departureTime;
147 
148  List<PlanElement> result = new ArrayList<>();
149 
150  // === access:
151  {
152  List<? extends PlanElement> accessTrip = computeAccessTripFromFacilityToLinkIfNecessary(fromFacility, person, accessActLink, now, populationFactory, mode,
153  scenario.getConfig(), request.getAttributes());
154  if(accessTrip == null ) return null; //access trip could not get routed so we return null for the entire trip => will lead to the tripRouter to call fallbackRoutingModule
155  now = timeInterpretation.decideOnElementsEndTime(accessTrip, now).seconds();
156  result.addAll(accessTrip);
157  }
158 
159  // === compute the network leg:
160  {
161  Leg newLeg = this.populationFactory.createLeg(this.mode);
162  newLeg.setDepartureTime(now);
163  now += routeLeg(person, newLeg, accessActLink, egressActLink, now);
164 
165  result.add(newLeg);
166  // log.warn( newLeg );
167  }
168 
169  // === egress:
170  {
171  List<PlanElement> egressTrip = computeEgressTripFromLinkToFacilityIfNecessary(toFacility, person, egressActLink, now, result.get(result.size() - 1), populationFactory, mode,
172  scenario.getConfig(), request.getAttributes());
173  if(egressTrip == null ) return null; //egress trip could not get routed so we return null for the entire trip => will lead to the tripRouter to call fallbackRoutingModule
174  result.addAll(egressTrip);
175  }
176 
177  return result;
178  }
179 
180  private List<PlanElement> computeEgressTripFromLinkToFacilityIfNecessary(final Facility toFacility, final Person person,
181  final Link egressActLink, double departureTime, PlanElement previousPlanElement,
182  final PopulationFactory populationFactory, final String stageActivityType,
183  Config config, Attributes routingAttributes) {
184 
185  log.debug("do bushwhacking leg from link=" + egressActLink.getId() + " to facility=" + toFacility.toString());
186 
187  if (isNotNeedingBushwhackingLeg(toFacility)) {
188  return Collections.emptyList();
189  }
190 
191  Coord startCoord = NetworkUtils.findNearestPointOnLink(toFacility.getCoord(),egressActLink);
192  Gbl.assertNotNull(startCoord);
193  final Id<Link> startLinkId = egressActLink.getId();
194 
195  List<PlanElement> egressTrip = new ArrayList<>();
196  // check whether we already have an identical interaction activity directly before
197  if (previousPlanElement instanceof Leg) {
198  final Activity interactionActivity = createInteractionActivity(startCoord, startLinkId, stageActivityType);
199  egressTrip.add(interactionActivity);
200  } else {
201  // don't add another (interaction) activity
202  // TODO: assuming that this is an interaction activity, e.g. walk - drt interaction - walk
203  // Not clear what we should do if it is not an interaction activity (and how that could happen).
204  }
205 
206  Id<Link> endLinkId = toFacility.getLinkId();
207  if (endLinkId == null) {
208  endLinkId = startLinkId;
209  }
210 
211  if (mode.equals(TransportMode.walk)) {
212  Leg egressLeg = populationFactory.createLeg(TransportMode.non_network_walk);
213  // (Here we need the non_network_walk!! ... since we need a way to bushwhack from the facility to the walk network! kai, may'21)
214 
215  egressLeg.setDepartureTime(departureTime);
216  routeBushwhackingLeg(person, egressLeg, startCoord, toFacility.getCoord(), departureTime, startLinkId, endLinkId, populationFactory, config);
217  egressTrip.add(egressLeg);
219  Leg egressLeg = populationFactory.createLeg(TransportMode.walk);
220  egressLeg.setDepartureTime(departureTime);
221  routeBushwhackingLeg(person, egressLeg, startCoord, toFacility.getCoord(), departureTime, startLinkId, endLinkId, populationFactory, config);
222  double egressTime = NetworkUtils.getLinkEgressTime(egressActLink, mode).orElseThrow(()->new RuntimeException("Egress Time not set for link "+ egressActLink.getId()));
223  egressLeg.setTravelTime(egressTime);
224  egressLeg.getRoute().setTravelTime(egressTime);
225  egressTrip.add(egressLeg);
227  Facility fromFacility = FacilitiesUtils.wrapLinkAndCoord(egressActLink,startCoord);
228  List<? extends PlanElement> networkRoutedEgressTrip = egressFromNetworkRouter.calcRoute(DefaultRoutingRequest.of(fromFacility, toFacility, departureTime, person, routingAttributes));
229  if(networkRoutedEgressTrip == null) return null;
231  double egressTime = NetworkUtils.getLinkEgressTime(egressActLink,mode).orElseThrow(()->new RuntimeException("Egress Time not set for link "+ egressActLink.getId().toString()));
232  Leg leg0 = TripStructureUtils.getLegs(networkRoutedEgressTrip).get(0);
233  double travelTime = leg0.getTravelTime().seconds()+egressTime;
234  leg0.setTravelTime(travelTime);
235  leg0.getRoute().setTravelTime(travelTime);
236  }
237  egressTrip.addAll(networkRoutedEgressTrip);
238  } else {
239  throw new RuntimeException( "should not happen" );
240  }
241  return egressTrip;
242  }
243 
244  private static boolean isNotNeedingBushwhackingLeg(Facility toFacility) {
245  if (toFacility.getCoord() == null) {
246  // facility does not have a coordinate; we cannot bushwhack
247  return true;
248  }
249  // trip ends on link; no need to bushwhack (this is, in fact, not totally clear: might be link on network of other mode)
250  return toFacility instanceof LinkWrapperFacility;
251  }
252 
253  private List<? extends PlanElement> computeAccessTripFromFacilityToLinkIfNecessary(final Facility fromFacility, final Person person,
254  final Link accessActLink, double departureTime,
255  final PopulationFactory populationFactory, final String stageActivityType,
256  Config config, Attributes routingAttributes) {
257  if (isNotNeedingBushwhackingLeg(fromFacility)) {
258  return Collections.emptyList();
259  }
260 
261  Coord endCoord = NetworkUtils.findNearestPointOnLink(fromFacility.getCoord(),accessActLink);
262  List<PlanElement> accessTrip = new ArrayList<>();
263 
264  if (mode.equals(TransportMode.walk)) {
265  Leg accessLeg = populationFactory.createLeg(TransportMode.non_network_walk);
266  // (Here we need the non_network_walk!! ... since we need a way to bushwhack from the facility to the walk network! kai, may'21)
267 
268  accessLeg.setDepartureTime(departureTime);
269 
270  Id<Link> startLinkId = fromFacility.getLinkId();
271  if (startLinkId == null) {
272  startLinkId = accessActLink.getId();
273  }
274 
275  routeBushwhackingLeg(person, accessLeg, fromFacility.getCoord(), endCoord, departureTime, startLinkId, accessActLink.getId(), populationFactory, config);
276  // yyyy might be possible to set the link ids to null. kai & dominik, may'16
277 
278  accessTrip.add(accessLeg);
280  Leg accessLeg = populationFactory.createLeg(TransportMode.walk);
281  accessLeg.setDepartureTime(departureTime);
282  Id<Link> startLinkId = fromFacility.getLinkId();
283  if (startLinkId == null) {
284  startLinkId = accessActLink.getId();
285  }
286  routeBushwhackingLeg(person, accessLeg, fromFacility.getCoord(), endCoord, departureTime, startLinkId, accessActLink.getId(), populationFactory,
287  config);
288 
289  double accessTime = NetworkUtils.getLinkAccessTime(accessActLink, mode).orElseThrow(()->new RuntimeException("Access Time not set for link "+ accessActLink.getId().toString()));
290  accessLeg.setTravelTime(accessTime);
291  accessLeg.getRoute().setTravelTime(accessTime);
292  accessTrip.add(accessLeg);
293 // now += accessTime;
295  Facility toFacility = FacilitiesUtils.wrapLinkAndCoord(accessActLink,endCoord);
296  List<? extends PlanElement> networkRoutedAccessTrip = accessToNetworkRouter.calcRoute(DefaultRoutingRequest.of(fromFacility, toFacility, departureTime, person, routingAttributes));
297  if (networkRoutedAccessTrip == null) return null; //no access trip could be computed for accessMode
299  double accessTime = NetworkUtils.getLinkAccessTime(accessActLink,mode).orElseThrow(()->new RuntimeException("Access Time not set for link "+ accessActLink.getId().toString()));
300  Leg leg0 = TripStructureUtils.getLegs(networkRoutedAccessTrip).get(0);
301  double travelTime = leg0.getTravelTime().seconds()+accessTime;
302  leg0.setTravelTime(travelTime);
303  leg0.getRoute().setTravelTime(travelTime);
304  }
305  accessTrip.addAll(networkRoutedAccessTrip);
306  } else {
307  throw new RuntimeException( "should not happen" );
308  }
309 
310  final Activity interactionActivity = createInteractionActivity(endCoord, accessActLink.getId(), stageActivityType);
311  accessTrip.add(interactionActivity);
312  return accessTrip;
313  }
314 
315  private static Activity createInteractionActivity(final Coord interactionCoord, final Id<Link> interactionLink, final String mode) {
316  return PopulationUtils.createStageActivityFromCoordLinkIdAndModePrefix(interactionCoord, interactionLink, mode);
317  }
318 
319  private static void routeBushwhackingLeg(Person person, Leg leg, Coord fromCoord, Coord toCoord, double depTime,
320  Id<Link> dpLinkId, Id<Link> arLinkId, PopulationFactory pf, Config config) {
323  final Map<String, RoutingConfigGroup.TeleportedModeParams> paramsMap = config.routing().getModeRoutingParams();
324  if ((tmp = paramsMap.get(TransportMode.non_network_walk)) != null) {
325  params = tmp;
326  } else if ((tmp = paramsMap.get(TransportMode.walk)) != null) {
327  params = tmp;
328  } else {
329  log.fatal( "Teleportation (= mode routing) params neither defined for " + TransportMode.walk + " nor for " + TransportMode.non_network_walk + ". There are two cases:" );
330  log.fatal( "(1) " + TransportMode.walk + " is teleported. Then you need to define the corresponding teleportation (= mode routing) params for " + TransportMode.walk + "." );
331  log.fatal( "(2) " + TransportMode.walk + " is routed on the network. Then you need to define the corresponding teleportation (= mode routing) params for "
333  log.fatal("The old default fallback bevhavior was disabled in may'21.");
334  throw new RuntimeException( "Need teleportation params for bushwhacking modes. See log statements above." );
335 
336 // params = new ModeRoutingParams();
337 // // old defaults
338 // params.setBeelineDistanceFactor(1.3);
339 // params.setTeleportedModeSpeed(2.0);
340 
341  // yyyyyy The above may be a source for buggy behavior: If the teleportation params are cleared, then presumably also the
342  // non-network routing is cleared, and then here it will fall back on the auto-magic behavior. kai, may'21.
343 
344  // Not sure if this can happen:
345  // (1) If walk is a non-network mode, then using the params from that is what we want.
346  // (2) If walk is a network mode, then the walk params cannot be defined as teleportation mode params at the same time.
347  // But presumably there are other cases. kai, jun'22
348  }
349 
350  routeBushwhackingLeg(person, leg, fromCoord, toCoord, depTime, dpLinkId, arLinkId, pf, params);
351  }
352 
353  static void routeBushwhackingLeg(Person person, Leg leg, Coord fromCoord, Coord toCoord, double depTime,
354  Id<Link> dpLinkId, Id<Link> arLinkId, PopulationFactory pf, TeleportedModeParams params ) {
355  // I don't think that it makes sense to use a RoutingModule for this, since that again makes assumptions about how to
356  // map facilities, and if you follow through to the teleportation routers one even finds activity wrappers, which is yet another
357  // complication which I certainly don't want here. kai, dec'15
358 
359  // dpLinkId, arLinkId need to be in Route for lots of code to function. So I am essentially putting in the "street address"
360  // for completeness. Note that if we are walking to a parked car, this can be different from the car link id!! kai, dec'15
361 
362  // make simple assumption about distance and walking speed
363  double dist = CoordUtils.calcEuclideanDistance(fromCoord, toCoord);
364 
365  // create an empty route, but with realistic travel time
366  Route route = pf.getRouteFactories().createRoute(Route.class, dpLinkId, arLinkId);
367 
368  Gbl.assertNotNull(params);
369  double beelineDistanceFactor = params.getBeelineDistanceFactor();
370  double networkTravelSpeed = params.getTeleportedModeSpeed();
371 
372  double estimatedNetworkDistance = dist * beelineDistanceFactor;
373  int travTime = (int) (estimatedNetworkDistance / networkTravelSpeed);
374 
375 
376  route.setTravelTime(travTime);
377  route.setDistance(estimatedNetworkDistance);
378  leg.setRoute(route);
379  leg.setDepartureTime(depTime);
380  leg.setTravelTime(travTime);
381  }
382 
383  @Override
384  public String toString() {
385  return "[NetworkRoutingModule: mode=" + this.mode + "]";
386  }
387 
388  /*package (Tests)*/ double routeLeg(Person person, Leg leg, Link fromLink, Link toLink, double depTime) {
389  double travTime;
390 
391  Node startNode = fromLink.getToNode(); // start at the end of the "current" link
392  Node endNode = toLink.getFromNode(); // the target is the start of the link
393 
394  if (toLink != fromLink) { // (a "true" route)
395 
396  if (invertedNetwork != null) {
397  startNode = invertedNetwork.getNodes().get(Id.create(fromLink.getId(), Node.class));
398  endNode = invertedNetwork.getNodes().get(Id.create(toLink.getId(), Node.class));
399  }
400 
401  Id<Vehicle> vehicleId = VehicleUtils.getVehicleId(person, leg.getMode());
402  Vehicle vehicle = scenario.getVehicles().getVehicles().get(vehicleId);
403  Path path = this.routeAlgo.calcLeastCostPath(startNode, endNode, depTime, person, vehicle);
404  if (path == null) {
405  throw new RuntimeException("No route found from node " + startNode.getId() + " to node " + endNode.getId() + " for mode " + mode + ".");
406  }
407 
408  NetworkRoute route = this.populationFactory.getRouteFactories().createRoute(NetworkRoute.class, fromLink.getId(), toLink.getId());
409  route.setLinkIds(fromLink.getId(), NetworkUtils.getLinkIds(path.links), toLink.getId());
410 
411  double relPosOnDepartureLink = 1.0;
412  double relPosOnArrivalLink = 1.0;
413 
414  double maxSpeedOnToLink = Math.min(vehicle.getType().getMaximumVelocity(),toLink.getFreespeed());
415  double travelTimeEstimateOnToLink = (toLink.getLength() / maxSpeedOnToLink) * relPosOnArrivalLink;
416  route.setTravelTime((int) (path.travelTime+travelTimeEstimateOnToLink));
417 
418  route.setTravelCost(path.travelCost);
419  route.setDistance(RouteUtils.calcDistance(route, relPosOnDepartureLink, relPosOnArrivalLink, this.filteredNetwork));
420  route.setVehicleId(vehicleId);
421  leg.setRoute(route);
422  travTime = (int) path.travelTime;
423 
424  } else {
425  // create an empty route == staying on place if toLink == endLink
426  // note that we still do a route: someone may drive from one location to another on the link. kai, dec'15
427  NetworkRoute route = this.populationFactory.getRouteFactories().createRoute(NetworkRoute.class, fromLink.getId(), toLink.getId());
428  route.setTravelTime(0);
429  route.setDistance(0.0);
430  route.setVehicleId(VehicleUtils.getVehicleId(person, leg.getMode()));
431  leg.setRoute(route);
432  travTime = 0;
433  }
434 
435  leg.setDepartureTime(depTime);
436  leg.setTravelTime(travTime);
437 
438  return travTime;
439  }
440 }
Map< Id< Node >, ? extends Node > getNodes()
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)
static void assertIf(boolean flag)
Definition: Gbl.java:207
Map< String, TeleportedModeParams > getModeRoutingParams()
void setDepartureTime(final double seconds)
static OptionalTime getLinkEgressTime(Link link, String routingMode)
Path calcLeastCostPath(Node fromNode, Node toNode, double starttime, final Person person, final Vehicle vehicle)
static< T > Id< T > create(final long key, final Class< T > type)
Definition: Id.java:68
static Facility wrapLinkAndCoord(final Link link, final Coord coord)
static Coord findNearestPointOnLink(Coord coord, Link link)
static Id< Vehicle > getVehicleId(Person person, String mode)
List<? extends PlanElement > calcRoute(RoutingRequest request)
abstract void setVehicleId(final Id< Vehicle > vehicleId)
List<? extends PlanElement > computeAccessTripFromFacilityToLinkIfNecessary(final Facility fromFacility, final Person person, final Link accessActLink, double departureTime, final PopulationFactory populationFactory, final String stageActivityType, Config config, Attributes routingAttributes)
static void assertNotNull(Object obj)
Definition: Gbl.java:212
static List< Id< Link > > getLinkIds(final String links)
static double calcDistance(final NetworkRoute networkRoute, final double relPosOnDepartureLink, final double relPosOnArrivalLink, final Network network)
RoutingConfigGroup routing()
Definition: Config.java:439
Map< Id< Link >, ? extends Link > getLinks()
List< PlanElement > computeEgressTripFromLinkToFacilityIfNecessary(final Facility toFacility, final Person person, final Link egressActLink, double departureTime, PlanElement previousPlanElement, final PopulationFactory populationFactory, final String stageActivityType, Config config, Attributes routingAttributes)
OptionalTime decideOnElementsEndTime(List<? extends PlanElement > elements, final double startTime)
static Activity createInteractionActivity(final Coord interactionCoord, final Id< Link > interactionLink, final String mode)
void setTravelTime(final double seconds)
Map< Id< Vehicle >, Vehicle > getVehicles()
synchronized List<? extends PlanElement > calcRoute(RoutingRequest request)
static OptionalTime getLinkAccessTime(Link link, String routingMode)
static Activity createStageActivityFromCoordLinkIdAndModePrefix(final Coord interactionCoord, final Id< Link > interactionLink, String modePrefix)
static List< Leg > getLegs(final Plan plan)
static void routeBushwhackingLeg(Person person, Leg leg, Coord fromCoord, Coord toCoord, double depTime, Id< Link > dpLinkId, Id< Link > arLinkId, PopulationFactory pf, Config config)
void setTravelTime(final double travelTime)