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