MATSIM
TripStructureUtils.java
Go to the documentation of this file.
1 /* *********************************************************************** *
2  * project: org.matsim.*
3  * TripStructureUtils.java
4  * *
5  * *********************************************************************** *
6  * *
7  * copyright : (C) 2013 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 org.matsim.core.router;
21 
22 import java.util.*;
23 import java.util.function.Predicate;
24 
25 import org.apache.logging.log4j.LogManager;
26 import org.apache.logging.log4j.Logger;
27 import org.matsim.api.core.v01.Coord;
33 import org.matsim.core.gbl.Gbl;
37 
54 public final class TripStructureUtils {
55 
56  private static final Logger log = LogManager.getLogger(TripStructureUtils.class);
57 
58  public static final String routingMode = "routingMode";
59 
60  public enum StageActivityHandling {StagesAsNormalActivities, ExcludeStageActivities}
61 
62  private TripStructureUtils() {}
63 
64  // also need this for plain old fashioned legs. kai
65  public static List<Leg> getLegs(final Plan plan) {
66  return getLegs( plan.getPlanElements() );
67  }
68 
69  public static List<Leg> getLegs( final List<? extends PlanElement> planElements ) {
70  final List<Leg> legs = new ArrayList<>();
71 
72  for (PlanElement pe : planElements) {
73  if ( !(pe instanceof Leg) ) continue;
74  legs.add( (Leg) pe );
75  }
76 
77  // it is not backed to the plan: fail if try to modify
78  return Collections.unmodifiableList( legs );
79  }
80 
81  public static List<Activity> getActivities(
82  final Plan plan,
83  final StageActivityHandling stageActivityHandling) {
84  return getActivities(
85  plan.getPlanElements(),
86  stageActivityHandling);
87  }
88 
89  public static List<Activity> getActivities(
90  final List<? extends PlanElement> planElements,
91  final StageActivityHandling stageActivityHandling) {
92  final List<Activity> activities = new ArrayList<>();
93 
94  for (PlanElement pe : planElements) {
95  if ( !(pe instanceof Activity) ) continue;
96  final Activity act = (Activity) pe;
97 
98  switch (stageActivityHandling) {
99  case StagesAsNormalActivities:
100  activities.add(act);
101  break;
102  case ExcludeStageActivities:
104  activities.add(act);
105  }
106  break;
107  default:
109  }
110  }
111 
112  // it is not backed to the plan: fail if try to modify
113  return Collections.unmodifiableList( activities );
114  }
115 
116  public static List<Trip> getTrips( final Plan plan) {
117  return getTrips( plan.getPlanElements());
118  }
119 
120 
121  public static List<Trip> getTrips( final Plan plan, final Predicate<String> isStageActivity) {
122  return getTrips( plan.getPlanElements(), isStageActivity);
123  }
124 
125  public static List<Trip> getTrips( final List<? extends PlanElement> planElements) {
126  return getTrips(planElements, TripStructureUtils::isStageActivityType ) ;
127  }
128 
129 
130  public static List<Trip> getTrips(
131  final List<? extends PlanElement> planElements,
132  final Predicate<String> isStageActivity ) {
133  final List<Trip> trips = new ArrayList<>();
134 
135  int originActivityIndex = -1;
136  int currentIndex = -1;
137  for (PlanElement pe : planElements) {
138  currentIndex++;
139 
140  if ( !(pe instanceof Activity) ) continue;
141  final Activity act = (Activity) pe;
142 
143 // if (StageActivityTypeIdentifier.isStageActivity( act.getType() ) || stageActivityTypes.contains( act.getType() )) continue;
144 // if (StageActivityTypeIdentifier.isStageActivity( act.getType() ) || isStageActivity.test( act.getType() )) continue;
145  // I I don't like the || (= "or"). If I want to identify subtrips, then I want to put in a reduced number of stage
146  // activities!!!! kai, jan'20
147  if ( isStageActivity.test( act.getType() ) ) {
148  continue;
149  }
150 
151  if ( currentIndex - originActivityIndex > 1 ) {
152  // which means, if I am understanding this right, that two activities without a leg in between will not be considered
153  // a trip.
154 
155  trips.add( new Trip(
156  (Activity) planElements.get( originActivityIndex ),
157  // do not back the list by the list in the plan:
158  // according to the documentation, this would result
159  // in an undefined behavior if the full sequence was modified
160  // (for instance by modifying another trip)
161  Collections.unmodifiableList(
162  new ArrayList<>(
163  planElements.subList(
164  originActivityIndex + 1,
165  currentIndex))),
166  act ) );
167  }
168 
169  originActivityIndex = currentIndex;
170  }
171 
172  return Collections.unmodifiableList( trips );
173  }
174 
175  public static Collection<Subtour> getSubtours( final Plan plan) {
176  return getSubtours( plan.getPlanElements(), 0 );
177  }
178 
179  public static Collection<Subtour> getSubtours( final Plan plan, double coordDistance) {
180  return getSubtours( plan.getPlanElements(), coordDistance );
181  }
182 
217  public static Collection<Subtour> getSubtours( final List<? extends PlanElement> planElements, double coordDistance) {
218  return getSubtours(planElements, TripStructureUtils::isStageActivityType, coordDistance );
219  }
220 
226  public static Subtour getUnclosedRootSubtour(final Plan plan) {
227  return new Subtour(TripStructureUtils.getTrips(plan), false);
228  }
229 
230  // for contrib socnetsim only
231  // I think now that we should actually keep this. kai, jan'20
232  @Deprecated
233  public static Collection<Subtour> getSubtours( final Plan plan, final Predicate<String> isStageActivity) {
234  return getSubtours( plan.getPlanElements(), isStageActivity, 0);
235  }
236 
237  public static Collection<Subtour> getSubtours(
238  final List<? extends PlanElement> planElements,
239  final Predicate<String> isStageActivity, double coordDistance) {
240  return getSubtoursFromTrips(getTrips(planElements, isStageActivity), coordDistance);
241  }
242 
243  public static Collection<Subtour> getSubtoursFromTrips(List<Trip> trips, double coordDistance) {
244 
245  final List<Subtour> subtours = new ArrayList<>();
246  Object destinationId = null;
247 
248  // can be either id or coordinate
249  final List<Object> originIds = new ArrayList<>();
250  final List<Trip> nonAllocatedTrips = new ArrayList<>( trips );
251 
252  for (Trip trip : trips) {
253  final Object originId;
254  //use facilities if available
255  if (trip.getOriginActivity().getFacilityId() != null) {
256  originId = trip.getOriginActivity().getFacilityId();
257  } else if (coordDistance > 0 && trip.getOriginActivity().getCoord() != null) {
258  originId = trip.getOriginActivity().getCoord();
259  } else {
260  originId = trip.getOriginActivity().getLinkId();
261  }
262 
263  if ( originId == null ) {
264  throw new NullPointerException( "Facility id, link id and coordinates for origin activity "+trip.getOriginActivity()+
265  " are null!" );
266  }
267 
268  if (destinationId != null && !originId.equals( destinationId )) {
269  throw new RuntimeException( "unconsistent trip location sequence: "+destinationId+" != "+originId );
270  }
271 
272  if (trip.getDestinationActivity().getFacilityId() != null) {
273  destinationId = trip.getDestinationActivity().getFacilityId();
274  } else if (coordDistance > 0 && trip.getDestinationActivity().getCoord() != null) {
275  destinationId = trip.getDestinationActivity().getCoord();
276  } else {
277  destinationId = trip.getDestinationActivity().getLinkId();
278  }
279 
280  if ( destinationId == null ) {
281  throw new NullPointerException( "Facility id, and link id and coordinates for destination activity "+trip.getDestinationActivity()+
282  " are null!" );
283  }
284 
285  originIds.add( originId );
286 
287  int lastIdx = originIds.lastIndexOf(destinationId);
288 
289  // fuzzy lookup for last idx based on coordinates
290  if (coordDistance > 0 && destinationId instanceof Coord destinationCoord) {
291  for (int i = originIds.size() - 1; i >= 0; i--) {
292 
293  Object cmp = originIds.get(i);
294  if (cmp instanceof Coord cmpCoord) {
295  if (CoordUtils.calcEuclideanDistance(destinationCoord, cmpCoord) <= coordDistance) {
296  lastIdx = i;
297  break;
298  }
299  }
300  }
301  }
302 
303  if (lastIdx > -1) {
304  // end of a subtour
305  final int subtourStartIndex = lastIdx;
306  final int subtourEndIndex = originIds.size();
307 
308  final List<Trip> subtour = new ArrayList<>( trips.subList( subtourStartIndex , subtourEndIndex ) );
309  nonAllocatedTrips.removeAll( subtour );
310 
311  // do not consider the locations visited in finished subtours
312  // as possible anchor points
313  for (int i=subtourStartIndex; i < subtourEndIndex; i++) {
314  originIds.set( i , null );
315  }
316 
318  subtours,
319  new Subtour(
320  subtourStartIndex,
321  subtourEndIndex,
322  subtour,
323  true) );
324  }
325  }
326 
327  if (nonAllocatedTrips.size() != 0) {
328  // "open" plan: the root is the sequence of all trips,
329  // even if it is not closed
331  subtours,
332  new Subtour(
333  0,
334  trips.size(),
335  new ArrayList<>( trips ),
336  false));
337  }
338 
339  return Collections.unmodifiableList( subtours );
340  }
341 
342  private static void addSubtourAndUpdateParents(
343  final List<Subtour> subtours,
344  final Subtour newSubtour) {
345  // the parent of a subtour is the first found enclosing subtour
346  for (Subtour existingSubtour : subtours) {
347  if ( existingSubtour.parent != null ) continue;
348  if ( existingSubtour.startIndex < newSubtour.startIndex ) continue;
349  if ( existingSubtour.endIndex < newSubtour.startIndex ) continue;
350 
351  // the trips are parsed in sequence, so it is not possible
352  // that a existing subtour contains elements later than the
353  // end of the new subtour.
354  assert existingSubtour.startIndex < newSubtour.endIndex;
355  assert existingSubtour.endIndex <= newSubtour.endIndex;
356 
357  existingSubtour.parent = newSubtour;
358  newSubtour.children.add( existingSubtour );
359  }
360  subtours.add( newSubtour );
361  }
362 
367  public static OptionalTime getDepartureTime(Trip trip) {
368  // does this always make sense?
369  Leg leg = (Leg) trip.getTripElements().get(0);
370  return leg.getDepartureTime();
371  }
372 
382  public final static class Trip {
383  private final Activity originActivity;
385  private final List<PlanElement> trip;
386  private final List<Leg> legs;
387 
388  Trip( final Activity originActivity,
389  final List<PlanElement> trip,
390  final Activity destinationActivity) {
391  this.originActivity = originActivity;
392  this.trip = trip;
393  this.legs = extractLegs( trip );
394  this.destinationActivity = destinationActivity;
395  }
396 
397  private static List<Leg> extractLegs( final List<PlanElement> trip ) {
398  final List<Leg> legs = new ArrayList<>();
399 
400  for (PlanElement pe : trip) {
401  if ( pe instanceof Leg ) {
402  legs.add( (Leg) pe );
403  }
404  }
405 
406  return Collections.unmodifiableList( legs );
407  }
408 
410  return originActivity;
411  }
412 
414  return destinationActivity;
415  }
416 
417  public List<PlanElement> getTripElements() {
418  return trip;
419  }
420 
421  public List<Leg> getLegsOnly() {
422  return legs;
423  }
424 
429  return originActivity.getAttributes();
430  }
431 
432  @Override
433  public String toString() {
434  return "{Trip: origin="+originActivity+"; "+
435  "trip="+trip+"; "+
436  "destination="+destinationActivity + "; " +
437  getTripAttributes().toString() + "}";
438  }
439 
440  @Override
441  public boolean equals(final Object other) {
442  if ( !(other instanceof Trip) ) return false;
443 
444  final Trip otherTrip = (Trip) other;
445  return otherTrip.originActivity.equals( originActivity ) &&
446  otherTrip.trip.equals( trip ) &&
447  otherTrip.destinationActivity.equals( destinationActivity );
448  }
449 
450  @Override
451  public int hashCode() {
452  return originActivity.hashCode() + trip.hashCode() + destinationActivity.hashCode();
453  }
454  }
455 
456  public static final class Subtour {
457  // this is used at construction to find parents,
458  // but I do not think this should be made accessible from outside.
459  // I even think it should be stored somewhere else.
460  // td, feb.13
461  private final int startIndex;
462  private final int endIndex;
463 
464  private final List<Trip> trips;
465  private final boolean isClosed;
466  Subtour parent = null;
467  final List<Subtour> children = new ArrayList<>();
468 
469  // for tests
470  Subtour(final List<Trip> trips, final boolean isClosed) {
471  this( -1 , -1 , trips , isClosed );
472  }
473 
474  private Subtour(final int startIndex,
475  final int endIndex,
476  final List<Trip> trips,
477  final boolean isClosed) {
478  this.startIndex = startIndex;
479  this.endIndex = endIndex;
480  this.trips = Collections.unmodifiableList( trips );
481  this.isClosed = isClosed;
482  }
483 
484  public List<Trip> getTrips() {
485  return trips;
486  }
487 
488  public List<Trip> getTripsWithoutSubSubtours() {
489  final List<Trip> list = new ArrayList<>();
490 
491  for (Trip t : trips) {
492  boolean isInChildSt = false;
493  for (Subtour child : children) {
494  if ( child.contains( t ) ) {
495  isInChildSt = true;
496  break;
497  }
498  }
499 
500  if ( !isInChildSt ) {
501  list.add( t );
502  }
503  }
504 
505  return list;
506  }
507 
508  private boolean contains(final Trip t) {
509  return trips.contains( t );
510  }
511 
512  public Subtour getParent() {
513  return this.parent;
514  }
515 
516  public Collection<Subtour> getChildren() {
517  return Collections.unmodifiableList( children );
518  }
519 
520  public boolean isClosed() {
521  return isClosed;
522  }
523 
524  @Override
525  public boolean equals(final Object other) {
526  if (other == null) return false;
527  if ( !other.getClass().equals( getClass() ) ) return false;
528  final Subtour s = (Subtour) other;
529  return s.trips.equals( trips ) &&
530  areChildrenCompatible( children , s.children ) &&
531  (s.parent == null ? parent == null : s.parent.equals( parent )) &&
532  (s.isClosed == isClosed);
533  }
534 
535  private static boolean areChildrenCompatible(
536  final List<Subtour> children2,
537  final List<Subtour> children3) {
538  return children2.size() == children3.size();// should check more, but risk of infinite recursion...
539 
540  }
541 
542  @Override
543  public int hashCode() {
544  return trips.hashCode();
545  }
546 
547  @Override
548  public String toString() {
549  return "Subtour: "+trips.toString();
550  }
551  }
552 
553  @Deprecated // use findTripAtPlanElement(...) instead.
554  public static Trip findCurrentTrip( PlanElement pe, Plan plan ) {
555  return findTripAtPlanElement( pe, plan ) ;
556  }
557 
558  public static Trip findTripAtPlanElement( PlanElement currentPlanElement, Plan plan ){
559  return findTripAtPlanElement( currentPlanElement, plan, TripStructureUtils::isStageActivityType ) ;
560  }
561  public static Trip findTripAtPlanElement( PlanElement currentPlanElement, Plan plan, Predicate<String> isStageActivity ) {
562  if ( currentPlanElement instanceof Activity ) {
563 // Gbl.assertIf( StageActivityTypeIdentifier.isStageActivity( ((Activity)currentPlanElement).getType() ) ) ;
564  Gbl.assertIf( isStageActivity.test( ((Activity)currentPlanElement).getType() ) ) ;
565  }
566  List<Trip> trips = getTrips(plan.getPlanElements(), isStageActivity) ;
567  for ( Trip trip : trips ) {
568  int index = trip.getTripElements().indexOf( currentPlanElement ) ;
569  if ( index != -1 ) {
570  return trip ;
571  }
572  }
573  return null ;
574  }
575 
576  public static Trip findTripEndingAtActivity(Activity activity, Plan plan) {
578  List<Trip> trips = getTrips(plan.getPlanElements()) ;
579  for ( Trip trip : trips ) {
580  if ( activity.equals( trip.getDestinationActivity() ) ) {
581  return trip;
582  }
583  }
584  return null ;
585  }
586 
587  public static Trip findTripStartingAtActivity( final Activity activity, final Plan plan ) {
589  List<Trip> trips = getTrips( plan ) ;
590  for ( Trip trip : trips ) {
591  if ( trip.getOriginActivity().equals( activity ) ) {
592  return trip ;
593  }
594  }
595  return null ;
596  }
597 
598  public static String getRoutingMode(Leg leg) {
599  return leg.getRoutingMode();
600  }
601 
602  public static void setRoutingMode(Leg leg, String mode) {
603  leg.setRoutingMode(mode);
604  }
605 
606  // if we make the routing mode identifier replaceable via Guice/Inject, we should return that one here or get rid of the method
608  return new RoutingModeMainModeIdentifier();
609  }
610 
611  public static String identifyMainMode( final List<? extends PlanElement> tripElements) {
612  // first try the routing mode:
613  String mode = TripStructureUtils.getRoutingMode(((Leg) tripElements.get( 0 )));
614  // else see if trip has only one leg, if so, use that mode (situation after initial demand generation)
615  if ( mode == null && tripElements.size()==1 ) {
616  mode = ((Leg) tripElements.get(0)).getMode() ;
617  }
618  if (mode == null) {
619  log.error("Could not find routing mode for trip " + tripElements);
620  }
621  return mode;
622  }
623 
624  public static boolean isStageActivityType( String activityType ) {
625  return StageActivityTypeIdentifier.isStageActivity( activityType ) ;
626  }
627  public static String createStageActivityType( String mode ) {
629  }
630 
631 }
632 
static Subtour getUnclosedRootSubtour(final Plan plan)
static String identifyMainMode(final List<? extends PlanElement > tripElements)
static Collection< Subtour > getSubtours(final List<? extends PlanElement > planElements, double coordDistance)
static List< Leg > getLegs(final List<? extends PlanElement > planElements)
static List< Trip > getTrips(final List<? extends PlanElement > planElements)
static double calcEuclideanDistance(Coord coord, Coord other)
static void assertIf(boolean flag)
Definition: Gbl.java:207
static List< Trip > getTrips(final Plan plan, final Predicate< String > isStageActivity)
static boolean isStageActivityType(String activityType)
static List< Activity > getActivities(final Plan plan, final StageActivityHandling stageActivityHandling)
static Collection< Subtour > getSubtours(final List<? extends PlanElement > planElements, final Predicate< String > isStageActivity, double coordDistance)
static List< Activity > getActivities(final List<? extends PlanElement > planElements, final StageActivityHandling stageActivityHandling)
static MainModeIdentifier getRoutingModeIdentifier()
static Trip findCurrentTrip(PlanElement pe, Plan plan)
static Trip findTripAtPlanElement(PlanElement currentPlanElement, Plan plan, Predicate< String > isStageActivity)
static Trip findTripEndingAtActivity(Activity activity, Plan plan)
static void setRoutingMode(Leg leg, String mode)
static final boolean isStageActivity(final String activityType)
static boolean areChildrenCompatible(final List< Subtour > children2, final List< Subtour > children3)
static List< Trip > getTrips(final Plan plan)
List< PlanElement > getPlanElements()
static Collection< Subtour > getSubtours(final Plan plan)
void setRoutingMode(String routingMode)
static final String NOT_IMPLEMENTED
Definition: Gbl.java:50
static List< Leg > extractLegs(final List< PlanElement > trip)
static Trip findTripAtPlanElement(PlanElement currentPlanElement, Plan plan)
static Collection< Subtour > getSubtours(final Plan plan, final Predicate< String > isStageActivity)
static String createStageActivityType(String mode)
Subtour(final int startIndex, final int endIndex, final List< Trip > trips, final boolean isClosed)
static Collection< Subtour > getSubtours(final Plan plan, double coordDistance)
static void addSubtourAndUpdateParents(final List< Subtour > subtours, final Subtour newSubtour)
static OptionalTime getDepartureTime(Trip trip)
static List< Leg > getLegs(final Plan plan)
static List< Trip > getTrips(final List<? extends PlanElement > planElements, final Predicate< String > isStageActivity)
static Trip findTripStartingAtActivity(final Activity activity, final Plan plan)
static Collection< Subtour > getSubtoursFromTrips(List< Trip > trips, double coordDistance)