1 package org.opentrafficsim.kpi.sampling;
2
3 import java.util.HashMap;
4 import java.util.HashSet;
5 import java.util.LinkedHashMap;
6 import java.util.LinkedHashSet;
7 import java.util.Map;
8 import java.util.Set;
9
10 import org.djunits.value.vdouble.scalar.Acceleration;
11 import org.djunits.value.vdouble.scalar.Length;
12 import org.djunits.value.vdouble.scalar.Speed;
13 import org.djunits.value.vdouble.scalar.Time;
14 import org.opentrafficsim.kpi.interfaces.GtuDataInterface;
15 import org.opentrafficsim.kpi.sampling.data.ExtendedDataType;
16 import org.opentrafficsim.kpi.sampling.meta.MetaData;
17 import org.opentrafficsim.kpi.sampling.meta.MetaDataType;
18
19 import nl.tudelft.simulation.language.Throw;
20
21
22
23
24
25
26
27
28
29
30
31
32 public abstract class Sampler
33 {
34
35
36 private final Map<KpiLaneDirection, TrajectoryGroup> trajectories = new LinkedHashMap<>();
37
38
39 private final Map<KpiLaneDirection, Time> endTimes = new LinkedHashMap<>();
40
41
42 private final Map<String, Map<KpiLaneDirection, Trajectory>> trajectoryPerGtu = new LinkedHashMap<>();
43
44
45 private final Set<ExtendedDataType<?>> extendedDataTypes = new LinkedHashSet<>();
46
47
48 private Set<MetaDataType<?>> registeredMetaDataTypes = new LinkedHashSet<>();
49
50
51
52
53
54 public final void registerSpaceTimeRegion(final SpaceTimeRegion spaceTimeRegion)
55 {
56 Throw.whenNull(spaceTimeRegion, "SpaceTimeRegion may not be null.");
57 Time firstPossibleDataTime;
58 if (this.trajectories.containsKey(spaceTimeRegion.getLaneDirection()))
59 {
60 firstPossibleDataTime = this.trajectories.get(spaceTimeRegion.getLaneDirection()).getStartTime();
61 }
62 else
63 {
64 firstPossibleDataTime = now();
65 }
66 Throw.when(spaceTimeRegion.getStartTime().lt(firstPossibleDataTime), IllegalStateException.class,
67 "Space time region with start time %s is defined while data is available from %s onwards.",
68 spaceTimeRegion.getStartTime(), firstPossibleDataTime);
69 if (this.trajectories.containsKey(spaceTimeRegion.getLaneDirection()))
70 {
71 this.endTimes.put(spaceTimeRegion.getLaneDirection(),
72 Time.max(this.endTimes.get(spaceTimeRegion.getLaneDirection()), spaceTimeRegion.getEndTime()));
73 }
74 else
75 {
76 this.endTimes.put(spaceTimeRegion.getLaneDirection(), spaceTimeRegion.getEndTime());
77 scheduleStartRecording(spaceTimeRegion.getStartTime(), spaceTimeRegion.getLaneDirection());
78 }
79 scheduleStopRecording(this.endTimes.get(spaceTimeRegion.getLaneDirection()), spaceTimeRegion.getLaneDirection());
80 }
81
82
83
84
85
86 public abstract Time now();
87
88
89
90
91
92
93 public abstract void scheduleStartRecording(final Time time, final KpiLaneDirection kpiLaneDirection);
94
95
96
97
98
99
100 public abstract void scheduleStopRecording(final Time time, final KpiLaneDirection kpiLaneDirection);
101
102
103
104
105
106 public final void registerMetaDataTypes(final Set<MetaDataType<?>> metaDataTypes)
107 {
108 Throw.whenNull(metaDataTypes, "MetaDataTypes may not be null.");
109 this.registeredMetaDataTypes.addAll(metaDataTypes);
110 }
111
112
113
114
115
116 public final void registerExtendedDataType(final ExtendedDataType<?> extendedDataType)
117 {
118 Throw.whenNull(extendedDataType, "ExtendedDataType may not be null.");
119 this.extendedDataTypes.add(extendedDataType);
120 }
121
122
123
124
125
126
127 public boolean contains(final ExtendedDataType<?> extendedDataType)
128 {
129 return this.extendedDataTypes.contains(extendedDataType);
130 }
131
132
133
134
135
136 public final void startRecording(final KpiLaneDirection kpiLaneDirection)
137 {
138 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
139 if (this.trajectories.containsKey(kpiLaneDirection))
140 {
141 return;
142 }
143 this.trajectories.put(kpiLaneDirection, new TrajectoryGroup(now(), kpiLaneDirection));
144 initRecording(kpiLaneDirection);
145 }
146
147
148
149
150
151 public abstract void initRecording(final KpiLaneDirection kpiLaneDirection);
152
153
154
155
156
157 public final void stopRecording(final KpiLaneDirection kpiLaneDirection)
158 {
159 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
160 if (!this.trajectories.containsKey(kpiLaneDirection) || this.endTimes.get(kpiLaneDirection).gt(now()))
161 {
162 return;
163 }
164 finalizeRecording(kpiLaneDirection);
165 }
166
167
168
169
170
171 public abstract void finalizeRecording(final KpiLaneDirection kpiLaneDirection);
172
173
174
175
176
177
178
179
180
181
182 public final void processGtuAddEvent(final KpiLaneDirection kpiLaneDirection, final Length position,
183 final Speed speed, final Acceleration acceleration, final Time time, final GtuDataInterface gtu)
184 {
185 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
186 Throw.whenNull(position, "Position may not be null.");
187 Throw.whenNull(speed, "Speed may not be null.");
188 Throw.whenNull(acceleration, "Acceleration may not be null.");
189 Throw.whenNull(time, "Time may not be null.");
190 Throw.whenNull(gtu, "GtuDataInterface may not be null.");
191 if (kpiLaneDirection.getLaneData().getLength().lt(position))
192 {
193
194 return;
195 }
196 String gtuId = gtu.getId();
197 Trajectory trajectory = new Trajectory(gtu, makeMetaData(gtu), this.extendedDataTypes, kpiLaneDirection);
198 if (!this.trajectoryPerGtu.containsKey(gtuId))
199 {
200 Map<KpiLaneDirection, Trajectory> map = new LinkedHashMap<>();
201 this.trajectoryPerGtu.put(gtuId, map);
202 }
203 this.trajectoryPerGtu.get(gtuId).put(kpiLaneDirection, trajectory);
204 this.trajectories.get(kpiLaneDirection).addTrajectory(trajectory);
205 processGtuMoveEvent(kpiLaneDirection, position, speed, acceleration, time, gtu);
206 }
207
208
209
210
211
212
213
214
215
216
217
218 public final void processGtuMoveEvent(final KpiLaneDirection kpiLaneDirection, final Length position,
219 final Speed speed, final Acceleration acceleration, final Time time, final GtuDataInterface gtu)
220 {
221 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
222 Throw.whenNull(position, "Position may not be null.");
223 Throw.whenNull(speed, "Speed may not be null.");
224 Throw.whenNull(acceleration, "Acceleration may not be null.");
225 Throw.whenNull(time, "Time may not be null.");
226 Throw.whenNull(gtu, "GtuDataInterface may not be null.");
227 String gtuId = gtu.getId();
228 if (this.trajectoryPerGtu.containsKey(gtuId) && this.trajectoryPerGtu.get(gtuId).containsKey(kpiLaneDirection))
229 {
230 this.trajectoryPerGtu.get(gtuId).get(kpiLaneDirection).add(position, speed, acceleration, time, gtu);
231 }
232 }
233
234
235
236
237
238
239
240
241
242
243 public final void processGtuRemoveEvent(final KpiLaneDirection kpiLaneDirection, final Length position,
244 final Speed speed, final Acceleration acceleration, final Time time, final GtuDataInterface gtu)
245 {
246 processGtuMoveEvent(kpiLaneDirection, position, speed, acceleration, time, gtu);
247 processGtuRemoveEvent(kpiLaneDirection, gtu);
248 }
249
250
251
252
253
254
255 public final void processGtuRemoveEvent(final KpiLaneDirection kpiLaneDirection, final GtuDataInterface gtu)
256 {
257 Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
258 Throw.whenNull(gtu, "GtuDataInterface may not be null.");
259 String gtuId = gtu.getId();
260 if (this.trajectoryPerGtu.containsKey(gtuId))
261 {
262 this.trajectoryPerGtu.get(gtuId).remove(kpiLaneDirection);
263 if (this.trajectoryPerGtu.get(gtuId).isEmpty())
264 {
265 this.trajectoryPerGtu.remove(gtuId);
266 }
267 }
268 }
269
270
271
272
273
274
275 @SuppressWarnings("unchecked")
276 private <T> MetaData makeMetaData(final GtuDataInterface gtu)
277 {
278 MetaData metaData = new MetaData();
279 for (MetaDataType<?> metaDataType : this.registeredMetaDataTypes)
280 {
281 T value = (T) metaDataType.getValue(gtu);
282 if (value != null)
283 {
284 metaData.put((MetaDataType<T>) metaDataType, value);
285 }
286 }
287 return metaData;
288 }
289
290
291
292
293
294
295 public final boolean contains(final KpiLaneDirection kpiLaneDirection)
296 {
297 return this.trajectories.containsKey(kpiLaneDirection);
298 }
299
300
301
302
303
304
305 public final TrajectoryGroup getTrajectoryGroup(final KpiLaneDirection kpiLaneDirection)
306 {
307 return this.trajectories.get(kpiLaneDirection);
308 }
309
310
311 @Override
312 public int hashCode()
313 {
314 final int prime = 31;
315 int result = 1;
316 result = prime * result + ((this.endTimes == null) ? 0 : this.endTimes.hashCode());
317 result = prime * result + ((this.extendedDataTypes == null) ? 0 : this.extendedDataTypes.hashCode());
318 result = prime * result + ((this.registeredMetaDataTypes == null) ? 0 : this.registeredMetaDataTypes.hashCode());
319 result = prime * result + ((this.trajectories == null) ? 0 : this.trajectories.hashCode());
320 result = prime * result + ((this.trajectoryPerGtu == null) ? 0 : this.trajectoryPerGtu.hashCode());
321 return result;
322 }
323
324
325 @Override
326 public boolean equals(final Object obj)
327 {
328 if (this == obj)
329 {
330 return true;
331 }
332 if (obj == null)
333 {
334 return false;
335 }
336 if (getClass() != obj.getClass())
337 {
338 return false;
339 }
340 Sampler other = (Sampler) obj;
341 if (this.endTimes == null)
342 {
343 if (other.endTimes != null)
344 {
345 return false;
346 }
347 }
348 else if (!this.endTimes.equals(other.endTimes))
349 {
350 return false;
351 }
352 if (this.extendedDataTypes == null)
353 {
354 if (other.extendedDataTypes != null)
355 {
356 return false;
357 }
358 }
359 else if (!this.extendedDataTypes.equals(other.extendedDataTypes))
360 {
361 return false;
362 }
363 if (this.registeredMetaDataTypes == null)
364 {
365 if (other.registeredMetaDataTypes != null)
366 {
367 return false;
368 }
369 }
370 else if (!this.registeredMetaDataTypes.equals(other.registeredMetaDataTypes))
371 {
372 return false;
373 }
374 if (this.trajectories == null)
375 {
376 if (other.trajectories != null)
377 {
378 return false;
379 }
380 }
381 else if (!this.trajectories.equals(other.trajectories))
382 {
383 return false;
384 }
385 if (this.trajectoryPerGtu == null)
386 {
387 if (other.trajectoryPerGtu != null)
388 {
389 return false;
390 }
391 }
392 else if (!this.trajectoryPerGtu.equals(other.trajectoryPerGtu))
393 {
394 return false;
395 }
396 return true;
397 }
398
399 }