View Javadoc
1   package org.opentrafficsim.kpi.sampling;
2   
3   import java.util.ArrayList;
4   import java.util.Iterator;
5   import java.util.List;
6   
7   import org.djunits.value.vdouble.scalar.Length;
8   import org.djunits.value.vdouble.scalar.Time;
9   import org.djutils.exceptions.Throw;
10  import org.opentrafficsim.kpi.interfaces.GtuData;
11  import org.opentrafficsim.kpi.interfaces.LaneData;
12  
13  /**
14   * Contains all trajectories pertaining to a certain space-time region.
15   * <p>
16   * Copyright (c) 2013-2024 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
17   * BSD-style license. See <a href="https://opentrafficsim.org/docs/license.html">OpenTrafficSim License</a>.
18   * </p>
19   * @author <a href="https://github.com/averbraeck">Alexander Verbraeck</a>
20   * @author <a href="https://github.com/peter-knoppers">Peter Knoppers</a>
21   * @author <a href="https://github.com/wjschakel">Wouter Schakel</a>
22   * @param <G> GTU data type
23   */
24  public class TrajectoryGroup<G extends GtuData> implements Iterable<Trajectory<G>>
25  {
26  
27      /** Start time of trajectories. */
28      private final Time startTime;
29  
30      /** Start position of the section. */
31      private final Length startPosition;
32  
33      /** End position of the section. */
34      private final Length endPosition;
35  
36      /** Lane for which the trajectories have been sampled. */
37      private final LaneData<?> lane;
38  
39      /** Trajectories. */
40      private final List<Trajectory<G>> trajectories = new ArrayList<>();
41  
42      /**
43       * Constructor without length specification. The complete lane will be used.
44       * @param startTime start time of trajectories
45       * @param lane lane
46       */
47      public TrajectoryGroup(final Time startTime, final LaneData<?> lane)
48      {
49          this(startTime, Length.ZERO, lane == null ? null : lane.getLength(), lane);
50      }
51  
52      /**
53       * Constructor.
54       * @param startTime start time of trajectory group
55       * @param startPosition start position
56       * @param endPosition end position
57       * @param lane the lane
58       */
59      public TrajectoryGroup(final Time startTime, final Length startPosition, final Length endPosition, final LaneData<?> lane)
60      {
61          Throw.whenNull(startTime, "Start time may not be null.");
62          // keep before position check; prevents "End position may not be null" due to missing direction in other constructor
63          Throw.whenNull(lane, "Lane time may not be null.");
64          Throw.whenNull(startPosition, "Start position may not be null");
65          Throw.whenNull(endPosition, "End position may not be null");
66          Throw.when(startPosition.gt(endPosition), IllegalArgumentException.class,
67                  "Start position should be smaller than end position in the direction of travel");
68          this.startTime = startTime;
69          this.startPosition = startPosition;
70          this.endPosition = endPosition;
71          this.lane = lane;
72      }
73  
74      /**
75       * Add trajectory.
76       * @param trajectory trajectory to add
77       */
78      public final synchronized void addTrajectory(final Trajectory<G> trajectory)
79      {
80          this.trajectories.add(trajectory);
81      }
82  
83      /**
84       * Returns the start time.
85       * @return start time
86       */
87      public final Time getStartTime()
88      {
89          return this.startTime;
90      }
91  
92      /**
93       * Returns the length.
94       * @return length
95       */
96      public final Length getLength()
97      {
98          return this.endPosition.minus(this.startPosition);
99      }
100 
101     /**
102      * Whether this {@code TrajectoryGroup} holds the given trajectory. Note that this is false if the given trajectory is
103      * derived from a trajectory in this {@code TrajectoryGroup} (e.g. a subset of).
104      * @param trajectory trajectory
105      * @return whether this {@code TrajectoryGroup} holds the given trajectory.
106      */
107     public final boolean contains(final Trajectory<?> trajectory)
108     {
109         return this.trajectories.contains(trajectory);
110     }
111 
112     /**
113      * Returns the number of trajectories in this group.
114      * @return number of trajectories in this group
115      */
116     public final int size()
117     {
118         return this.trajectories.size();
119     }
120 
121     /**
122      * Returns a list of trajectories.
123      * @return list of trajectories
124      */
125     public final List<Trajectory<G>> getTrajectories()
126     {
127         return new ArrayList<>(this.trajectories);
128     }
129 
130     /**
131      * Returns trajectory group between two locations.
132      * @param x0 start length
133      * @param x1 end length
134      * @return list of trajectories
135      */
136     public final synchronized TrajectoryGroup<G> getTrajectoryGroup(final Length x0, final Length x1)
137     {
138         Length minLenght = Length.max(x0, this.startPosition);
139         Length maxLenght = Length.min(x1, this.endPosition);
140         TrajectoryGroup<G> out = new TrajectoryGroup<>(this.startTime, minLenght, maxLenght, this.lane);
141         for (Trajectory<G> trajectory : this.trajectories)
142         {
143             Trajectory<G> sub = trajectory.subSet(x0, x1);
144             if (sub.size() > 0)
145             {
146                 out.addTrajectory(sub);
147             }
148         }
149         return out;
150     }
151 
152     /**
153      * Returns trajectory group between two times.
154      * @param t0 start time
155      * @param t1 end time
156      * @return list of trajectories
157      */
158     public final synchronized TrajectoryGroup<G> getTrajectoryGroup(final Time t0, final Time t1)
159     {
160         TrajectoryGroup<G> out = new TrajectoryGroup<>(this.startTime.lt(t0) ? t0 : this.startTime, this.lane);
161         for (Trajectory<G> trajectory : this.trajectories)
162         {
163             Trajectory<G> sub = trajectory.subSet(t0, t1);
164             if (sub.size() > 0)
165             {
166                 out.addTrajectory(sub);
167             }
168         }
169         return out;
170     }
171 
172     /**
173      * Returns trajectory group between two locations and between two times.
174      * @param x0 start length
175      * @param x1 end length
176      * @param t0 start time
177      * @param t1 end time
178      * @return list of trajectories
179      */
180     public final synchronized TrajectoryGroup<G> getTrajectoryGroup(final Length x0, final Length x1, final Time t0,
181             final Time t1)
182     {
183         TrajectoryGroup<G> out = new TrajectoryGroup<>(this.startTime.lt(t0) ? t0 : this.startTime, this.lane);
184         for (Trajectory<G> trajectory : this.trajectories)
185         {
186             out.addTrajectory(trajectory.subSet(x0, x1, t0, t1));
187         }
188         return out;
189     }
190 
191     /**
192      * Returns the lane.
193      * @return lane
194      */
195     public final LaneData<?> getLane()
196     {
197         return this.lane;
198     }
199 
200     @Override
201     public final int hashCode()
202     {
203         final int prime = 31;
204         int result = 1;
205         result = prime * result + this.lane.hashCode();
206         result = prime * result + this.endPosition.hashCode();
207         result = prime * result + this.startPosition.hashCode();
208         result = prime * result + this.startTime.hashCode();
209         result = prime * result + this.trajectories.hashCode();
210         return result;
211     }
212 
213     @Override
214     public final boolean equals(final Object obj)
215     {
216         if (this == obj)
217         {
218             return true;
219         }
220         if (obj == null)
221         {
222             return false;
223         }
224         if (getClass() != obj.getClass())
225         {
226             return false;
227         }
228         TrajectoryGroup<?> other = (TrajectoryGroup<?>) obj;
229         if (!this.lane.equals(other.lane))
230         {
231             return false;
232         }
233         if (!this.endPosition.equals(other.endPosition))
234         {
235             return false;
236         }
237         if (!this.startPosition.equals(other.startPosition))
238         {
239             return false;
240         }
241         if (!this.startTime.equals(other.startTime))
242         {
243             return false;
244         }
245         if (!this.trajectories.equals(other.trajectories))
246         {
247             return false;
248         }
249         return true;
250     }
251 
252     @Override
253     public final String toString()
254     {
255         return "TrajectoryGroup [startTime=" + this.startTime + ", minLength=" + this.startPosition + ", maxLength="
256                 + this.endPosition + ", lane=" + this.lane + ", collected "
257                 + (this.trajectories == null ? "null" : this.trajectories.size()) + " trajectories]";
258     }
259 
260     @Override
261     public Iterator<Trajectory<G>> iterator()
262     {
263         return this.trajectories.iterator();
264     }
265 
266 }