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