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