1 package org.opentrafficsim.kpi.sampling;
2
3 import java.util.ArrayList;
4 import java.util.HashMap;
5 import java.util.HashSet;
6 import java.util.Iterator;
7 import java.util.List;
8 import java.util.Map;
9 import java.util.Map.Entry;
10 import java.util.Set;
11 import java.util.UUID;
12
13 import org.djunits.unit.LengthUnit;
14 import org.djunits.value.vdouble.scalar.Duration;
15 import org.djunits.value.vdouble.scalar.Frequency;
16 import org.djunits.value.vdouble.scalar.Length;
17 import org.djunits.value.vdouble.scalar.Time;
18 import org.opentrafficsim.kpi.interfaces.LaneDataInterface;
19 import org.opentrafficsim.kpi.interfaces.LinkDataInterface;
20 import org.opentrafficsim.kpi.sampling.meta.MetaDataSet;
21 import org.opentrafficsim.kpi.sampling.meta.MetaDataType;
22
23 import nl.tudelft.simulation.immutablecollections.ImmutableIterator;
24 import nl.tudelft.simulation.language.Throw;
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39 public final class Query
40 {
41
42 private final String id;
43
44
45 private final Sampler sampler;
46
47
48 private final String description;
49
50
51 private final MetaDataSet metaDataSet;
52
53
54 private final Frequency updateFrequency;
55
56
57 private final Duration interval;
58
59
60 private final List<SpaceTimeRegion> spaceTimeRegions = new ArrayList<>();
61
62
63
64
65
66
67
68
69 public Query(final Sampler sampler, final String id, final String description, final MetaDataSet metaDataSet)
70 {
71 this(sampler, description, metaDataSet, null, null);
72 }
73
74
75
76
77
78
79
80
81
82 public Query(final Sampler sampler, final String id, final String description, final MetaDataSet metaDataSet,
83 final Duration interval)
84 {
85 this(sampler, id, description, metaDataSet, null, interval);
86 }
87
88
89
90
91
92
93
94
95
96 public Query(final Sampler sampler, final String id, final String description, final MetaDataSet metaDataSet,
97 final Frequency updateFrequency)
98 {
99 this(sampler, id, description, metaDataSet, updateFrequency, null);
100 }
101
102
103
104
105
106
107
108
109
110
111 public Query(final Sampler sampler, final String id, final String description, final MetaDataSet metaDataSet,
112 final Frequency updateFrequency, final Duration interval)
113 {
114 Throw.whenNull(sampler, "Sampling may not be null.");
115 Throw.whenNull(description, "Description may not be null.");
116 Throw.whenNull(metaDataSet, "Meta data may not be null.");
117 this.sampler = sampler;
118 this.metaDataSet = new MetaDataSet(metaDataSet);
119 this.id = id == null ? UUID.randomUUID().toString() : id;
120 this.description = description;
121 this.updateFrequency = updateFrequency;
122 this.interval = interval;
123 sampler.registerMetaDataTypes(metaDataSet.getMetaDataTypes());
124 }
125
126
127
128
129
130
131
132 public Query(final Sampler sampler, final String description, final MetaDataSet metaDataSet)
133 {
134 this(sampler, null, description, metaDataSet, null, null);
135 }
136
137
138
139
140
141
142
143
144 public Query(final Sampler sampler, final String description, final MetaDataSet metaDataSet, final Duration interval)
145 {
146 this(sampler, null, description, metaDataSet, null, interval);
147 }
148
149
150
151
152
153
154
155
156 public Query(final Sampler sampler, final String description, final MetaDataSet metaDataSet,
157 final Frequency updateFrequency)
158 {
159 this(sampler, null, description, metaDataSet, updateFrequency, null);
160 }
161
162
163
164
165
166
167
168
169
170 public Query(final Sampler sampler, final String description, final MetaDataSet metaDataSet,
171 final Frequency updateFrequency, final Duration interval)
172 {
173 this(sampler, null, description, metaDataSet, updateFrequency, interval);
174 }
175
176
177
178
179
180 public String getId()
181 {
182 return this.id.toString();
183 }
184
185
186
187
188 public String getDescription()
189 {
190 return this.description;
191 }
192
193
194
195
196 public Frequency getUpdateFrequency()
197 {
198 return this.updateFrequency;
199 }
200
201
202
203
204 public Duration getInterval()
205 {
206 return this.interval;
207 }
208
209
210
211
212 public int metaDataSize()
213 {
214 return this.metaDataSet.size();
215 }
216
217
218
219
220 public Iterator<Entry<MetaDataType<?>, Set<?>>> getMetaDataSetIterator()
221 {
222 return this.metaDataSet.getMetaDataSetIterator();
223 }
224
225
226
227
228
229
230
231
232
233
234 public void addSpaceTimeRegionLink(final LinkDataInterface link, final KpiGtuDirectionality direction,
235 final Length startPosition, final Length endPosition, final Time startTime, final Time endTime)
236 {
237 Throw.whenNull(link, "Link may not be null.");
238 Throw.whenNull(direction, "Direction may not be null.");
239 Throw.whenNull(startPosition, "Start position may not be null.");
240 Throw.whenNull(endPosition, "End position may not be null.");
241 Throw.whenNull(startTime, "Start time may not be null.");
242 Throw.whenNull(endTime, "End time may not be null.");
243 Throw.when(endPosition.lt(startPosition), IllegalArgumentException.class,
244 "End position should be greater than start position.");
245 Throw.when(endTime.lt(startTime), IllegalArgumentException.class, "End time should be greater than start time.");
246 for (LaneDataInterface lane : link.getLaneDatas())
247 {
248 Length x0 = new Length(lane.getLength().si * startPosition.si / link.getLength().si, LengthUnit.SI);
249 Length x1 = new Length(lane.getLength().si * endPosition.si / link.getLength().si, LengthUnit.SI);
250 addSpaceTimeRegion(new KpiLaneDirection(lane, direction), x0, x1, startTime, endTime);
251 }
252 }
253
254
255
256
257
258
259
260
261
262 public void addSpaceTimeRegion(final KpiLaneDirection laneDirection, final Length startPosition, final Length endPosition,
263 final Time startTime, final Time endTime)
264 {
265 Throw.whenNull(laneDirection, "Lane direction may not be null.");
266 Throw.whenNull(startPosition, "Start position may not be null.");
267 Throw.whenNull(endPosition, "End position may not be null.");
268 Throw.whenNull(startTime, "Start time may not be null.");
269 Throw.whenNull(endTime, "End time may not be null.");
270 Throw.when(endPosition.lt(startPosition), IllegalArgumentException.class,
271 "End position should be greater than start position.");
272 Throw.when(endTime.lt(startTime), IllegalArgumentException.class, "End time should be greater than start time.");
273 SpaceTimeRegion spaceTimeRegion = new SpaceTimeRegion(laneDirection, startPosition, endPosition, startTime, endTime);
274 this.sampler.registerSpaceTimeRegion(spaceTimeRegion);
275 this.spaceTimeRegions.add(spaceTimeRegion);
276 }
277
278
279
280
281 public int spaceTimeRegionSize()
282 {
283 return this.spaceTimeRegions.size();
284 }
285
286
287
288
289 public Iterator<SpaceTimeRegion> getSpaceTimeIterator()
290 {
291 return new ImmutableIterator<>(this.spaceTimeRegions.iterator());
292 }
293
294
295
296
297
298
299
300
301
302 public <T> List<TrajectoryGroup> getTrajectoryGroups(final Time endTime)
303 {
304 return getTrajectoryGroups(Time.ZERO, endTime);
305 }
306
307
308
309
310
311
312
313
314
315
316 @SuppressWarnings("unchecked")
317 public <T> List<TrajectoryGroup> getTrajectoryGroups(final Time startTime, final Time endTime)
318 {
319 Throw.whenNull(startTime, "Start t may not be null.");
320 Throw.whenNull(endTime, "End t may not be null.");
321
322 Map<String, TrajectoryAcceptList> trajectoryAcceptLists = new HashMap<>();
323 List<TrajectoryGroup> trajectoryGroupList = new ArrayList<>();
324 for (SpaceTimeRegion spaceTimeRegion : this.spaceTimeRegions)
325 {
326 Time start = startTime.gt(spaceTimeRegion.getStartTime()) ? startTime : spaceTimeRegion.getStartTime();
327 Time end = endTime.lt(spaceTimeRegion.getEndTime()) ? endTime : spaceTimeRegion.getEndTime();
328 TrajectoryGroup trajectoryGroup;
329 if (this.sampler.getTrajectoryGroup(spaceTimeRegion.getLaneDirection()) == null)
330 {
331 trajectoryGroup = new TrajectoryGroup(start, spaceTimeRegion.getLaneDirection());
332 }
333 else
334 {
335 trajectoryGroup = this.sampler.getTrajectoryGroup(spaceTimeRegion.getLaneDirection())
336 .getTrajectoryGroup(spaceTimeRegion.getStartPosition(), spaceTimeRegion.getEndPosition(), start, end);
337 }
338 for (Trajectory trajectory : trajectoryGroup.getTrajectories())
339 {
340 if (!trajectoryAcceptLists.containsKey(trajectory.getGtuId()))
341 {
342 trajectoryAcceptLists.put(trajectory.getGtuId(), new TrajectoryAcceptList());
343 }
344 trajectoryAcceptLists.get(trajectory.getGtuId()).addTrajectory(trajectory, trajectoryGroup);
345 }
346 trajectoryGroupList.add(trajectoryGroup);
347 }
348
349 Iterator<String> iterator = trajectoryAcceptLists.keySet().iterator();
350 while (iterator.hasNext())
351 {
352 String gtuId = iterator.next();
353 TrajectoryAcceptList trajectoryAcceptListCombined = trajectoryAcceptLists.get(gtuId);
354 trajectoryAcceptListCombined.acceptAll();
355 for (MetaDataType<?> metaDataType : this.metaDataSet.getMetaDataTypes())
356 {
357
358 TrajectoryAcceptList trajectoryAcceptList = trajectoryAcceptLists.get(gtuId);
359 TrajectoryAcceptList trajectoryAcceptListCopy = new TrajectoryAcceptList();
360 for (int i = 0; i < trajectoryAcceptList.size(); i++)
361 {
362 trajectoryAcceptListCopy.addTrajectory(trajectoryAcceptList.getTrajectory(i),
363 trajectoryAcceptList.getTrajectoryGroup(i));
364 }
365
366 ((MetaDataType<T>) metaDataType).accept(trajectoryAcceptListCopy,
367 (Set<T>) new HashSet<>(this.metaDataSet.get(metaDataType)));
368
369 for (int i = 0; i < trajectoryAcceptListCopy.size(); i++)
370 {
371 Trajectory trajectory = trajectoryAcceptListCopy.getTrajectory(i);
372 trajectoryAcceptListCombined.acceptTrajectory(trajectory,
373 trajectoryAcceptListCombined.isAccepted(trajectory)
374 && trajectoryAcceptListCopy.isAccepted(trajectory));
375 }
376 }
377 }
378
379 List<TrajectoryGroup> out = new ArrayList<>();
380 for (TrajectoryGroup full : trajectoryGroupList)
381 {
382 TrajectoryGroup filtered = new TrajectoryGroup(full.getStartTime(), full.getLaneDirection());
383 for (Trajectory trajectory : full.getTrajectories())
384 {
385 String gtuId = trajectory.getGtuId();
386 if (trajectoryAcceptLists.get(gtuId).isAccepted(trajectory))
387 {
388 filtered.addTrajectory(trajectory);
389 }
390 }
391 out.add(filtered);
392 }
393 return out;
394 }
395
396
397
398
399 public Sampler getSampler()
400 {
401 return this.sampler;
402 }
403
404
405 @Override
406 public int hashCode()
407 {
408 final int prime = 31;
409 int result = 1;
410 result = prime * result + ((this.description == null) ? 0 : this.description.hashCode());
411 result = prime * result + ((this.interval == null) ? 0 : this.interval.hashCode());
412 result = prime * result + ((this.metaDataSet == null) ? 0 : this.metaDataSet.hashCode());
413 result = prime * result + ((this.sampler == null) ? 0 : this.sampler.hashCode());
414 result = prime * result + ((this.spaceTimeRegions == null) ? 0 : this.spaceTimeRegions.hashCode());
415 result = prime * result + ((this.id == null) ? 0 : this.id.hashCode());
416 result = prime * result + ((this.updateFrequency == null) ? 0 : this.updateFrequency.hashCode());
417 return result;
418 }
419
420
421 @Override
422 public boolean equals(final Object obj)
423 {
424 if (this == obj)
425 {
426 return true;
427 }
428 if (obj == null)
429 {
430 return false;
431 }
432 if (getClass() != obj.getClass())
433 {
434 return false;
435 }
436 Query other = (Query) obj;
437 if (this.description == null)
438 {
439 if (other.description != null)
440 {
441 return false;
442 }
443 }
444 else if (!this.description.equals(other.description))
445 {
446 return false;
447 }
448 if (this.interval == null)
449 {
450 if (other.interval != null)
451 {
452 return false;
453 }
454 }
455 else if (!this.interval.equals(other.interval))
456 {
457 return false;
458 }
459 if (this.metaDataSet == null)
460 {
461 if (other.metaDataSet != null)
462 {
463 return false;
464 }
465 }
466 else if (!this.metaDataSet.equals(other.metaDataSet))
467 {
468 return false;
469 }
470 if (this.sampler == null)
471 {
472 if (other.sampler != null)
473 {
474 return false;
475 }
476 }
477 else if (!this.sampler.equals(other.sampler))
478 {
479 return false;
480 }
481 if (this.spaceTimeRegions == null)
482 {
483 if (other.spaceTimeRegions != null)
484 {
485 return false;
486 }
487 }
488 else if (!this.spaceTimeRegions.equals(other.spaceTimeRegions))
489 {
490 return false;
491 }
492 if (this.id == null)
493 {
494 if (other.id != null)
495 {
496 return false;
497 }
498 }
499 else if (!this.id.equals(other.id))
500 {
501 return false;
502 }
503 if (this.updateFrequency == null)
504 {
505 if (other.updateFrequency != null)
506 {
507 return false;
508 }
509 }
510 else if (!this.updateFrequency.equals(other.updateFrequency))
511 {
512 return false;
513 }
514 return true;
515 }
516
517
518 @Override
519 public String toString()
520 {
521 return "Query (" + this.description + ")";
522 }
523
524 }