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