MATSIM
EditRoutes.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * EditRoutes.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2010 by the members listed in the COPYING, *
8  * LICENSE and WARRANTY file. *
9  * email : info at matsim dot org *
10  * *
11  * *********************************************************************** *
12  * *
13  * This program is free software; you can redistribute it and/or modify *
14  * it under the terms of the GNU General Public License as published by *
15  * the Free Software Foundation; either version 2 of the License, or *
16  * (at your option) any later version. *
17  * See also COPYING, LICENSE and WARRANTY file *
18  * *
19  * *********************************************************************** */
20 
21 package org.matsim.withinday.utils;
22 
23 import java.util.ArrayList;
24 import java.util.List;
25 
26 import org.apache.logging.log4j.LogManager;
27 import org.apache.logging.log4j.Logger;
28 import org.matsim.api.core.v01.Id;
51 import org.matsim.vehicles.Vehicle;
52 
57 public final class EditRoutes {
58  // I think that this class is now for the time being (nov'17) roughly ok: The pure replanning calls just find a possibly
59  // different path to the same destination, which is ok. The relocate calls are marked as "deprecated" to discourage use,
60  // but they are still ok if the user knows what she/he is doing; evidently, with access/egress routing, they will lead to
61  // disconnected trips. kai, nov'17
62 
63 
64  /*
65  * Design thoughts while adding access/egress walk legs to car trips:
66  * . Matsim has a computer science router (LeastCostPathCalculator, from node to node) and a behavioral router (RoutingModule, from
67  * facility to facility).
68  * . For something like replanCurrentRoute it does not really make sense to start at a facility.
69  * . So it seems that for replanCurrentRoute, the computer science router makes more sense.
70  * . This is, however, not true for all the relocate calls, since they also need to reroute the egress leg.
71  * yyyy So, in short: all the relocate methods need to be adapted. For "future", this is easy. For "current", the splicing needs to be re-done. kai, dec'15
72  *
73  * Hm, next thought: It is not fully clear how relocateXxx has worked in the past, since also there the succeeding activity would
74  * have needed adaptation. So in the end the method should just do what it promises (i.e. modify a single leg), and not
75  * think too much about its context. (In the end, will presumably retrofit the mobsim in a way that it is able to process also the
76  * old car trips.)
77  */
78 
79  private static final Logger log = LogManager.getLogger(EditRoutes.class);
80 
81  private Network network ;
84 
85  @Deprecated // I think this only exists since some calls that are now static used to be non-static. kai, nov'17
86  public EditRoutes(){} // for backwards compatibility
87  // yyyyyy ??????
88 
89 // @Deprecated // use ctor with population factory (no need to pass non-API route factory as argument). kai, may'16
90 // public EditRoutes( Network network, LeastCostPathCalculator pathCalculator, RouteFactories routeFactory ) {
91 // this.network = network ;
92 // this.pathCalculator = pathCalculator ;
93 // this.routeFactories = routeFactory ;
94 // }
95  // it is more than a year later; commenting this out. kai, nov'17
96 
97  public EditRoutes( Network network, LeastCostPathCalculator pathCalculator, PopulationFactory popFactory ) {
98  this.network = network ;
99  this.pathCalculator = pathCalculator ;
100  this.routeFactories = popFactory.getRouteFactories() ;
101  }
102 
110  @Deprecated // not consistent with access/egress approach; can only be used if you know exactly what you are doing.
111  // Maybe replanXxx (which does not have these problems) is already sufficient? Otherwise use EditTrips or EditPlans. kai, nov'17
112  public boolean relocateFutureLegRoute(Leg leg, Id<Link> fromLinkId, Id<Link> toLinkId, Person person ) {
113 
114  Link fromLink = network.getLinks().get(fromLinkId);
115  Link toLink = network.getLinks().get(toLinkId);
116 
117  Vehicle vehicle = null ;
118  Node startNode = fromLink.getToNode() ;
119  Node endNode = toLink.getFromNode() ;
120  double starttime = leg.getDepartureTime().seconds();
121  Path path = pathCalculator.calcLeastCostPath(startNode, endNode, starttime, person, vehicle) ;
122 
123  if (path == null) throw new RuntimeException("No route found from node " + startNode.getId() + " to node " + endNode.getId() + ".");
124  NetworkRoute route = this.routeFactories.createRoute(NetworkRoute.class, fromLink.getId(), toLink.getId());
125  route.setLinkIds(fromLink.getId(), NetworkUtils.getLinkIds(path.links), toLink.getId());
126  route.setTravelTime((int) path.travelTime); // yyyy why int? kai, dec'15
127  route.setTravelCost(path.travelCost);
128  route.setDistance(RouteUtils.calcDistance(route,1.,1., this.network));
129  leg.setRoute(route);
130 
131  return true;
132  }
133 
134  public final void replanCurrentLeg( MobsimAgent agent, double now ) {
137  if ( !(pe instanceof Leg) ) {
138  return ;
139  }
140  int currentLinkIndex = WithinDayAgentUtils.getCurrentRouteLinkIdIndex(agent) ;
141  this.replanCurrentLegRoute((Leg)pe, ((HasPerson)agent).getPerson(), currentLinkIndex, now ) ;
143  }
144 
152  @Deprecated // not consistent with access/egress approach; can only be used if you know exactly what you are doing.
153  // Maybe replanXxx is already sufficient? Otherwise use EditTrips or EditPlans. kai, nov'17
154  public static boolean relocateFutureLegRoute(Leg leg, Id<Link> fromLinkId, Id<Link> toLinkId, Person person, Network network, TripRouter tripRouter, Activity fromActivity) {
155  // TripRouter variant; everything else uses the PathCalculator
156 
157  Link fromLink = network.getLinks().get(fromLinkId);
158  Link toLink = network.getLinks().get(toLinkId);
159 
160  Facility fromFacility = new LinkWrapperFacility(fromLink);
161  Facility toFacility = new LinkWrapperFacility(toLink);
162 
163  List<? extends PlanElement> planElements = tripRouter.calcRoute(leg.getMode(), fromFacility, toFacility,
164  leg.getDepartureTime().seconds(), person, fromActivity.getAttributes());
165 
166  if (planElements.size() != 1) {
167  throw new RuntimeException("Expected a list of PlanElements containing exactly one element, " +
168  "but the returned list contained " + planElements.size() + " elements.");
169  }
170 
171  Leg newLeg = (Leg) planElements.get(0);
172 
173  leg.setTravelTime(newLeg.getTravelTime().seconds());
174  leg.setRoute(newLeg.getRoute());
175 
176  return true;
177  }
178 
179 // /**
180 // * Re-plans a future route. The route is given by its leg. It is expected that the
181 // * leg's route is not null and that the start- and end link Ids are set properly.
182 // *
183 // * If the start- and or end-location of the leg have changed, use relocateFutureLegRoute(...)!
184 // *
185 // * @return true when replacing the route worked, false when something went wrong
186 // */
187 // @Deprecated // switch this to relocateFutureTrip, since with egress legs relocating the destination of a single leg leads to disconnected trips. kai, dec'15
188 // public static boolean replanFutureLegRoute(Leg leg, Person person, Network network, TripRouter tripRouter) {
189 // return relocateFutureLegRoute( leg, leg.getRoute().getStartLinkId(), leg.getRoute().getEndLinkId(), person, network, tripRouter ) ;
190 // }
191 
200  public boolean replanFutureLegRoute(Leg leg, Person person ) {
201  // just a pointer to that other method, but this one (which does not change the destination) is still ok also with access/egress
202  // routing
203 
204  return relocateFutureLegRoute( leg, leg.getRoute().getStartLinkId(), leg.getRoute().getEndLinkId(), person ) ;
205  }
206 
207 
208 
209 
210 
211 
212 // /**
213 // * @deprecated switch this to (a new) relocateCurrentTrip, since with egress legs relocating the destination of a single leg leads to disconnected trips. kai, dec'15
214 // */
215 // @Deprecated // switch this to (a new) relocateCurrentTrip, since with egress legs relocating the destination of a single leg leads to disconnected trips. kai, dec'15
216 // public static boolean relocateCurrentRoute( MobsimAgent agent, Id<Link> toLinkId, double now, Network network, TripRouter tripRouter ) {
221 // throw new RuntimeException("currently not implemented: existing version was not consistent with access/egress legs") ;
222 // }
223 //
224 // @Deprecated // use the non-static variant
225 // public static boolean replanCurrentRoute( MobsimAgent agent, double now, Network network, TripRouter tripRouter ) {
226 // Leg leg = WithinDayAgentUtils.getModifiableCurrentLeg(agent) ;
227 // Person person = ((HasPerson) agent).getPerson() ;
228 // int currentLinkIndex = WithinDayAgentUtils.getCurrentRouteLinkIdIndex(agent) ;
230 // throw new RuntimeException("currently not implemented") ;
231 // }
232 
239  @Deprecated // not consistent with access/egress approach; can only be used if you know exactly what you are doing.
240  // Maybe replanXxx is already sufficient? Otherwise use EditTrips or EditPlans. kai, nov'17
241  public boolean relocateCurrentLegRoute(Leg leg, Person person, int currentLinkIndex, Id<Link> toLinkId, double time ) {
242 
243  Route route = leg.getRoute();
244 
245  // if the route type is not supported:
246  if (!(route instanceof NetworkRoute)) {
247  log.warn( "route not instance of NetworkRoute");
248  return false;
249  }
250 
251  NetworkRoute oldRoute = (NetworkRoute) route;
252 
253  /*
254  * Create a List that contains all links of a route, including the Start- and EndLinks.
255  * Get the Id of the current Link.
256  */
257  List<Id<Link>> oldLinkIds = getRouteLinkIds(oldRoute);
258  Id<Link> currentLinkId = oldLinkIds.get(currentLinkIndex);
259 
260  final Link startLink = network.getLinks().get(currentLinkId);
261  final Link endLink = network.getLinks().get(toLinkId);
262 
263  Vehicle vehicle = null ;
264  Path path = this.pathCalculator.calcLeastCostPath(startLink.getToNode(), endLink.getFromNode(), time, person, vehicle) ;
265 
266  spliceNewPathIntoOldRoute(currentLinkIndex, toLinkId, oldRoute, NetworkUtils.getLinkIds(path.links), currentLinkId) ;
267 
268  return true;
269  }
270 
271  static void spliceNewPathIntoOldRoute(int currentLinkIndex, Id<Link> toLinkId, NetworkRoute oldRoute,
272  List<Id<Link>> newLinksIds, Id<Link> currentLinkId) {
273  List<Id<Link>> oldLinkIds = oldRoute.getLinkIds();
274  List<Id<Link>> resultingLinkIds = new ArrayList<>();
275 
276  // REMEMBER: the "currentLinkIndex" does not point to the current position, but one beyond. Probably
277  // because there is a starting link, which is not part of getLinkIds().
278 
279  /*
280  * Get those Links which have already been passed.
281  * oldLinkIds contains also the startLinkId, which should not
282  * be part of the List - it is set separately. Therefore we start
283  * at index 1. CD, 201X
284  * --> I cannot confirm this from the code. Maybe it was like that in earlier times? kai, feb'18
285  */
286  if (currentLinkIndex > 0) {
287 // log.warn("oldLinkIds.size = " + oldLinkIds.size() + "; currentLinkIndex = " + currentLinkIndex ) ;
288 // resultingLinkIds.addAll(oldLinkIds.subList(1, currentLinkIndex + 1));
289  resultingLinkIds.addAll(oldLinkIds.subList(0, currentLinkIndex-1));
290  // (1) I cannot confirm the "starting at 1" from the code, as stated above. Maybe it was like that in earlier code.
291  // (2) [0,current-1[ now means [0,current-2]. Since "current-1" is the current link, current-2 is one before.
292  // This will then be compensated below.
293  }
294  if ( !oldRoute.getStartLinkId().equals( currentLinkId ) ) {
295  // (this happens if the agent is still on the departure link: that is not part of getLinkIds(). Otherwise: )
296  resultingLinkIds.add(currentLinkId);
297  }
298 
299  // Merge old and new Route.
300  /*
301  * Edit cdobler 25.5.2010
302  * If the new leg ends at the current Link, we have to
303  * remove that linkId from the linkIds List - it is stored
304  * in the endLinkId field of the route.
305  * --> This is essentially the addition of the "currentLinkId" above, which
306  * could now just be suppressed in this situation. kai, feb'18
307  * --> I just tried that, but at least the obvious approach does not pass the tests. kai, feb'18
308  */
309 // if (newLinkIds.size() > 0 && path.links.size()>0 && newLinkIds.get(newLinkIds.size() - 1).equals( path.links.get( path.links.size()-1 ) ) ) {
310  if (resultingLinkIds.size() > 0 && newLinksIds.size()>0
311  && resultingLinkIds.get(resultingLinkIds.size() - 1).equals( newLinksIds.get( newLinksIds.size()-1 ) )
312  ) {
313  resultingLinkIds.remove( resultingLinkIds.size()-1 );
314  }
315 
316  resultingLinkIds.addAll( newLinksIds ) ;
317 
318  StringBuilder strb = new StringBuilder() ;
319  for ( int ii= Math.max(0,currentLinkIndex-2) ; ii < Math.min( resultingLinkIds.size(), currentLinkIndex+3) ; ii++ ) {
320  strb.append("-") ;
321  strb.append( resultingLinkIds.get(ii) ) ;
322  strb.append("-") ;
323  }
324  log.info( "linkIds at join: " + strb ) ;
325 
326  // Overwrite old Route
327  oldRoute.setLinkIds(oldRoute.getStartLinkId(), resultingLinkIds, toLinkId );
328  }
329 
330 
331 // /**
332 // * We create a new Plan which contains only the Leg that should be replanned and its previous and next
333 // * Activities. By doing so the PlanAlgorithm will only change the Route of that Leg.
334 // *
335 // * Use currentNodeIndex from a DriverAgent if possible!
336 // *
337 // * Otherwise code it as following:
338 // * startLink - Node1 - routeLink1 - Node2 - routeLink2 - Node3 - endLink
339 // * The currentNodeIndex has to Point to the next Node
340 // * (which is the endNode of the current Link)
341 // */
342 // public static boolean replanCurrentLegRoute(Leg leg, Person person, int currentLinkIndex, double time, Network network, TripRouter tripRouter) {
343 //
344 // Route route = leg.getRoute();
345 //
346 // // if the route type is not supported (e.g. because it is a walking agent)
347 // if (!(route instanceof NetworkRoute)) return false;
348 //
349 // // This is just a special case of relocateCurrentLegRoute where the end link of the route is not changed.
350 // return relocateCurrentLegRoute(leg, person, currentLinkIndex, route.getEndLinkId(), time, network, tripRouter);
351 // throw new RuntimeException("currently not implemented: not consistent with access/egress legs" ) ;
352 // }
353 
365  public boolean replanCurrentLegRoute(Leg leg, Person person, int currentLinkIndex, double time ) {
366  // just a pointer to that other method, but this one (which does not change the destination) is still ok also with access/egress
367  // routing
368 
369  Route route = leg.getRoute();
370  // if the route type is not supported:
371  if (!(route instanceof NetworkRoute)) {
372  log.warn( "route not instance of NetworkRoute");
373  return false;
374  }
375  // (cannot move the above test down into relocateCurrentLegRoute, since it also hedges against route==null. kai, nov'17)
376 
377  return relocateCurrentLegRoute(leg, person, currentLinkIndex, route.getEndLinkId(), time );
378  }
379 
381  return pathCalculator;
382  }
383 
384  // #########################################################################################
385  // helper methods below
386 
387  private static List<Id<Link>> getRouteLinkIds(Route route) {
388  List<Id<Link>> linkIds = new ArrayList<>();
389 
390  if (route instanceof NetworkRoute) {
391  NetworkRoute networkRoute = (NetworkRoute) route;
392  linkIds.add(networkRoute.getStartLinkId());
393  linkIds.addAll(networkRoute.getLinkIds());
394  linkIds.add(networkRoute.getEndLinkId());
395  } else {
396  throw new RuntimeException("Currently only NetworkRoutes are supported for Within-Day Replanning!");
397  }
398 
399  return linkIds;
400  }
401 }
synchronized List<? extends PlanElement > calcRoute(final String mainMode, final Facility fromFacility, final Facility toFacility, final double departureTime, final Person person, final Attributes routingAttributes)
static Integer getCurrentRouteLinkIdIndex(MobsimAgent agent)
static< T > Id< T > get(int index, final Class< T > type)
Definition: Id.java:112
static List< Id< Link > > getRouteLinkIds(Route route)
static Integer getCurrentPlanElementIndex(MobsimAgent agent)
boolean relocateFutureLegRoute(Leg leg, Id< Link > fromLinkId, Id< Link > toLinkId, Person person)
boolean replanFutureLegRoute(Leg leg, Person person)
Path calcLeastCostPath(Node fromNode, Node toNode, double starttime, final Person person, final Vehicle vehicle)
final LeastCostPathCalculator getPathCalculator()
boolean replanCurrentLegRoute(Leg leg, Person person, int currentLinkIndex, double time)
EditRoutes(Network network, LeastCostPathCalculator pathCalculator, PopulationFactory popFactory)
Definition: EditRoutes.java:97
LeastCostPathCalculator pathCalculator
Definition: EditRoutes.java:82
final void replanCurrentLeg(MobsimAgent agent, double now)
List< PlanElement > getPlanElements()
static List< Id< Link > > getLinkIds(final String links)
static double calcDistance(final NetworkRoute networkRoute, final double relPosOnDepartureLink, final double relPosOnArrivalLink, final Network network)
Map< Id< Link >, ? extends Link > getLinks()
boolean relocateCurrentLegRoute(Leg leg, Person person, int currentLinkIndex, Id< Link > toLinkId, double time)
static boolean relocateFutureLegRoute(Leg leg, Id< Link > fromLinkId, Id< Link > toLinkId, Person person, Network network, TripRouter tripRouter, Activity fromActivity)
void setTravelTime(final double seconds)
void setLinkIds(final Id< Link > startLinkId, final List< Id< Link >> linkIds, final Id< Link > endLinkId)