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