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.Collection;
7 import java.util.Iterator;
8 import java.util.LinkedHashMap;
9 import java.util.LinkedHashSet;
10 import java.util.List;
11 import java.util.Map;
12 import java.util.Set;
13 import java.util.function.BiFunction;
14 import java.util.function.Function;
15
16 import org.djunits.value.vdouble.scalar.Length;
17 import org.djunits.value.vdouble.scalar.Time;
18 import org.opentrafficsim.base.CompressedFileWriter;
19 import org.opentrafficsim.kpi.interfaces.GtuDataInterface;
20 import org.opentrafficsim.kpi.interfaces.GtuTypeDataInterface;
21 import org.opentrafficsim.kpi.interfaces.LaneDataInterface;
22 import org.opentrafficsim.kpi.interfaces.LinkDataInterface;
23 import org.opentrafficsim.kpi.interfaces.NodeDataInterface;
24 import org.opentrafficsim.kpi.interfaces.RouteDataInterface;
25 import org.opentrafficsim.kpi.sampling.ListTable.ListRecord;
26 import org.opentrafficsim.kpi.sampling.data.ExtendedDataType;
27 import org.opentrafficsim.kpi.sampling.meta.FilterDataType;
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43 public class SamplerData<G extends GtuDataInterface> extends AbstractTable
44 {
45
46
47
48
49
50 public SamplerData(final Collection<Column<?>> columns)
51 {
52 super("sampler", "Trajectory data", columns);
53 }
54
55
56 private final Map<KpiLaneDirection, TrajectoryGroup<G>> trajectories = new LinkedHashMap<>();
57
58
59
60
61
62
63 protected final void putTrajectoryGroup(final KpiLaneDirection kpiLaneDirection, final TrajectoryGroup<G> trajectoryGroup)
64 {
65 this.trajectories.put(kpiLaneDirection, trajectoryGroup);
66 }
67
68
69
70
71
72 public final Set<KpiLaneDirection> getLaneDirections()
73 {
74 return this.trajectories.keySet();
75 }
76
77
78
79
80
81
82 public final boolean contains(final KpiLaneDirection kpiLaneDirection)
83 {
84 return this.trajectories.containsKey(kpiLaneDirection);
85 }
86
87
88
89
90
91
92 public final TrajectoryGroup<G> getTrajectoryGroup(final KpiLaneDirection kpiLaneDirection)
93 {
94 return this.trajectories.get(kpiLaneDirection);
95 }
96
97
98
99
100
101 public final void writeToFile(final String file)
102 {
103 writeToFile(file, "%.3f", CompressionMethod.ZIP);
104 }
105
106
107
108
109
110
111
112 public final void writeToFile(final String file, final String format, final CompressionMethod compression)
113 {
114 int counter = 0;
115 BufferedWriter bw = CompressedFileWriter.create(file, compression.equals(CompressionMethod.ZIP));
116
117
118
119
120
121
122
123
124
125
126
127
128 Collection<TrajectoryGroup<G>> groups = this.trajectories.values();
129 try
130 {
131
132 List<FilterDataType<?>> allFilterDataTypes = new ArrayList<>();
133 for (TrajectoryGroup<G> group : groups)
134 {
135 for (Trajectory<G> trajectory : group.getTrajectories())
136 {
137 for (FilterDataType<?> filterDataType : trajectory.getFilterDataTypes())
138 {
139 if (!allFilterDataTypes.contains(filterDataType))
140 {
141 allFilterDataTypes.add(filterDataType);
142 }
143 }
144 }
145 }
146
147 List<ExtendedDataType<?, ?, ?, ?>> allExtendedDataTypes = new ArrayList<>();
148 for (TrajectoryGroup<G> group : groups)
149 {
150 for (Trajectory<?> trajectory : group.getTrajectories())
151 {
152 for (ExtendedDataType<?, ?, ?, ?> extendedDataType : trajectory.getExtendedDataTypes())
153 {
154 if (!allExtendedDataTypes.contains(extendedDataType))
155 {
156 allExtendedDataTypes.add(extendedDataType);
157 }
158 }
159 }
160 }
161
162 StringBuilder str = new StringBuilder();
163 str.append("traj#,linkId,laneId&dir,gtuId,t,x,v,a");
164 for (FilterDataType<?> metaDataType : allFilterDataTypes)
165 {
166 str.append(",");
167 str.append(metaDataType.getId());
168 }
169 for (ExtendedDataType<?, ?, ?, ?> extendedDataType : allExtendedDataTypes)
170 {
171 str.append(",");
172 str.append(extendedDataType.getId());
173 }
174 bw.write(str.toString());
175 bw.newLine();
176 for (TrajectoryGroup<G> group : groups)
177 {
178 for (Trajectory<G> trajectory : group.getTrajectories())
179 {
180 counter++;
181 float[] t = trajectory.getT();
182 float[] x = trajectory.getX();
183 float[] v = trajectory.getV();
184 float[] a = trajectory.getA();
185 Map<ExtendedDataType<?, ?, ?, ?>, Object> extendedData = new LinkedHashMap<>();
186 for (ExtendedDataType<?, ?, ?, ?> extendedDataType : allExtendedDataTypes)
187 {
188 if (trajectory.contains(extendedDataType))
189 {
190 try
191 {
192 extendedData.put(extendedDataType, trajectory.getExtendedData(extendedDataType));
193 }
194 catch (SamplingException exception)
195 {
196
197 throw new RuntimeException("Error while loading extended data type.", exception);
198 }
199 }
200 }
201 for (int i = 0; i < t.length; i++)
202 {
203
204 str = new StringBuilder();
205 str.append(counter);
206 str.append(",");
207 if (!compression.equals(CompressionMethod.OMIT_DUPLICATE_INFO) || i == 0)
208 {
209 str.append(group.getLaneDirection().getLaneData().getLinkData().getId());
210 str.append(",");
211 str.append(group.getLaneDirection().getLaneData().getId());
212 str.append(group.getLaneDirection().getKpiDirection().isPlus() ? "+" : "-");
213 str.append(",");
214 str.append(trajectory.getGtuId());
215 str.append(",");
216 }
217 else
218 {
219
220 str.append(",,,");
221 }
222 str.append(String.format(format, t[i]));
223 str.append(",");
224 str.append(String.format(format, x[i]));
225 str.append(",");
226 str.append(String.format(format, v[i]));
227 str.append(",");
228 str.append(String.format(format, a[i]));
229 for (FilterDataType<?> metaDataType : allFilterDataTypes)
230 {
231 str.append(",");
232 if (i == 0 && trajectory.contains(metaDataType))
233 {
234
235 str.append(metaDataType.formatValue(format, castValue(trajectory.getMetaData(metaDataType))));
236 }
237 }
238 for (ExtendedDataType<?, ?, ?, ?> extendedDataType : allExtendedDataTypes)
239 {
240 str.append(",");
241 if (trajectory.contains(extendedDataType))
242 {
243 try
244 {
245 str.append(extendedDataType.formatValue(format, castValue(extendedData, extendedDataType,
246 i)));
247 }
248 catch (SamplingException exception)
249 {
250
251 throw new RuntimeException("Error while loading extended data type.", exception);
252 }
253 }
254 }
255 bw.write(str.toString());
256 bw.newLine();
257 }
258 }
259 }
260 }
261 catch (IOException exception)
262 {
263 throw new RuntimeException("Could not write to file.", exception);
264 }
265
266 finally
267 {
268 try
269 {
270 if (bw != null)
271 {
272 bw.close();
273 }
274 }
275 catch (IOException ex)
276 {
277 ex.printStackTrace();
278 }
279 }
280 }
281
282
283
284
285
286
287
288 @SuppressWarnings("unchecked")
289 private <T> T castValue(final Object value)
290 {
291 return (T) value;
292 }
293
294
295
296
297
298
299
300
301
302
303
304
305 @SuppressWarnings("unchecked")
306 private <T, O, S> T castValue(final Map<ExtendedDataType<?, ?, ?, ?>, Object> extendedData, final ExtendedDataType<?, ?, ?,
307 ?> extendedDataType, final int i) throws SamplingException
308 {
309
310 ExtendedDataType<T, O, S, ?> edt = (ExtendedDataType<T, O, S, ?>) extendedDataType;
311 return edt.getOutputValue((O) extendedData.get(edt), i);
312 }
313
314
315
316
317
318
319
320
321
322
323
324
325
326 public enum CompressionMethod
327 {
328
329 NONE,
330
331
332 OMIT_DUPLICATE_INFO,
333
334
335 ZIP,
336 }
337
338
339
340
341
342
343
344
345
346
347
348
349 public static SamplerData<?> loadFromFile(final String file)
350 {
351 return loadFromFile(file, new LinkedHashSet<ExtendedDataType<?, ?, ?, ?>>(), new LinkedHashSet<FilterDataType<?>>());
352 }
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367 @SuppressWarnings("unchecked")
368 public static SamplerData<?> loadFromFile(final String file, final Set<ExtendedDataType<?, ?, ?, ?>> extendedDataTypes,
369 final Set<FilterDataType<?>> metaDataTypes)
370 {
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
393
394
395
396
397
398
399
400
401
402
403
404
405
406
407
408
409 return null;
410 }
411
412
413
414
415
416
417
418
419
420 private final <T> T getOrCreate(final String id, final Map<String, T> map, final Function<String, T> producer)
421 {
422 if (!map.containsKey(id))
423 {
424 map.put(id, producer.apply(id));
425 }
426 return map.get(id);
427 }
428
429
430
431
432 @Override
433 public Iterator<Record> iterator()
434 {
435
436
437
438
439
440 Iterator<KpiLaneDirection> laneIterator = this.trajectories.keySet().iterator();
441
442 return new Iterator<Record>()
443 {
444 private Iterator<Trajectory<G>> trajectoryIterator = laneIterator.hasNext() ? SamplerData.this.trajectories.get(
445 laneIterator.next()).iterator() : null;
446
447 private Trajectory<G> trajectory = this.trajectoryIterator != null && this.trajectoryIterator.hasNext()
448 ? this.trajectoryIterator.next() : null;
449
450 private Trajectory<G> currentTrajectory;
451
452 private int index;
453
454 @Override
455 public boolean hasNext()
456 {
457 if (this.index == this.currentTrajectory.size())
458 {
459
460
461 }
462 return true;
463 }
464
465 @Override
466 public Record next()
467 {
468 Record record = new Record()
469 {
470 @Override
471 public <T> T getValue(final Column<T> column)
472 {
473 return null;
474 }
475
476 @Override
477 public Object getValue(final String id)
478 {
479 return null;
480 }
481 };
482 this.index++;
483 return record;
484 }
485 };
486 }
487
488
489 @Override
490 public boolean isEmpty()
491 {
492 for (TrajectoryGroup<G> group : this.trajectories.values())
493 {
494 for (Trajectory<G> trajectory : group.getTrajectories())
495 {
496 if (trajectory.size() > 0)
497 {
498 return false;
499 }
500 }
501 }
502 return true;
503 }
504
505
506
507
508
509
510
511 private static class Getter<T>
512 {
513
514
515 private final Map<String, T> map = new LinkedHashMap<>();
516
517
518 private Function<String, T> function;
519
520
521
522
523
524 Getter(final Function<String, T> function)
525 {
526 this.function = function;
527 }
528
529
530
531
532
533
534 public T get(final String id)
535 {
536 T t;
537 if (!this.map.containsKey(id))
538 {
539 t = this.function.apply(id);
540 this.map.put(id, t);
541 }
542 else
543 {
544 t = this.map.get(id);
545 }
546 return t;
547 }
548 }
549
550
551
552
553
554
555 private static class BiGetter<O, T>
556 {
557
558
559 private final Map<String, T> map = new LinkedHashMap<>();
560
561
562 private BiFunction<String, O, T> function;
563
564
565
566
567
568 BiGetter(final BiFunction<String, O, T> function)
569 {
570 this.function = function;
571 }
572
573
574
575
576
577
578
579 public T get(final String id, final O o)
580 {
581 T t;
582 if (!this.map.containsKey(id))
583 {
584 t = this.function.apply(id, o);
585 this.map.put(id, t);
586 }
587 else
588 {
589 t = this.map.get(id);
590 }
591 return t;
592 }
593 }
594
595
596 private static class LinkData implements LinkDataInterface
597 {
598
599
600 private final Length length = null;
601
602
603 private final String id;
604
605
606 private final List<LaneData> lanes = new ArrayList<>();
607
608
609
610
611 LinkData(final String id)
612 {
613 this.id = id;
614 }
615
616
617 @Override
618 public Length getLength()
619 {
620 return this.length;
621 }
622
623
624 @Override
625 public List<? extends LaneDataInterface> getLaneDatas()
626 {
627 return this.lanes;
628 }
629
630
631 @Override
632 public String getId()
633 {
634 return this.id;
635 }
636
637 }
638
639
640 private static class LaneData implements LaneDataInterface
641 {
642
643
644 private final Length length = null;
645
646
647 private final String id;
648
649
650 private final LinkData link;
651
652
653
654
655
656
657 @SuppressWarnings("synthetic-access")
658 LaneData(final String id, final LinkData link)
659 {
660 this.id = id;
661 this.link = link;
662 link.lanes.add(this);
663 }
664
665
666 @Override
667 public Length getLength()
668 {
669 return this.length;
670 }
671
672
673 @Override
674 public LinkData getLinkData()
675 {
676 return this.link;
677 }
678
679
680 @Override
681 public String getId()
682 {
683 return this.id;
684 }
685
686 }
687
688
689 private static class NodeData implements NodeDataInterface
690 {
691
692
693 private String id;
694
695
696
697
698
699 NodeData(final String id)
700 {
701 this.id = id;
702 }
703
704
705 @Override
706 public String getId()
707 {
708 return null;
709 }
710
711 }
712
713
714 private static class GtuTypeData implements GtuTypeDataInterface
715 {
716
717
718 private String id;
719
720
721
722
723
724 GtuTypeData(final String id)
725 {
726 this.id = id;
727 }
728
729
730 @Override
731 public String getId()
732 {
733 return null;
734 }
735
736 }
737
738
739 private static class RouteData implements RouteDataInterface
740 {
741
742
743 private String id;
744
745
746
747
748
749 RouteData(final String id)
750 {
751 this.id = id;
752 }
753
754
755 @Override
756 public String getId()
757 {
758 return null;
759 }
760
761 }
762
763
764 private static class GtuData implements GtuDataInterface
765 {
766
767
768 private final String id;
769
770
771 private final NodeData origin;
772
773
774 private final NodeData destination;
775
776
777 private final GtuTypeData gtuType;
778
779
780 private final RouteData route;
781
782
783
784
785
786
787
788
789 GtuData(final String id, final NodeData origin, final NodeData destination, final GtuTypeData gtuType,
790 final RouteData route)
791 {
792 this.id = id;
793 this.origin = origin;
794 this.destination = destination;
795 this.gtuType = gtuType;
796 this.route = route;
797 }
798
799
800 @Override
801 public String getId()
802 {
803 return this.id;
804 }
805
806
807 @Override
808 public NodeDataInterface getOriginNodeData()
809 {
810 return this.origin;
811 }
812
813
814 @Override
815 public NodeDataInterface getDestinationNodeData()
816 {
817 return this.destination;
818 }
819
820
821 @Override
822 public GtuTypeDataInterface getGtuTypeData()
823 {
824 return this.gtuType;
825 }
826
827
828 @Override
829 public RouteDataInterface getRouteData()
830 {
831 return this.route;
832 }
833
834 }
835
836 }