1 package org.opentrafficsim.draw.egtf;
2
3 import java.util.LinkedHashMap;
4 import java.util.LinkedHashSet;
5 import java.util.Map;
6 import java.util.NavigableMap;
7 import java.util.Objects;
8 import java.util.Set;
9 import java.util.SortedMap;
10 import java.util.TreeMap;
11 import java.util.stream.IntStream;
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70 public class Egtf
71 {
72
73
74 private static final double DEFAULT_SIGMA = 300.0;
75
76
77 private static final double DEFAULT_TAU = 30.0;
78
79
80 private Kernel kernel;
81
82
83 private final double cCong;
84
85
86 private final double cFree;
87
88
89 private final double deltaV;
90
91
92 private final double vc;
93
94
95 private final Map<String, DataSource> dataSources = new LinkedHashMap<>();
96
97
98 private DataSource defaultDataSource = null;
99
100
101 private Map<Quantity<?, ?>, DataStream<?>> defaultDataStreams = null;
102
103
104 private boolean addingByQuantity;
105
106
107 private NavigableMap<Double, NavigableMap<Double, Map<DataStream<?>, Double>>> data = new TreeMap<>();
108
109
110 private boolean interrupted = false;
111
112
113 private Set<EgtfListener> listeners = new LinkedHashSet<>();
114
115
116
117
118 public Egtf()
119 {
120 this(-18.0, 80.0, 10.0, 80.0);
121 }
122
123
124
125
126
127
128
129
130 public Egtf(final double cCong, final double cFree, final double deltaV, final double vc)
131 {
132 this.cCong = cCong / 3.6;
133 this.cFree = cFree / 3.6;
134 this.deltaV = deltaV / 3.6;
135 this.vc = vc / 3.6;
136 setKernel();
137 }
138
139
140
141
142
143
144
145
146
147
148
149
150 @SuppressWarnings("parameternumber")
151 public Egtf(final double cCong, final double cFree, final double deltaV, final double vc, final double sigma,
152 final double tau, final double xMax, final double tMax)
153 {
154 this(cCong, cFree, deltaV, vc);
155 setKernelSI(sigma, tau, xMax, tMax);
156 }
157
158
159
160
161
162
163
164
165
166
167
168 public DataSource getDataSource(final String name)
169 {
170 if (this.defaultDataSource != null)
171 {
172 throw new IllegalStateException(
173 "Obtaining a (new) data source after data has been added without a data source is not allowed.");
174 }
175 return this.dataSources.computeIfAbsent(name, (key) -> new DataSource(key));
176 }
177
178
179
180
181
182
183 public synchronized void clearDataBefore(final double time)
184 {
185 for (SortedMap<Double, Map<DataStream<?>, Double>> map : this.data.values())
186 {
187 map.subMap(Double.NEGATIVE_INFINITY, time).clear();
188 }
189 }
190
191
192
193
194
195
196
197
198
199 public synchronized void addPointDataSI(final Quantity<?, ?> quantity, final double location, final double time,
200 final double value)
201 {
202 this.addingByQuantity = true;
203 addPointDataSI(getDefaultDataStream(quantity), location, time, value);
204 this.addingByQuantity = false;
205 }
206
207
208
209
210
211
212
213
214
215 public synchronized void addPointDataSI(final DataStream<?> dataStream, final double location, final double time,
216 final double value)
217 {
218 checkNoQuantityData();
219 Objects.requireNonNull(dataStream, "Datastream may not be null.");
220 if (!Double.isNaN(value))
221 {
222 getSpacioTemporalData(getSpatialData(location), time).put(dataStream, value);
223 }
224 }
225
226
227
228
229
230
231
232
233
234 public synchronized void addVectorDataSI(final Quantity<?, ?> quantity, final double[] location, final double[] time,
235 final double[] values)
236 {
237 this.addingByQuantity = true;
238 addVectorDataSI(getDefaultDataStream(quantity), location, time, values);
239 this.addingByQuantity = false;
240 }
241
242
243
244
245
246
247
248
249
250 public synchronized void addVectorDataSI(final DataStream<?> dataStream, final double[] location, final double[] time,
251 final double[] values)
252 {
253 checkNoQuantityData();
254 Objects.requireNonNull(dataStream, "Datastream may not be null.");
255 Objects.requireNonNull(location, "Location may not be null.");
256 Objects.requireNonNull(time, "Time may not be null.");
257 Objects.requireNonNull(values, "Values may not be null.");
258 if (location.length != time.length || time.length != values.length)
259 {
260 throw new IllegalArgumentException(String.format("Unequal lengths: location %d, time %d, data %d.", location.length,
261 time.length, values.length));
262 }
263 for (int i = 0; i < values.length; i++)
264 {
265 if (!Double.isNaN(values[i]))
266 {
267 getSpacioTemporalData(getSpatialData(location[i]), time[i]).put(dataStream, values[i]);
268 }
269 }
270 }
271
272
273
274
275
276
277
278
279
280 public synchronized void addGridDataSI(final Quantity<?, ?> quantity, final double[] location, final double[] time,
281 final double[][] values)
282 {
283 this.addingByQuantity = true;
284 addGridDataSI(getDefaultDataStream(quantity), location, time, values);
285 this.addingByQuantity = false;
286 }
287
288
289
290
291
292
293
294
295
296 public synchronized void addGridDataSI(final DataStream<?> dataStream, final double[] location, final double[] time,
297 final double[][] values)
298 {
299 checkNoQuantityData();
300 Objects.requireNonNull(dataStream, "Datastream may not be null.");
301 Objects.requireNonNull(location, "Location may not be null.");
302 Objects.requireNonNull(time, "Time may not be null.");
303 Objects.requireNonNull(values, "Values may not be null.");
304 if (values.length != location.length)
305 {
306 throw new IllegalArgumentException(
307 String.format("%d locations while length of data is %d", location.length, values.length));
308 }
309 for (int i = 0; i < location.length; i++)
310 {
311 if (values[i].length != time.length)
312 {
313 throw new IllegalArgumentException(
314 String.format("%d times while length of data is %d", time.length, values[i].length));
315 }
316 Map<Double, Map<DataStream<?>, Double>> spatialData = getSpatialData(location[i]);
317 for (int j = 0; j < time.length; j++)
318 {
319 if (!Double.isNaN(values[i][j]))
320 {
321 getSpacioTemporalData(spatialData, time[j]).put(dataStream, values[i][j]);
322 }
323 }
324 }
325 }
326
327
328
329
330
331 private void checkNoQuantityData()
332 {
333 if (!this.addingByQuantity && this.defaultDataSource != null)
334 {
335 throw new IllegalStateException(
336 "Adding data with a data stream is not allowed after data has been added with a quantity.");
337 }
338 }
339
340
341
342
343
344
345
346 private DataStream<?> getDefaultDataStream(final Quantity<?, ?> quantity)
347 {
348 Objects.requireNonNull(quantity, "Quantity may not be null.");
349 if (!this.dataSources.isEmpty())
350 {
351 throw new IllegalStateException(
352 "Adding data with a quantity is not allowed after data has been added with a data stream.");
353 }
354 if (this.defaultDataSource == null)
355 {
356 this.defaultDataSource = new DataSource("default");
357 this.defaultDataStreams = new LinkedHashMap<>();
358 }
359 return this.defaultDataStreams.computeIfAbsent(quantity,
360 (key) -> this.defaultDataSource.addStreamSI(quantity, 1.0, 1.0));
361 }
362
363
364
365
366
367
368 private SortedMap<Double, Map<DataStream<?>, Double>> getSpatialData(final double location)
369 {
370 return this.data.computeIfAbsent(location, (key) -> new TreeMap<>());
371 }
372
373
374
375
376
377
378
379
380 private Map<DataStream<?>, Double> getSpacioTemporalData(final Map<Double, Map<DataStream<?>, Double>> spatialData,
381 final double time)
382 {
383 return spatialData.computeIfAbsent(time, (key) -> new LinkedHashMap<>());
384 }
385
386
387
388
389
390
391
392
393 public void setKernel()
394 {
395 setKernelSI(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, new ExpKernelShape(DEFAULT_SIGMA, DEFAULT_TAU));
396 }
397
398
399
400
401
402
403 public void setKernelSI(final double sigma, final double tau)
404 {
405 setKernelSI(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, sigma, tau);
406 }
407
408
409
410
411
412
413
414
415 public void setKernelSI(final double sigma, final double tau, final double xMax, final double tMax)
416 {
417 setKernelSI(xMax, tMax, new ExpKernelShape(sigma, tau));
418 }
419
420
421
422
423
424
425 public void setGaussKernelSI(final double sigma, final double tau)
426 {
427 setGaussKernelSI(Double.POSITIVE_INFINITY, Double.POSITIVE_INFINITY, sigma, tau);
428 }
429
430
431
432
433
434
435
436
437 public void setGaussKernelSI(final double sigma, final double tau, final double xMax, final double tMax)
438 {
439 setKernelSI(xMax, tMax, new GaussKernelShape(sigma, tau));
440 }
441
442
443
444
445
446
447
448 public synchronized void setKernelSI(final double xMax, final double tMax, final KernelShape shape)
449 {
450 this.kernel = new Kernel(xMax, tMax, shape);
451 }
452
453
454
455
456
457 final double getWaveSpeedCongestion()
458 {
459 return this.cCong;
460 }
461
462
463
464
465
466 final double getWaveSpeedFreeFlow()
467 {
468 return this.cFree;
469 }
470
471
472
473
474
475
476
477
478
479
480
481
482
483 public EgtfParallelListener filterParallelSI(final double[] location, final double[] time,
484 final Quantity<?, ?>... quantities)
485 {
486 Objects.requireNonNull(location, "Location may not be null.");
487 Objects.requireNonNull(time, "Time may not be null.");
488 EgtfParallelListener listener = new EgtfParallelListener();
489 addListener(listener);
490 new Thread(new Runnable()
491 {
492 @Override
493 public void run()
494 {
495 listener.setFilter(filterSI(location, time, quantities));
496 removeListener(listener);
497 }
498 }, "Egtf calculation thread").start();
499 return listener;
500 }
501
502
503
504
505
506
507
508
509
510
511
512
513
514 public EgtfParallelListener filterParallelFastSI(final double xMin, final double xStep, final double xMax,
515 final double tMin, final double tStep, final double tMax, final Quantity<?, ?>... quantities)
516 {
517 EgtfParallelListener listener = new EgtfParallelListener();
518 addListener(listener);
519 new Thread(new Runnable()
520 {
521 @Override
522 public void run()
523 {
524 listener.setFilter(filterFastSI(xMin, xStep, xMax, tMin, tStep, tMax, quantities));
525 removeListener(listener);
526 }
527 }, "Egtf calculation thread").start();
528 return listener;
529 }
530
531
532
533
534
535
536
537
538 @SuppressWarnings("methodlength")
539 public Filter filterSI(final double[] location, final double[] time, final Quantity<?, ?>... quantities)
540 {
541 Objects.requireNonNull(location, "Location may not be null.");
542 Objects.requireNonNull(time, "Time may not be null.");
543
544
545 Map<Quantity<?, ?>, double[][]> map = new LinkedHashMap<>();
546 for (Quantity<?, ?> quantity : quantities)
547 {
548 map.put(quantity, new double[location.length][time.length]);
549 }
550
551
552 for (int i = 0; i < location.length; i++)
553 {
554 double xGrid = location[i];
555
556
557 Map<Double, NavigableMap<Double, Map<DataStream<?>, Double>>> spatialData =
558 this.data.subMap(this.kernel.fromLocation(xGrid), true, this.kernel.toLocation(xGrid), true);
559
560
561 for (int j = 0; j < time.length; j++)
562 {
563 double tGrid = time[j];
564
565
566 if (notifyListeners((i + (double) j / time.length) / location.length))
567 {
568 return null;
569 }
570
571
572
573 Map<DataStream<?>, DualWeightedMean> zCongFree = new LinkedHashMap<>();
574
575
576 for (Map.Entry<Double, NavigableMap<Double, Map<DataStream<?>, Double>>> xEntry : spatialData.entrySet())
577 {
578 double dx = xEntry.getKey() - xGrid;
579 Map<Double, Map<DataStream<?>, Double>> temporalData =
580 xEntry.getValue().subMap(this.kernel.fromTime(tGrid), true, this.kernel.toTime(tGrid), true);
581
582 for (Map.Entry<Double, Map<DataStream<?>, Double>> tEntry : temporalData.entrySet())
583 {
584 double dt = tEntry.getKey() - tGrid;
585 Map<DataStream<?>, Double> pData = tEntry.getValue();
586
587 double phiCong = this.kernel.weight(this.cCong, dx, dt);
588 double phiFree = this.kernel.weight(this.cFree, dx, dt);
589
590
591 for (Map.Entry<DataStream<?>, Double> vEntry : pData.entrySet())
592 {
593 DataStream<?> stream = vEntry.getKey();
594 if (map.containsKey(stream.getQuantity()) || stream.getQuantity().isSpeed())
595 {
596 double v = vEntry.getValue();
597 DualWeightedMean zCongFreeOfStream =
598 zCongFree.computeIfAbsent(stream, (key) -> new DualWeightedMean());
599 zCongFreeOfStream.addCong(v, phiCong);
600 zCongFreeOfStream.addFree(v, phiFree);
601 }
602 }
603 }
604 }
605
606
607 Map<DataSource, Double> w = new LinkedHashMap<>();
608 for (Map.Entry<DataStream<?>, DualWeightedMean> streamEntry : zCongFree.entrySet())
609 {
610 DataStream<?> dataStream = streamEntry.getKey();
611 if (dataStream.getQuantity().isSpeed())
612 {
613 DualWeightedMean zCongFreeOfStream = streamEntry.getValue();
614 double u = Math.min(zCongFreeOfStream.getCong(), zCongFreeOfStream.getFree());
615 w.put(dataStream.getDataSource(),
616 .5 * (1.0 + Math.tanh((Egtf.this.vc - u) / Egtf.this.deltaV)));
617 continue;
618 }
619 }
620
621
622 Double wMean = null;
623 for (Map.Entry<Quantity<?, ?>, double[][]> qEntry : map.entrySet())
624 {
625 Quantity<?, ?> quantity = qEntry.getKey();
626 WeightedMean z = new WeightedMean();
627 for (Map.Entry<DataStream<?>, DualWeightedMean> zEntry : zCongFree.entrySet())
628 {
629 DataStream<?> dataStream = zEntry.getKey();
630 if (dataStream.getQuantity().equals(quantity))
631 {
632
633 double wCong;
634 if (!w.containsKey(dataStream.getDataSource()))
635 {
636
637 if (wMean == null)
638 {
639
640 for (Quantity<?, ?> prevQuant : quantities)
641 {
642 if (prevQuant.equals(quantity))
643 {
644
645 wMean = 0.0;
646 for (double ww : w.values())
647 {
648 wMean += ww / w.size();
649 }
650 break;
651 }
652 else if (prevQuant.isSpeed())
653 {
654 wMean = .5 * (1.0
655 + Math.tanh((Egtf.this.vc - map.get(prevQuant)[i][j]) / Egtf.this.deltaV));
656 break;
657 }
658 }
659 }
660 wCong = wMean;
661 }
662 else
663 {
664 wCong = w.get(dataStream.getDataSource());
665 }
666
667 double wfree = 1.0 - wCong;
668 DualWeightedMean zCongFreej = zEntry.getValue();
669 double zStream = wCong * zCongFreej.getCong() + wfree * zCongFreej.getFree();
670 double weight;
671 if (w.size() > 1)
672 {
673
674 double beta = wCong * zCongFreej.getDenominatorCong() + wfree * zCongFreej.getDenominatorFree();
675
676 double alpha = wCong / dataStream.getThetaCong() + wfree / dataStream.getThetaFree();
677 weight = alpha * beta;
678 }
679 else
680 {
681 weight = 1.0;
682 }
683 z.add(zStream, weight);
684 }
685 }
686 qEntry.getValue()[i][j] = z.get();
687 }
688 }
689 }
690 notifyListeners(1.0);
691
692 return new FilterDouble(location, time, map);
693 }
694
695
696
697
698
699
700
701
702
703
704
705
706
707
708
709
710
711
712 @SuppressWarnings("methodlength")
713 public Filter filterFastSI(final double xMin, final double xStep, final double xMax, final double tMin, final double tStep,
714 final double tMax, final Quantity<?, ?>... quantities)
715 {
716 if (xMin > xMax || xStep <= 0.0 || tMin > tMax || tStep <= 0.0)
717 {
718 throw new IllegalArgumentException(
719 "Ill-defined grid. Make sure that xMax >= xMin, dx > 0, tMax >= tMin and dt > 0");
720 }
721 if (notifyListeners(0.0))
722 {
723 return null;
724 }
725
726
727 int n = 1 + (int) ((xMax - xMin) / xStep);
728 double[] location = new double[n];
729 IntStream.range(0, n).forEach(i -> location[i] = xMin + i * xStep);
730 n = 1 + (int) ((tMax - tMin) / tStep);
731 double[] time = new double[n];
732 IntStream.range(0, n).forEach(j -> time[j] = tMin + j * tStep);
733 Map<Quantity<?, ?>, double[][]> map = new LinkedHashMap<>();
734 Map<Quantity<?, ?>, double[][]> weights = new LinkedHashMap<>();
735 for (Quantity<?, ?> quantity : quantities)
736 {
737 map.put(quantity, new double[location.length][time.length]);
738 weights.put(quantity, new double[location.length][time.length]);
739 }
740
741
742 double xFrom = this.kernel.fromLocation(0.0);
743 xFrom = Double.isInfinite(xFrom) ? 2.0 * (xMin - xMax) : xFrom;
744 double xTo = this.kernel.toLocation(0.0);
745 xTo = Double.isInfinite(xTo) ? 2.0 * (xMax - xMin) : xTo;
746 double[] dx = equidistant(xFrom, xStep, xTo);
747 double tFrom = this.kernel.fromTime(0.0);
748 tFrom = Double.isInfinite(tFrom) ? 2.0 * (tMin - tMax) : tFrom;
749 double tTo = this.kernel.toTime(0.0);
750 tTo = Double.isInfinite(tTo) ? 2.0 * (tMax - tMin) : tTo;
751 double[] dt = equidistant(tFrom, tStep, tTo);
752 double[][] phiCong = new double[dx.length][dt.length];
753 double[][] phiFree = new double[dx.length][dt.length];
754 for (int i = 0; i < dx.length; i++)
755 {
756 for (int j = 0; j < dt.length; j++)
757 {
758 phiCong[i][j] = this.kernel.weight(this.cCong, dx[i], dt[j]);
759 phiFree[i][j] = this.kernel.weight(this.cFree, dx[i], dt[j]);
760 }
761 }
762
763
764 Map<DataStream<?>, double[][]> dataSum = new LinkedHashMap<>();
765 Map<DataStream<?>, double[][]> dataCount = new LinkedHashMap<>();
766
767 for (int i = 0; i < location.length; i++)
768 {
769
770 Map<Double, NavigableMap<Double, Map<DataStream<?>, Double>>> spatialData =
771 this.data.subMap(location[i] - 0.5 * xStep, true, location[i] + 0.5 * xStep, true);
772
773 for (int j = 0; j < time.length; j++)
774 {
775
776 for (NavigableMap<Double, Map<DataStream<?>, Double>> locationData : spatialData.values())
777 {
778 NavigableMap<Double, Map<DataStream<?>, Double>> temporalData =
779 locationData.subMap(time[j] - 0.5 * tStep, true, time[j] + 0.5 * tStep, true);
780 for (Map<DataStream<?>, Double> timeData : temporalData.values())
781 {
782 for (Map.Entry<DataStream<?>, Double> timeEntry : timeData.entrySet())
783 {
784 if (map.containsKey(timeEntry.getKey().getQuantity()) || timeEntry.getKey().getQuantity().isSpeed())
785 {
786 dataSum.computeIfAbsent(timeEntry.getKey(),
787 (key) -> new double[location.length][time.length])[i][j] += timeEntry.getValue();
788 dataCount.computeIfAbsent(timeEntry.getKey(),
789 (key) -> new double[location.length][time.length])[i][j]++;
790 }
791 }
792 }
793 }
794 }
795 }
796
797
798 double steps = quantities.length + 1;
799 double step = 0;
800
801 Map<DataSource, double[][]> w = new LinkedHashMap<>();
802 Map<DataSource, double[][]> zCongSpeed = new LinkedHashMap<>();
803 Map<DataSource, double[][]> zFreeSpeed = new LinkedHashMap<>();
804 Map<DataSource, double[][]> nCongSpeed = new LinkedHashMap<>();
805 Map<DataSource, double[][]> nFreeSpeed = new LinkedHashMap<>();
806 for (Map.Entry<DataStream<?>, double[][]> zEntry : dataSum.entrySet())
807 {
808 DataStream<?> dataStream = zEntry.getKey();
809 if (dataStream.getQuantity().isSpeed())
810 {
811
812 double[][] vCong = Convolution.convolution(phiCong, zEntry.getValue());
813 if (notifyListeners((step + 0.25) / steps))
814 {
815 return null;
816 }
817 double[][] vFree = Convolution.convolution(phiFree, zEntry.getValue());
818 if (notifyListeners((step + 0.5) / steps))
819 {
820 return null;
821 }
822 double[][] count = dataCount.get(dataStream);
823 double[][] nCong = Convolution.convolution(phiCong, count);
824 if (notifyListeners((step + 0.75) / steps))
825 {
826 return null;
827 }
828 double[][] nFree = Convolution.convolution(phiFree, count);
829 double[][] wSource = new double[vCong.length][vCong[0].length];
830 for (int i = 0; i < vCong.length; i++)
831 {
832 for (int j = 0; j < vCong[0].length; j++)
833 {
834 double u = Math.min(vCong[i][j] / nCong[i][j], vFree[i][j] / nFree[i][j]);
835 wSource[i][j] = .5 * (1.0 + Math.tanh((Egtf.this.vc - u) / Egtf.this.deltaV));
836 }
837 }
838 w.put(dataStream.getDataSource(), wSource);
839 zCongSpeed.put(dataStream.getDataSource(), vCong);
840 zFreeSpeed.put(dataStream.getDataSource(), vFree);
841 nCongSpeed.put(dataStream.getDataSource(), nCong);
842 nFreeSpeed.put(dataStream.getDataSource(), nFree);
843 }
844 }
845 step++;
846 if (notifyListeners(step / steps))
847 {
848 return null;
849 }
850
851
852 double[][] wMean = null;
853 for (Quantity<?, ?> quantity : quantities)
854 {
855
856 double[][] qData = map.get(quantity);
857 double[][] qWeights = weights.get(quantity);
858
859 Set<Map.Entry<DataStream<?>, double[][]>> zEntries = new LinkedHashSet<>();
860 for (Map.Entry<DataStream<?>, double[][]> zEntry : dataSum.entrySet())
861 {
862 if (zEntry.getKey().getQuantity().equals(quantity))
863 {
864 zEntries.add(zEntry);
865 }
866 }
867 double streamCounter = 0;
868 for (Map.Entry<DataStream<?>, double[][]> zEntry : zEntries)
869 {
870 DataStream<?> dataStream = zEntry.getKey();
871
872
873 double[][] wj;
874 if (!w.containsKey(dataStream.getDataSource()))
875 {
876
877 if (wMean == null)
878 {
879
880 for (Quantity<?, ?> prevQuant : quantities)
881 {
882 if (prevQuant.equals(quantity))
883 {
884
885 wMean = new double[location.length][time.length];
886 for (double[][] ww : w.values())
887 {
888 for (int i = 0; i < location.length; i++)
889 {
890 for (int j = 0; j < time.length; j++)
891 {
892 wMean[i][j] += ww[i][j] / w.size();
893 }
894 }
895 }
896 break;
897 }
898 else if (prevQuant.isSpeed())
899 {
900 wMean = new double[location.length][time.length];
901 double[][] v = map.get(prevQuant);
902 for (int i = 0; i < location.length; i++)
903 {
904 for (int j = 0; j < time.length; j++)
905 {
906 wMean[i][j] = .5 * (1.0 + Math.tanh((Egtf.this.vc - v[i][j]) / Egtf.this.deltaV));
907 }
908 }
909 break;
910 }
911 }
912 }
913 wj = wMean;
914 }
915 else
916 {
917 wj = w.get(dataStream.getDataSource());
918 }
919
920
921 double[][] zCong;
922 double[][] zFree;
923 double[][] nCong;
924 double[][] nFree;
925 if (dataStream.getQuantity().isSpeed())
926 {
927 zCong = zCongSpeed.get(dataStream.getDataSource());
928 zFree = zFreeSpeed.get(dataStream.getDataSource());
929 nCong = nCongSpeed.get(dataStream.getDataSource());
930 nFree = nFreeSpeed.get(dataStream.getDataSource());
931 }
932 else
933 {
934 zCong = Convolution.convolution(phiCong, zEntry.getValue());
935 if (notifyListeners((step + (streamCounter + 0.25) / zEntries.size()) / steps))
936 {
937 return null;
938 }
939 zFree = Convolution.convolution(phiFree, zEntry.getValue());
940 if (notifyListeners((step + (streamCounter + 0.5) / zEntries.size()) / steps))
941 {
942 return null;
943 }
944 double[][] count = dataCount.get(dataStream);
945 nCong = Convolution.convolution(phiCong, count);
946 if (notifyListeners((step + (streamCounter + 0.75) / zEntries.size()) / steps))
947 {
948 return null;
949 }
950 nFree = Convolution.convolution(phiFree, count);
951 }
952
953
954 for (int i = 0; i < location.length; i++)
955 {
956 for (int j = 0; j < time.length; j++)
957 {
958 double wCong = wj[i][j];
959 double wFree = 1.0 - wCong;
960 double value = wCong * zCong[i][j] / nCong[i][j] + wFree * zFree[i][j] / nFree[i][j];
961
962
963 double beta = wCong * nCong[i][j] + wFree * nFree[i][j];
964 double alpha = wCong / dataStream.getThetaCong() + wFree / dataStream.getThetaFree();
965 double weight = beta * alpha;
966 qData[i][j] += (value * weight);
967 qWeights[i][j] += weight;
968 }
969 }
970 streamCounter++;
971 if (notifyListeners((step + streamCounter / zEntries.size()) / steps))
972 {
973 return null;
974 }
975 }
976 for (int i = 0; i < location.length; i++)
977 {
978 for (int j = 0; j < time.length; j++)
979 {
980 qData[i][j] /= qWeights[i][j];
981 }
982 }
983 step++;
984 }
985
986 return new FilterDouble(location, time, map);
987 }
988
989
990
991
992
993
994
995
996 private double[] equidistant(final double from, final double step, final double to)
997 {
998 int n1 = (int) (-from / step);
999 int n2 = (int) (to / step);
1000 int n = n1 + n2 + 1;
1001 double[] array = new double[n];
1002 for (int i = 0; i < n; i++)
1003 {
1004 array[i] = i < n1 ? step * (-n1 + i) : step * (i - n1);
1005 }
1006 return array;
1007 }
1008
1009
1010
1011
1012
1013
1014
1015
1016 public final void interrupt()
1017 {
1018 this.interrupted = true;
1019 }
1020
1021
1022
1023
1024
1025 public final void addListener(final EgtfListener listener)
1026 {
1027 this.listeners.add(listener);
1028 }
1029
1030
1031
1032
1033
1034 public final void removeListener(final EgtfListener listener)
1035 {
1036 this.listeners.remove(listener);
1037 }
1038
1039
1040
1041
1042
1043
1044 private boolean notifyListeners(final double progress)
1045 {
1046 if (!this.listeners.isEmpty())
1047 {
1048 EgtfEvent event = new EgtfEvent(this, progress);
1049 for (EgtfListener listener : this.listeners)
1050 {
1051 listener.notifyProgress(event);
1052 }
1053 }
1054 return this.interrupted;
1055 }
1056
1057
1058
1059
1060
1061
1062
1063
1064 private final class DualWeightedMean
1065 {
1066
1067 private double numeratorCong;
1068
1069
1070 private double numeratorFree;
1071
1072
1073 private double denominatorCong;
1074
1075
1076 private double denominatorFree;
1077
1078
1079
1080
1081
1082
1083 public void addCong(final double value, final double weight)
1084 {
1085 this.numeratorCong += value * weight;
1086 this.denominatorCong += weight;
1087 }
1088
1089
1090
1091
1092
1093
1094 public void addFree(final double value, final double weight)
1095 {
1096 this.numeratorFree += value * weight;
1097 this.denominatorFree += weight;
1098 }
1099
1100
1101
1102
1103
1104 public double getCong()
1105 {
1106 return this.numeratorCong / this.denominatorCong;
1107 }
1108
1109
1110
1111
1112
1113 public double getFree()
1114 {
1115 return this.numeratorFree / this.denominatorFree;
1116 }
1117
1118
1119
1120
1121
1122 public double getDenominatorCong()
1123 {
1124 return this.denominatorCong;
1125 }
1126
1127
1128
1129
1130
1131 public double getDenominatorFree()
1132 {
1133 return this.denominatorFree;
1134 }
1135
1136 @Override
1137 public String toString()
1138 {
1139 return "DualWeightedMean [numeratorCong=" + this.numeratorCong + ", numeratorFree=" + this.numeratorFree
1140 + ", denominatorCong=" + this.denominatorCong + ", denominatorFree=" + this.denominatorFree + "]";
1141 }
1142
1143 }
1144
1145
1146
1147
1148 private final class WeightedMean
1149 {
1150
1151 private double numerator;
1152
1153
1154 private double denominator;
1155
1156
1157
1158
1159
1160
1161 public void add(final double value, final double weight)
1162 {
1163 this.numerator += value * weight;
1164 this.denominator += weight;
1165 }
1166
1167
1168
1169
1170
1171 public double get()
1172 {
1173 return this.numerator / this.denominator;
1174 }
1175
1176 @Override
1177 public String toString()
1178 {
1179 return "WeightedMean [numerator=" + this.numerator + ", denominator=" + this.denominator + "]";
1180 }
1181
1182 }
1183
1184 @Override
1185 public String toString()
1186 {
1187 return "EGTF [kernel=" + this.kernel + ", cCong=" + this.cCong + ", cFree=" + this.cFree + ", deltaV=" + this.deltaV
1188 + ", vc=" + this.vc + ", dataSources=" + this.dataSources + ", data=" + this.data + ", interrupted="
1189 + this.interrupted + ", listeners=" + this.listeners + "]";
1190 }
1191
1192 }