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