View Javadoc
1   package org.opentrafficsim.kpi.sampling;
2   
3   import java.io.BufferedWriter;
4   import java.io.IOException;
5   import java.util.ArrayList;
6   import java.util.LinkedHashMap;
7   import java.util.LinkedHashSet;
8   import java.util.List;
9   import java.util.Map;
10  import java.util.Set;
11  
12  import org.djunits.value.vdouble.scalar.Acceleration;
13  import org.djunits.value.vdouble.scalar.Length;
14  import org.djunits.value.vdouble.scalar.Speed;
15  import org.djunits.value.vdouble.scalar.Time;
16  import org.djutils.exceptions.Throw;
17  import org.opentrafficsim.base.CompressedFileWriter;
18  import org.opentrafficsim.kpi.interfaces.GtuDataInterface;
19  import org.opentrafficsim.kpi.sampling.data.ExtendedDataType;
20  import org.opentrafficsim.kpi.sampling.meta.MetaData;
21  import org.opentrafficsim.kpi.sampling.meta.MetaDataSet;
22  import org.opentrafficsim.kpi.sampling.meta.MetaDataType;
23  
24  /**
25   * Sampler is the highest level organizer for sampling.
26   * <p>
27   * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved. <br>
28   * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
29   * <p>
30   * @version $Revision$, $LastChangedDate$, by $Author$, initial version Sep 22, 2016 <br>
31   * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
32   * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
33   * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
34   * @param <G> gtu data type
35   */
36  public abstract class Sampler<G extends GtuDataInterface>
37  {
38  
39      /** Map with all sampling data. */
40      private final Map<KpiLaneDirection, TrajectoryGroup<G>> trajectories = new LinkedHashMap<>();
41  
42      /** End times of active samplings. */
43      private final Map<KpiLaneDirection, Time> endTimes = new LinkedHashMap<>();
44  
45      /** Registration of current trajectories of each GTU per lane. */
46      private final Map<String, Map<KpiLaneDirection, Trajectory<G>>> trajectoryPerGtu = new LinkedHashMap<>();
47  
48      /** Registration of included extended data types. */
49      private final Set<ExtendedDataType<?, ?, ?, G>> extendedDataTypes = new LinkedHashSet<>();
50  
51      /** Set of registered meta data types. */
52      private Set<MetaDataType<?>> registeredMetaDataTypes = new LinkedHashSet<>();
53  
54      /** Space time regions. */
55      private Set<SpaceTimeRegion> spaceTimeRegions = new LinkedHashSet<>();
56  
57      /**
58       * @param spaceTimeRegion SpaceTimeRegion; space-time region
59       * @throws IllegalStateException if data is not available from the requested start time
60       */
61      public final void registerSpaceTimeRegion(final SpaceTimeRegion spaceTimeRegion)
62      {
63          Throw.whenNull(spaceTimeRegion, "SpaceTimeRegion may not be null.");
64          Time firstPossibleDataTime;
65          if (this.trajectories.containsKey(spaceTimeRegion.getLaneDirection()))
66          {
67              firstPossibleDataTime = this.trajectories.get(spaceTimeRegion.getLaneDirection()).getStartTime();
68          }
69          else
70          {
71              firstPossibleDataTime = now();
72          }
73          Throw.when(spaceTimeRegion.getStartTime().lt(firstPossibleDataTime), IllegalStateException.class,
74                  "Space time region with start time %s is defined while data is available from %s onwards.",
75                  spaceTimeRegion.getStartTime(), firstPossibleDataTime);
76          if (this.trajectories.containsKey(spaceTimeRegion.getLaneDirection()))
77          {
78              this.endTimes.put(spaceTimeRegion.getLaneDirection(),
79                      Time.max(this.endTimes.get(spaceTimeRegion.getLaneDirection()), spaceTimeRegion.getEndTime()));
80          }
81          else
82          {
83              this.endTimes.put(spaceTimeRegion.getLaneDirection(), spaceTimeRegion.getEndTime());
84              scheduleStartRecording(spaceTimeRegion.getStartTime(), spaceTimeRegion.getLaneDirection());
85          }
86          scheduleStopRecording(this.endTimes.get(spaceTimeRegion.getLaneDirection()), spaceTimeRegion.getLaneDirection());
87          this.spaceTimeRegions.add(spaceTimeRegion);
88      }
89  
90      /**
91       * Returns the current simulation time.
92       * @return current simulation time
93       */
94      public abstract Time now();
95  
96      /**
97       * Schedules the start of recording for a given lane-direction.
98       * @param time Time; time to start recording
99       * @param kpiLaneDirection KpiLaneDirection; lane-direction to start recording
100      */
101     public abstract void scheduleStartRecording(final Time time, final KpiLaneDirection kpiLaneDirection);
102 
103     /**
104      * Schedules the stop of recording for a given lane-direction.
105      * @param time Time; time to stop recording
106      * @param kpiLaneDirection KpiLaneDirection; lane-direction to stop recording
107      */
108     public abstract void scheduleStopRecording(final Time time, final KpiLaneDirection kpiLaneDirection);
109 
110     /**
111      * Registers meta data types that will be stored with the trajectories.
112      * @param metaDataTypes Set&lt;MetaDataType&lt;?&gt;&gt;; meta data types to register
113      */
114     public final void registerMetaDataTypes(final Set<MetaDataType<?>> metaDataTypes)
115     {
116         Throw.whenNull(metaDataTypes, "MetaDataTypes may not be null.");
117         this.registeredMetaDataTypes.addAll(metaDataTypes);
118     }
119 
120     /**
121      * Registers extended data type that will be stored with the trajectories.
122      * @param extendedDataType ExtendedDataType&lt;?,?,?,G&gt;; extended data type to register
123      */
124     public final void registerExtendedDataType(final ExtendedDataType<?, ?, ?, G> extendedDataType)
125     {
126         Throw.whenNull(extendedDataType, "ExtendedDataType may not be null.");
127         this.extendedDataTypes.add(extendedDataType);
128     }
129 
130     /**
131      * Whether this sampler has the given extended data type registered to it.
132      * @param extendedDataType ExtendedDataType&lt;?,?,?,?&gt;; extended data type
133      * @return whether this sampler has the given extended data type registered to it
134      */
135     public boolean contains(final ExtendedDataType<?, ?, ?, ?> extendedDataType)
136     {
137         return this.extendedDataTypes.contains(extendedDataType);
138     }
139 
140     /**
141      * Start recording at the given time (which should be the current time) on the given lane direction.
142      * @param kpiLaneDirection KpiLaneDirection; lane direction
143      */
144     public final void startRecording(final KpiLaneDirection kpiLaneDirection)
145     {
146         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
147         if (this.trajectories.containsKey(kpiLaneDirection))
148         {
149             return;
150         }
151         this.trajectories.put(kpiLaneDirection, new TrajectoryGroup<>(now(), kpiLaneDirection));
152         initRecording(kpiLaneDirection);
153     }
154 
155     /**
156      * Adds listeners to start recording.
157      * @param kpiLaneDirection KpiLaneDirection; lane direction to initialize recording for
158      */
159     public abstract void initRecording(final KpiLaneDirection kpiLaneDirection);
160 
161     /**
162      * Stop recording at given lane direction.
163      * @param kpiLaneDirection KpiLaneDirection; lane direction
164      */
165     public final void stopRecording(final KpiLaneDirection kpiLaneDirection)
166     {
167         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
168         if (!this.trajectories.containsKey(kpiLaneDirection) || this.endTimes.get(kpiLaneDirection).gt(now()))
169         {
170             return;
171         }
172         finalizeRecording(kpiLaneDirection);
173     }
174 
175     /**
176      * Remove listeners to stop recording.
177      * @param kpiLaneDirection KpiLaneDirection; lane direction to finalize recording for
178      */
179     public abstract void finalizeRecording(final KpiLaneDirection kpiLaneDirection);
180 
181     /**
182      * Creates a trajectory with the current snapshot of a GTU.
183      * @param kpiLaneDirection KpiLaneDirection; lane direction the gtu is at
184      * @param position Length; position of the gtu on the lane
185      * @param speed Speed; speed of the gtu
186      * @param acceleration Acceleration; acceleration of the gtu
187      * @param time Time; current time
188      * @param gtu G; gtu
189      */
190     public final void processGtuAddEvent(final KpiLaneDirection kpiLaneDirection, final Length position, final Speed speed,
191             final Acceleration acceleration, final Time time, final G gtu)
192     {
193         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
194         Throw.whenNull(position, "Position may not be null.");
195         Throw.whenNull(speed, "Speed may not be null.");
196         Throw.whenNull(acceleration, "Acceleration may not be null.");
197         Throw.whenNull(time, "Time may not be null.");
198         Throw.whenNull(gtu, "GtuDataInterface may not be null.");
199         if (kpiLaneDirection.getLaneData().getLength().lt(position))
200         {
201             // ignore event if beyond lane length (may happen during lane change)
202             return;
203         }
204         String gtuId = gtu.getId();
205         Trajectory<G> trajectory = new Trajectory<>(gtu, makeMetaData(gtu), this.extendedDataTypes, kpiLaneDirection);
206         if (!this.trajectoryPerGtu.containsKey(gtuId))
207         {
208             Map<KpiLaneDirection, Trajectory<G>> map = new LinkedHashMap<>();
209             this.trajectoryPerGtu.put(gtuId, map);
210         }
211         this.trajectoryPerGtu.get(gtuId).put(kpiLaneDirection, trajectory);
212         this.trajectories.get(kpiLaneDirection).addTrajectory(trajectory);
213         processGtuMoveEvent(kpiLaneDirection, position, speed, acceleration, time, gtu);
214     }
215 
216     /**
217      * Adds a new snapshot of a GTU to its recording trajectory, if recorded. This method may be invoked on GTU that are not
218      * being recorded; the event will then be ignored.
219      * @param kpiLaneDirection KpiLaneDirection; lane direction the gtu is at
220      * @param position Length; position of the gtu on the lane
221      * @param speed Speed; speed of the gtu
222      * @param acceleration Acceleration; acceleration of the gtu
223      * @param time Time; current time
224      * @param gtu G; gtu
225      */
226     public final void processGtuMoveEvent(final KpiLaneDirection kpiLaneDirection, final Length position, final Speed speed,
227             final Acceleration acceleration, final Time time, final G gtu)
228     {
229         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
230         Throw.whenNull(position, "Position may not be null.");
231         Throw.whenNull(speed, "Speed may not be null.");
232         Throw.whenNull(acceleration, "Acceleration may not be null.");
233         Throw.whenNull(time, "Time may not be null.");
234         Throw.whenNull(gtu, "GtuDataInterface may not be null.");
235         String gtuId = gtu.getId();
236         if (this.trajectoryPerGtu.containsKey(gtuId) && this.trajectoryPerGtu.get(gtuId).containsKey(kpiLaneDirection))
237         {
238             this.trajectoryPerGtu.get(gtuId).get(kpiLaneDirection).add(position, speed, acceleration, time, gtu);
239         }
240     }
241 
242     /**
243      * Finalizes a trajectory with the current snapshot of a GTU.
244      * @param kpiLaneDirection KpiLaneDirection; lane direction the gtu is at
245      * @param position Length; position of the gtu on the lane
246      * @param speed Speed; speed of the gtu
247      * @param acceleration Acceleration; acceleration of the gtu
248      * @param time Time; current time
249      * @param gtu G; gtu
250      */
251     public final void processGtuRemoveEvent(final KpiLaneDirection kpiLaneDirection, final Length position, final Speed speed,
252             final Acceleration acceleration, final Time time, final G gtu)
253     {
254         processGtuMoveEvent(kpiLaneDirection, position, speed, acceleration, time, gtu);
255         processGtuRemoveEvent(kpiLaneDirection, gtu);
256     }
257 
258     /**
259      * Finalizes a trajectory.
260      * @param kpiLaneDirection KpiLaneDirection; lane direction the gtu is at
261      * @param gtu G; gtu
262      */
263     public final void processGtuRemoveEvent(final KpiLaneDirection kpiLaneDirection, final G gtu)
264     {
265         Throw.whenNull(kpiLaneDirection, "KpiLaneDirection may not be null.");
266         Throw.whenNull(gtu, "GtuDataInterface may not be null.");
267         String gtuId = gtu.getId();
268         if (this.trajectoryPerGtu.containsKey(gtuId))
269         {
270             this.trajectoryPerGtu.get(gtuId).remove(kpiLaneDirection);
271             if (this.trajectoryPerGtu.get(gtuId).isEmpty())
272             {
273                 this.trajectoryPerGtu.remove(gtuId);
274             }
275         }
276     }
277 
278     /**
279      * @param gtu G; gtu to return meta data for
280      * @param <T> underlying type of a meta data type
281      * @return meta data for the given gtu
282      */
283     @SuppressWarnings("unchecked")
284     private <T> MetaData makeMetaData(final G gtu)
285     {
286         MetaDatameta/MetaData.html#MetaData">MetaData metaData = new MetaData();
287         for (MetaDataType<?> metaDataType : this.registeredMetaDataTypes)
288         {
289             T value = (T) metaDataType.getValue(gtu);
290             if (value != null)
291             {
292                 metaData.put((MetaDataType<T>) metaDataType, value);
293             }
294         }
295         return metaData;
296     }
297 
298     /**
299      * Returns whether there is data for the give lane direction.
300      * @param kpiLaneDirection KpiLaneDirection; lane direction
301      * @return whether there is data for the give lane direction
302      */
303     public final boolean contains(final KpiLaneDirection kpiLaneDirection)
304     {
305         return this.trajectories.containsKey(kpiLaneDirection);
306     }
307 
308     /**
309      * Returns the trajectory group of given lane direction.
310      * @param kpiLaneDirection KpiLaneDirection; lane direction
311      * @return trajectory group of given lane direction, {@code null} if none
312      */
313     public final TrajectoryGroup<G> getTrajectoryGroup(final KpiLaneDirection kpiLaneDirection)
314     {
315         return this.trajectories.get(kpiLaneDirection);
316     }
317 
318     /**
319      * Write the contents of the sampler in to a file. By default this is zipped and numeric data is formated %.3f.
320      * @param file String; file
321      */
322     public final void writeToFile(final String file)
323     {
324         writeToFile(file, "%.3f", CompressionMethod.ZIP);
325     }
326 
327     /**
328      * Write the contents of the sampler in to a file.
329      * @param file String; file
330      * @param format String; number format, as used in {@code String.format()}
331      * @param compression CompressionMethod; how to compress the data
332      */
333     public final void writeToFile(final String file, final String format, final CompressionMethod compression)
334     {
335         int counter = 0;
336         BufferedWriter bw = CompressedFileWriter.create(file, compression.equals(CompressionMethod.ZIP));
337         // create Query, as this class is designed to filter for space-time regions
338         Query<G> query = new Query<>(this, "", new MetaDataSet());
339         for (SpaceTimeRegion str : this.spaceTimeRegions)
340         {
341             query.addSpaceTimeRegion(str.getLaneDirection(), str.getStartPosition(), str.getEndPosition(), str.getStartTime(),
342                     str.getEndTime());
343         }
344         List<TrajectoryGroup<G>> groups = query.getTrajectoryGroups(Time.instantiateSI(Double.POSITIVE_INFINITY));
345         try
346         {
347             // gather all meta data types for the header line
348             List<MetaDataType<?>> allMetaDataTypes = new ArrayList<>();
349             for (TrajectoryGroup<G> group : groups)
350             {
351                 for (Trajectory<G> trajectory : group.getTrajectories())
352                 {
353                     for (MetaDataType<?> metaDataType : trajectory.getMetaDataTypes())
354                     {
355                         if (!allMetaDataTypes.contains(metaDataType))
356                         {
357                             allMetaDataTypes.add(metaDataType);
358                         }
359                     }
360                 }
361             }
362             // gather all extended data types for the header line
363             List<ExtendedDataType<?, ?, ?, ?>> allExtendedDataTypes = new ArrayList<>();
364             for (TrajectoryGroup<G> group : groups)
365             {
366                 for (Trajectory<?> trajectory : group.getTrajectories())
367                 {
368                     for (ExtendedDataType<?, ?, ?, ?> extendedDataType : trajectory.getExtendedDataTypes())
369                     {
370                         if (!allExtendedDataTypes.contains(extendedDataType))
371                         {
372                             allExtendedDataTypes.add(extendedDataType);
373                         }
374                     }
375                 }
376             }
377             // create header line
378             StringBuilder str = new StringBuilder();
379             str.append("traj#,linkId,laneId&dir,gtuId,t,x,v,a");
380             for (MetaDataType<?> metaDataType : allMetaDataTypes)
381             {
382                 str.append(",");
383                 str.append(metaDataType.getId());
384             }
385             for (ExtendedDataType<?, ?, ?, ?> extendedDataType : allExtendedDataTypes)
386             {
387                 str.append(",");
388                 str.append(extendedDataType.getId());
389             }
390             bw.write(str.toString());
391             bw.newLine();
392             for (TrajectoryGroup<G> group : groups)
393             {
394                 for (Trajectory<G> trajectory : group.getTrajectories())
395                 {
396                     counter++;
397                     float[] t = trajectory.getT();
398                     float[] x = trajectory.getX();
399                     float[] v = trajectory.getV();
400                     float[] a = trajectory.getA();
401                     Map<ExtendedDataType<?, ?, ?, ?>, Object> extendedData = new LinkedHashMap<>();
402                     for (ExtendedDataType<?, ?, ?, ?> extendedDataType : allExtendedDataTypes)
403                     {
404                         if (trajectory.contains(extendedDataType))
405                         {
406                             try
407                             {
408                                 extendedData.put(extendedDataType, trajectory.getExtendedData(extendedDataType));
409                             }
410                             catch (SamplingException exception)
411                             {
412                                 // should not occur, we obtain the extended data types from the trajectory
413                                 throw new RuntimeException("Error while loading extended data type.", exception);
414                             }
415                         }
416                     }
417                     for (int i = 0; i < t.length; i++)
418                     {
419                         str = new StringBuilder();
420                         str.append(counter);
421                         str.append(",");
422                         if (!compression.equals(CompressionMethod.OMIT_DUPLICATE_INFO) || i == 0)
423                         {
424                             str.append(group.getLaneDirection().getLaneData().getLinkData().getId());
425                             str.append(",");
426                             str.append(group.getLaneDirection().getLaneData().getId());
427                             str.append(group.getLaneDirection().getKpiDirection().isPlus() ? "+" : "-");
428                             str.append(",");
429                             str.append(trajectory.getGtuId());
430                             str.append(",");
431                         }
432                         else
433                         {
434                             // one trajectory is on the same lane and pertains to the same GTU, no need to repeat data
435                             str.append(",,,");
436                         }
437                         str.append(String.format(format, t[i]));
438                         str.append(",");
439                         str.append(String.format(format, x[i]));
440                         str.append(",");
441                         str.append(String.format(format, v[i]));
442                         str.append(",");
443                         str.append(String.format(format, a[i]));
444                         for (MetaDataType<?> metaDataType : allMetaDataTypes)
445                         {
446                             str.append(",");
447                             if (i == 0 && trajectory.contains(metaDataType))
448                             {
449                                 // no need to repeat meta data
450                                 str.append(metaDataType.formatValue(format, castValue(trajectory.getMetaData(metaDataType))));
451                             }
452                         }
453                         for (ExtendedDataType<?, ?, ?, ?> extendedDataType : allExtendedDataTypes)
454                         {
455                             str.append(",");
456                             if (trajectory.contains(extendedDataType))
457                             {
458                                 try
459                                 {
460                                     str.append(
461                                             extendedDataType.formatValue(format, castValue(extendedData, extendedDataType, i)));
462                                 }
463                                 catch (SamplingException exception)
464                                 {
465                                     // should not occur, we obtain the extended data types from the trajectory
466                                     throw new RuntimeException("Error while loading extended data type.", exception);
467                                 }
468                             }
469                         }
470                         bw.write(str.toString());
471                         bw.newLine();
472                     }
473                 }
474             }
475         }
476         catch (IOException exception)
477         {
478             throw new RuntimeException("Could not write to file.", exception);
479         }
480         // close file on fail
481         finally
482         {
483             try
484             {
485                 if (bw != null)
486                 {
487                     bw.close();
488                 }
489             }
490             catch (IOException ex)
491             {
492                 ex.printStackTrace();
493             }
494         }
495     }
496 
497     /**
498      * Cast value to type for meta data.
499      * @param value Object; value object to cast
500      * @return cast value
501      */
502     @SuppressWarnings("unchecked")
503     private <T> T castValue(final Object value)
504     {
505         return (T) value;
506     }
507 
508     /**
509      * Cast value to type for extended data.
510      * @param extendedData Map&lt;ExtendedDataType&lt;?,?,?,?&gt;,Object&gt;; extended data of trajectory in output form
511      * @param extendedDataType ExtendedDataType&lt;?,?,?,?&gt;; extended data type
512      * @param i int; index of value to return
513      * @return cast value
514      * @throws SamplingException when the found index is out of bounds
515      */
516     @SuppressWarnings("unchecked")
517     private <T, O, S> T castValue(final Map<ExtendedDataType<?, ?, ?, ?>, Object> extendedData,
518             final ExtendedDataType<?, ?, ?, ?> extendedDataType, final int i) throws SamplingException
519     {
520         // is only called on value directly taken from an ExtendedDataType within range of trajectory
521         ExtendedDataType<T, O, S, ?> edt = (ExtendedDataType<T, O, S, ?>) extendedDataType;
522         return edt.getOutputValue((O) extendedData.get(edt), i);
523     }
524 
525     /** {@inheritDoc} */
526     @Override
527     public int hashCode()
528     {
529         final int prime = 31;
530         int result = 1;
531         result = prime * result + ((this.endTimes == null) ? 0 : this.endTimes.hashCode());
532         result = prime * result + ((this.extendedDataTypes == null) ? 0 : this.extendedDataTypes.hashCode());
533         result = prime * result + ((this.registeredMetaDataTypes == null) ? 0 : this.registeredMetaDataTypes.hashCode());
534         result = prime * result + ((this.trajectories == null) ? 0 : this.trajectories.hashCode());
535         result = prime * result + ((this.trajectoryPerGtu == null) ? 0 : this.trajectoryPerGtu.hashCode());
536         return result;
537     }
538 
539     /** {@inheritDoc} */
540     @Override
541     public boolean equals(final Object obj)
542     {
543         if (this == obj)
544         {
545             return true;
546         }
547         if (obj == null)
548         {
549             return false;
550         }
551         if (getClass() != obj.getClass())
552         {
553             return false;
554         }
555         Sampler<?> other = (Sampler<?>) obj;
556         if (this.endTimes == null)
557         {
558             if (other.endTimes != null)
559             {
560                 return false;
561             }
562         }
563         else if (!this.endTimes.equals(other.endTimes))
564         {
565             return false;
566         }
567         if (this.extendedDataTypes == null)
568         {
569             if (other.extendedDataTypes != null)
570             {
571                 return false;
572             }
573         }
574         else if (!this.extendedDataTypes.equals(other.extendedDataTypes))
575         {
576             return false;
577         }
578         if (this.registeredMetaDataTypes == null)
579         {
580             if (other.registeredMetaDataTypes != null)
581             {
582                 return false;
583             }
584         }
585         else if (!this.registeredMetaDataTypes.equals(other.registeredMetaDataTypes))
586         {
587             return false;
588         }
589         if (this.trajectories == null)
590         {
591             if (other.trajectories != null)
592             {
593                 return false;
594             }
595         }
596         else if (!this.trajectories.equals(other.trajectories))
597         {
598             return false;
599         }
600         if (this.trajectoryPerGtu == null)
601         {
602             if (other.trajectoryPerGtu != null)
603             {
604                 return false;
605             }
606         }
607         else if (!this.trajectoryPerGtu.equals(other.trajectoryPerGtu))
608         {
609             return false;
610         }
611         return true;
612     }
613 
614     /**
615      * Defines the compression method for stored data.
616      * <p>
617      * Copyright (c) 2013-2020 Delft University of Technology, PO Box 5, 2600 AA, Delft, the Netherlands. All rights reserved.
618      * <br>
619      * BSD-style license. See <a href="http://opentrafficsim.org/node/13">OpenTrafficSim License</a>.
620      * <p>
621      * @version $Revision$, $LastChangedDate$, by $Author$, initial version 3 mei 2017 <br>
622      * @author <a href="http://www.tbm.tudelft.nl/averbraeck">Alexander Verbraeck</a>
623      * @author <a href="http://www.tudelft.nl/pknoppers">Peter Knoppers</a>
624      * @author <a href="http://www.transport.citg.tudelft.nl">Wouter Schakel</a>
625      */
626     public enum CompressionMethod
627     {
628         /** No compression. */
629         NONE,
630 
631         /** Duplicate info per trajectory is only stored at the first sample, and empty for other samples. */
632         OMIT_DUPLICATE_INFO,
633 
634         /** Zip compression. */
635         ZIP,
636 
637     }
638 
639 }