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