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.value.vdouble.scalar.Duration;
14 import org.djunits.value.vdouble.scalar.Frequency;
15 import org.djunits.value.vdouble.scalar.Length;
16 import org.djutils.base.Identifiable;
17 import org.djutils.exceptions.Throw;
18 import org.djutils.immutablecollections.ImmutableIterator;
19 import org.opentrafficsim.kpi.interfaces.GtuData;
20 import org.opentrafficsim.kpi.interfaces.LaneData;
21 import org.opentrafficsim.kpi.interfaces.LinkData;
22 import org.opentrafficsim.kpi.sampling.filter.FilterDataSet;
23 import org.opentrafficsim.kpi.sampling.filter.FilterDataType;
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38 public final class Query<G extends GtuData, L extends LaneData<L>> implements Identifiable
39 {
40
41
42 private final String id;
43
44
45 private final Sampler<G, L> sampler;
46
47
48 private final String description;
49
50
51 private final FilterDataSet filterDataSet;
52
53
54 private final Frequency updateFrequency;
55
56
57 private final Duration interval;
58
59
60 private final List<SpaceTimeRegion<? extends L>> spaceTimeRegions = new ArrayList<>();
61
62
63
64
65
66
67
68
69
70 public Query(final Sampler<G, L> sampler, final String id, final String description, final FilterDataSet filterDataSet)
71 {
72 this(sampler, id, description, filterDataSet, null, null);
73 }
74
75
76
77
78
79
80
81
82
83
84
85 public Query(final Sampler<G, L> sampler, final String id, final String description, final FilterDataSet filterDataSet,
86 final Frequency updateFrequency, final Duration interval)
87 {
88 Throw.whenNull(sampler, "Sampling may not be null.");
89 Throw.whenNull(description, "Description may not be null.");
90 Throw.whenNull(filterDataSet, "Meta data may not be null.");
91 this.sampler = sampler;
92 this.filterDataSet = new FilterDataSet(filterDataSet);
93 this.id = id == null ? UUID.randomUUID().toString() : id;
94 this.description = description;
95 this.updateFrequency = updateFrequency;
96 this.interval = interval;
97 }
98
99 @Override
100 public String getId()
101 {
102 return this.id.toString();
103 }
104
105
106
107
108
109 public String getDescription()
110 {
111 return this.description;
112 }
113
114
115
116
117
118 public Frequency getUpdateFrequency()
119 {
120 return this.updateFrequency;
121 }
122
123
124
125
126
127 public Duration getInterval()
128 {
129 return this.interval;
130 }
131
132
133
134
135
136 public int filterSize()
137 {
138 return this.filterDataSet.size();
139 }
140
141
142
143
144
145 public Iterator<Entry<FilterDataType<?, ?>, Set<?>>> getFilterDataSetIterator()
146 {
147 return this.filterDataSet.getFilterDataSetIterator();
148 }
149
150
151
152
153
154
155
156
157
158 public void addSpaceTimeRegionLink(final LinkData<? extends L> link, final Length startPosition, final Length endPosition,
159 final Duration startTime, final Duration endTime)
160 {
161 Throw.whenNull(link, "Link may not be null.");
162 Throw.whenNull(startPosition, "Start position may not be null.");
163 Throw.whenNull(endPosition, "End position may not be null.");
164 Throw.whenNull(startTime, "Start time may not be null.");
165 Throw.whenNull(endTime, "End time may not be null.");
166 Throw.when(endPosition.lt(startPosition), IllegalArgumentException.class,
167 "End position should be greater than start position.");
168 Throw.when(endTime.lt(startTime), IllegalArgumentException.class, "End time should be greater than start time.");
169 double fStart = startPosition.si / link.getLength().si;
170 double fEnd = endPosition.si / link.getLength().si;
171 for (L lane : link.getLanes())
172 {
173 Length x0 = lane.getLength().times(fStart);
174 Length x1 = lane.getLength().times(fEnd);
175 addSpaceTimeRegion(lane, x0, x1, startTime, endTime);
176 }
177 }
178
179
180
181
182
183
184
185
186
187 public void addSpaceTimeRegion(final L lane, final Length startPosition, final Length endPosition, final Duration startTime,
188 final Duration endTime)
189 {
190 Throw.whenNull(lane, "Lane direction may not be null.");
191 Throw.whenNull(startPosition, "Start position may not be null.");
192 Throw.whenNull(endPosition, "End position may not be null.");
193 Throw.whenNull(startTime, "Start time may not be null.");
194 Throw.whenNull(endTime, "End time may not be null.");
195 Throw.when(endPosition.lt(startPosition), IllegalArgumentException.class,
196 "End position should be greater than start position.");
197 Throw.when(endTime.lt(startTime), IllegalArgumentException.class, "End time should be greater than start time.");
198 SpaceTimeRegion<L> spaceTimeRegion = new SpaceTimeRegion<>(lane, startPosition, endPosition, startTime, endTime);
199 this.sampler.registerSpaceTimeRegion(spaceTimeRegion);
200 this.spaceTimeRegions.add(spaceTimeRegion);
201 }
202
203
204
205
206
207 public int spaceTimeRegionSize()
208 {
209 return this.spaceTimeRegions.size();
210 }
211
212
213
214
215
216 public Iterator<SpaceTimeRegion<? extends L>> getSpaceTimeIterator()
217 {
218 return new ImmutableIterator<>(this.spaceTimeRegions.iterator());
219 }
220
221
222
223
224
225
226
227
228
229 public <T> List<TrajectoryGroup<G>> getTrajectoryGroups(final Duration endTime)
230 {
231 return getTrajectoryGroups(Duration.ZERO, endTime);
232 }
233
234
235
236
237
238
239
240
241
242
243 @SuppressWarnings("unchecked")
244 public <T> List<TrajectoryGroup<G>> getTrajectoryGroups(final Duration startTime, final Duration endTime)
245 {
246 Throw.whenNull(startTime, "Start t may not be null.");
247 Throw.whenNull(endTime, "End t may not be null.");
248
249 Map<String, TrajectoryAcceptList> trajectoryAcceptLists = new LinkedHashMap<>();
250 List<TrajectoryGroup<G>> trajectoryGroupList = new ArrayList<>();
251 for (SpaceTimeRegion<? extends L> spaceTimeRegion : this.spaceTimeRegions)
252 {
253 Duration start = startTime.gt(spaceTimeRegion.startTime()) ? startTime : spaceTimeRegion.startTime();
254 Duration end = endTime.lt(spaceTimeRegion.endTime()) ? endTime : spaceTimeRegion.endTime();
255 TrajectoryGroup<G> trajectoryGroup;
256 if (this.sampler.getSamplerData().getTrajectoryGroup(spaceTimeRegion.lane()).isEmpty())
257 {
258 trajectoryGroup = new TrajectoryGroup<>(start, spaceTimeRegion.lane());
259 }
260 else
261 {
262 trajectoryGroup = this.sampler.getSamplerData().getTrajectoryGroup(spaceTimeRegion.lane()).get()
263 .getTrajectoryGroup(spaceTimeRegion.startPosition(), spaceTimeRegion.endPosition(), start, end);
264 }
265 for (Trajectory<G> trajectory : trajectoryGroup.getTrajectories())
266 {
267 if (!trajectoryAcceptLists.containsKey(trajectory.getGtuId()))
268 {
269 trajectoryAcceptLists.put(trajectory.getGtuId(), new TrajectoryAcceptList());
270 }
271 trajectoryAcceptLists.get(trajectory.getGtuId()).addTrajectory(trajectory, trajectoryGroup);
272 }
273 trajectoryGroupList.add(trajectoryGroup);
274 }
275
276 Iterator<String> iterator = trajectoryAcceptLists.keySet().iterator();
277 while (iterator.hasNext())
278 {
279 String gtuId = iterator.next();
280 TrajectoryAcceptList trajectoryAcceptListCombined = trajectoryAcceptLists.get(gtuId);
281 trajectoryAcceptListCombined.acceptAll();
282 for (FilterDataType<?, ?> filterDataType : this.filterDataSet.getFilterDataTypes())
283 {
284
285 TrajectoryAcceptList trajectoryAcceptListCopy = copyTrajectoryAcceptList(trajectoryAcceptLists.get(gtuId));
286
287 ((FilterDataType<T, ?>) filterDataType).accept(trajectoryAcceptListCopy,
288 (Set<T>) new LinkedHashSet<>(this.filterDataSet.get(filterDataType)));
289
290 for (int i = 0; i < trajectoryAcceptListCopy.size(); i++)
291 {
292 Trajectory<?> trajectory = trajectoryAcceptListCopy.getTrajectory(i);
293 trajectoryAcceptListCombined.acceptTrajectory(trajectory,
294 trajectoryAcceptListCombined.isAccepted(trajectory)
295 && trajectoryAcceptListCopy.isAccepted(trajectory));
296 }
297 }
298 }
299
300 List<TrajectoryGroup<G>> out = new ArrayList<>();
301 for (TrajectoryGroup<G> full : trajectoryGroupList)
302 {
303 TrajectoryGroup<G> filtered = new TrajectoryGroup<>(full.getStartTime(), full.getLane());
304 for (Trajectory<G> trajectory : full.getTrajectories())
305 {
306 String gtuId = trajectory.getGtuId();
307 if (trajectory.size() > 0 && trajectoryAcceptLists.get(gtuId).isAccepted(trajectory))
308 {
309 filtered.addTrajectory(trajectory);
310 }
311 }
312 out.add(filtered);
313 }
314 return out;
315 }
316
317
318
319
320
321
322 private TrajectoryAcceptList copyTrajectoryAcceptList(final TrajectoryAcceptList trajectoryAcceptList)
323 {
324 TrajectoryAcceptList trajectoryAcceptListCopy = new TrajectoryAcceptList();
325 for (int i = 0; i < trajectoryAcceptList.size(); i++)
326 {
327 trajectoryAcceptListCopy.addTrajectory(trajectoryAcceptList.getTrajectory(i),
328 trajectoryAcceptList.getTrajectoryGroup(i));
329 }
330 return trajectoryAcceptListCopy;
331 }
332
333
334
335
336
337 public Sampler<G, L> getSampler()
338 {
339 return this.sampler;
340 }
341
342 @Override
343 public int hashCode()
344 {
345 final int prime = 31;
346 int result = 1;
347 result = prime * result + this.description.hashCode();
348 result = prime * result + ((this.interval == null) ? 0 : this.interval.hashCode());
349 result = prime * result + this.filterDataSet.hashCode();
350 result = prime * result + this.sampler.hashCode();
351 result = prime * result + this.spaceTimeRegions.hashCode();
352 result = prime * result + this.id.hashCode();
353 result = prime * result + ((this.updateFrequency == null) ? 0 : this.updateFrequency.hashCode());
354 return result;
355 }
356
357 @Override
358 public boolean equals(final Object obj)
359 {
360 if (this == obj)
361 {
362 return true;
363 }
364 if (obj == null)
365 {
366 return false;
367 }
368 if (getClass() != obj.getClass())
369 {
370 return false;
371 }
372 Query<?, ?> other = (Query<?, ?>) obj;
373 if (!this.description.equals(other.description))
374 {
375 return false;
376 }
377 if (this.interval == null)
378 {
379 if (other.interval != null)
380 {
381 return false;
382 }
383 }
384 else if (!this.interval.equals(other.interval))
385 {
386 return false;
387 }
388 if (!this.filterDataSet.equals(other.filterDataSet))
389 {
390 return false;
391 }
392 if (!this.sampler.equals(other.sampler))
393 {
394 return false;
395 }
396 if (!this.spaceTimeRegions.equals(other.spaceTimeRegions))
397 {
398 return false;
399 }
400 if (!this.id.equals(other.id))
401 {
402 return false;
403 }
404 if (this.updateFrequency == null)
405 {
406 if (other.updateFrequency != null)
407 {
408 return false;
409 }
410 }
411 else if (!this.updateFrequency.equals(other.updateFrequency))
412 {
413 return false;
414 }
415 return true;
416 }
417
418 @Override
419 public String toString()
420 {
421 return "Query (" + this.description + ")";
422 }
423
424 }