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