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.Duration;
8   import org.djunits.value.vdouble.scalar.Length;
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 Duration 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 Duration 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 Duration startTime, final Length startPosition, final Length endPosition,
60              final LaneData<?> lane)
61      {
62          Throw.whenNull(startTime, "Start time may not be null.");
63          // keep before position check; prevents "End position may not be null" due to missing direction in other constructor
64          Throw.whenNull(lane, "Lane time may not be null.");
65          Throw.whenNull(startPosition, "Start position may not be null");
66          Throw.whenNull(endPosition, "End position may not be null");
67          Throw.when(startPosition.gt(endPosition), IllegalArgumentException.class,
68                  "Start position should be smaller than end position in the direction of travel");
69          this.startTime = startTime;
70          this.startPosition = startPosition;
71          this.endPosition = endPosition;
72          this.lane = lane;
73      }
74  
75      /**
76       * Add trajectory.
77       * @param trajectory trajectory to add
78       */
79      public final synchronized void addTrajectory(final Trajectory<G> trajectory)
80      {
81          this.trajectories.add(trajectory);
82      }
83  
84      /**
85       * Returns the start time.
86       * @return start time
87       */
88      public final Duration getStartTime()
89      {
90          return this.startTime;
91      }
92  
93      /**
94       * Returns the length.
95       * @return length
96       */
97      public final Length getLength()
98      {
99          return this.endPosition.minus(this.startPosition);
100     }
101 
102     /**
103      * Whether this {@code TrajectoryGroup} holds the given trajectory. Note that this is false if the given trajectory is
104      * derived from a trajectory in this {@code TrajectoryGroup} (e.g. a subset of).
105      * @param trajectory trajectory
106      * @return whether this {@code TrajectoryGroup} holds the given trajectory.
107      */
108     public final boolean contains(final Trajectory<?> trajectory)
109     {
110         return this.trajectories.contains(trajectory);
111     }
112 
113     /**
114      * Returns the number of trajectories in this group.
115      * @return number of trajectories in this group
116      */
117     public final int size()
118     {
119         return this.trajectories.size();
120     }
121 
122     /**
123      * Returns a list of trajectories.
124      * @return list of trajectories
125      */
126     public final List<Trajectory<G>> getTrajectories()
127     {
128         return new ArrayList<>(this.trajectories);
129     }
130 
131     /**
132      * Returns trajectory group between two locations.
133      * @param x0 start length
134      * @param x1 end length
135      * @return list of trajectories
136      */
137     public final synchronized TrajectoryGroup<G> getTrajectoryGroup(final Length x0, final Length x1)
138     {
139         Length minLenght = Length.max(x0, this.startPosition);
140         Length maxLenght = Length.min(x1, this.endPosition);
141         TrajectoryGroup<G> out = new TrajectoryGroup<>(this.startTime, minLenght, maxLenght, this.lane);
142         for (Trajectory<G> trajectory : this.trajectories)
143         {
144             Trajectory<G> sub = trajectory.subSet(x0, x1);
145             if (sub.size() > 0)
146             {
147                 out.addTrajectory(sub);
148             }
149         }
150         return out;
151     }
152 
153     /**
154      * Returns trajectory group between two times.
155      * @param t0 start time
156      * @param t1 end time
157      * @return list of trajectories
158      */
159     public final synchronized TrajectoryGroup<G> getTrajectoryGroup(final Duration t0, final Duration t1)
160     {
161         TrajectoryGroup<G> out = new TrajectoryGroup<>(this.startTime.lt(t0) ? t0 : this.startTime, this.lane);
162         for (Trajectory<G> trajectory : this.trajectories)
163         {
164             Trajectory<G> sub = trajectory.subSet(t0, t1);
165             if (sub.size() > 0)
166             {
167                 out.addTrajectory(sub);
168             }
169         }
170         return out;
171     }
172 
173     /**
174      * Returns trajectory group between two locations and between two times.
175      * @param x0 start length
176      * @param x1 end length
177      * @param t0 start time
178      * @param t1 end time
179      * @return list of trajectories
180      */
181     public final synchronized TrajectoryGroup<G> getTrajectoryGroup(final Length x0, final Length x1, final Duration t0,
182             final Duration t1)
183     {
184         TrajectoryGroup<G> out = new TrajectoryGroup<>(this.startTime.lt(t0) ? t0 : this.startTime, this.lane);
185         for (Trajectory<G> trajectory : this.trajectories)
186         {
187             out.addTrajectory(trajectory.subSet(x0, x1, t0, t1));
188         }
189         return out;
190     }
191 
192     /**
193      * Returns the lane.
194      * @return lane
195      */
196     public final LaneData<?> getLane()
197     {
198         return this.lane;
199     }
200 
201     @Override
202     public final int hashCode()
203     {
204         final int prime = 31;
205         int result = 1;
206         result = prime * result + this.lane.hashCode();
207         result = prime * result + this.endPosition.hashCode();
208         result = prime * result + this.startPosition.hashCode();
209         result = prime * result + this.startTime.hashCode();
210         result = prime * result + this.trajectories.hashCode();
211         return result;
212     }
213 
214     @Override
215     public final boolean equals(final Object obj)
216     {
217         if (this == obj)
218         {
219             return true;
220         }
221         if (obj == null)
222         {
223             return false;
224         }
225         if (getClass() != obj.getClass())
226         {
227             return false;
228         }
229         TrajectoryGroup<?> other = (TrajectoryGroup<?>) obj;
230         if (!this.lane.equals(other.lane))
231         {
232             return false;
233         }
234         if (!this.endPosition.equals(other.endPosition))
235         {
236             return false;
237         }
238         if (!this.startPosition.equals(other.startPosition))
239         {
240             return false;
241         }
242         if (!this.startTime.equals(other.startTime))
243         {
244             return false;
245         }
246         if (!this.trajectories.equals(other.trajectories))
247         {
248             return false;
249         }
250         return true;
251     }
252 
253     @Override
254     public final String toString()
255     {
256         return "TrajectoryGroup [startTime=" + this.startTime + ", minLength=" + this.startPosition + ", maxLength="
257                 + this.endPosition + ", lane=" + this.lane + ", collected "
258                 + (this.trajectories == null ? "null" : this.trajectories.size()) + " trajectories]";
259     }
260 
261     @Override
262     public Iterator<Trajectory<G>> iterator()
263     {
264         return this.trajectories.iterator();
265     }
266 
267 }