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