java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CourseChangeBasedTrackApproximationTest.java
... ...
@@ -1,7 +1,15 @@
1 1
package com.sap.sailing.domain.test;
2 2
3
+import static org.junit.jupiter.api.Assertions.assertEquals;
4
+import static org.junit.jupiter.api.Assertions.assertFalse;
3 5
import static org.junit.jupiter.api.Assertions.assertTrue;
4 6
7
+import java.io.File;
8
+import java.net.MalformedURLException;
9
+import java.net.URI;
10
+import java.net.URISyntaxException;
11
+import java.net.URL;
12
+
5 13
import org.junit.jupiter.api.BeforeEach;
6 14
import org.junit.jupiter.api.Test;
7 15
... ...
@@ -15,24 +23,80 @@ import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
15 23
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
16 24
import com.sap.sailing.domain.tracking.impl.CourseChangeBasedTrackApproximation;
17 25
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
26
+import com.sap.sailing.domain.tractracadapter.ReceiverType;
18 27
import com.sap.sse.common.TimePoint;
19 28
import com.sap.sse.common.Util;
20 29
import com.sap.sse.common.impl.DegreeBearingImpl;
21 30
import com.sap.sse.common.impl.MillisecondsDurationImpl;
22 31
import com.sap.sse.common.impl.MillisecondsTimePoint;
23 32
24
-public class CourseChangeBasedTrackApproximationTest {
33
+public class CourseChangeBasedTrackApproximationTest extends OnlineTracTracBasedTest {
25 34
private DynamicGPSFixTrack<Competitor, GPSFixMoving> track;
26 35
private CourseChangeBasedTrackApproximation approximation;
27 36
37
+ public CourseChangeBasedTrackApproximationTest() throws MalformedURLException, URISyntaxException {
38
+ super();
39
+ }
40
+
28 41
@BeforeEach
29
- public void setUp() throws InterruptedException {
42
+ public void setUp() throws Exception {
43
+ super.setUp();
44
+ URI storedUri = new URI("file:///" + new File("resources/event_20110609_KielerWoch-505_Race_2.mtb").getCanonicalPath().replace('\\', '/'));
45
+ super.setUp(
46
+ new URL("file:///" + new File("resources/event_20110609_KielerWoch-505_Race_2.txt").getCanonicalPath()),
47
+ /* liveUri */ null, /* storedUri */ storedUri,
48
+ new ReceiverType[] { ReceiverType.RACECOURSE, ReceiverType.RAWPOSITIONS });
49
+ assertFalse(Util.isEmpty(getTrackedRace().getRace().getCompetitors()));
50
+ final DynamicGPSFixTrack<Competitor, GPSFixMoving> sampleTrack = getTrackedRace().getTrack(getTrackedRace().getRace().getCompetitors().iterator().next());
51
+ sampleTrack.lockForRead();
52
+ try {
53
+ assertFalse(Util.isEmpty(sampleTrack.getRawFixes()));
54
+ } finally {
55
+ sampleTrack.unlockAfterRead();
56
+ }
30 57
final CompetitorWithBoat competitor = TrackBasedTest.createCompetitorWithBoat("Someone");
31 58
track = new DynamicGPSFixMovingTrackImpl<Competitor>(competitor,
32 59
/* millisecondsOverWhichToAverage */5000, /* lossless compaction */true);
33 60
approximation = new CourseChangeBasedTrackApproximation(track, competitor.getBoat().getBoatClass());
34 61
}
35 62
63
+ /**
64
+ * During the work on bug5959 (https://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=5959) we identified an issue
65
+ * with early vs. late initialization of the approximation. When initializing the
66
+ * {@link CourseChangeBasedTrackApproximation} objects before adding any fixes to the track, we received different
67
+ * results than when initializing it after adding fixes and first trying to compute maneuvers. This goes against the
68
+ * specification that the approximation should be independent of when it is initialized.<p>
69
+ *
70
+ * To conduct the test, we take the fixes from a loaded competitor track, create a new {@link DynamicGPSFixMovingTrackImpl},
71
+ * construct a {@link CourseChangeBasedTrackApproximation} based on this track (early initialization), then copy all fixes
72
+ * from the loaded competitor track to the new test track (which adds these fixes to the approximation), then create a second
73
+ * {@link CourseChangeBasedTrackApproximation} which will then add all fixes in one sweep. Then, we compare the results of
74
+ * {@link CourseChangeBasedTrackApproximation#approximate(TimePoint, TimePoint)} for the duration of the entire track,
75
+ * expecting them to be equal.
76
+ */
77
+ @Test
78
+ public void testNoDiffBetweenEarlyAndLateInitialization() {
79
+ final CompetitorWithBoat sampleCompetitor = (CompetitorWithBoat) getTrackedRace().getRace().getCompetitors().iterator().next();
80
+ final DynamicGPSFixTrack<Competitor, GPSFixMoving> sampleTrack = getTrackedRace().getTrack(sampleCompetitor);
81
+ final DynamicGPSFixTrack<Competitor, GPSFixMoving> trackCopy = new DynamicGPSFixMovingTrackImpl<Competitor>(sampleCompetitor, /* millisecondsOverWhichToAverage */ 15000);
82
+ final CourseChangeBasedTrackApproximation earlyInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass());
83
+ final TimePoint from = sampleTrack.getFirstRawFix().getTimePoint();
84
+ final TimePoint to = sampleTrack.getLastRawFix().getTimePoint();
85
+ sampleTrack.lockForRead();
86
+ try {
87
+ for (final GPSFixMoving fix : sampleTrack.getRawFixes()) {
88
+ trackCopy.add(fix);
89
+ }
90
+ } finally {
91
+ sampleTrack.unlockAfterRead();
92
+ }
93
+ final CourseChangeBasedTrackApproximation lateInitApproximation = new CourseChangeBasedTrackApproximation(trackCopy, sampleCompetitor.getBoat().getBoatClass());
94
+ final Iterable<GPSFixMoving> earlyInitResult = earlyInitApproximation.approximate(from, to);
95
+ final Iterable<GPSFixMoving> lateInitResult = lateInitApproximation.approximate(from, to);
96
+ assertEquals(Util.size(earlyInitResult), Util.size(lateInitResult));
97
+ assertEquals(Util.asSet(earlyInitResult), Util.asSet(lateInitResult));
98
+ }
99
+
36 100
@Test
37 101
public void simpleTackRecognition() {
38 102
final GPSFixMoving start = fix(10000l, 0, 0, 5, 0);
... ...
@@ -51,7 +115,6 @@ public class CourseChangeBasedTrackApproximationTest {
51 115
for (int i=0; i<20; i++) {
52 116
track.add(next = travel(next, 1000 /*ms*/, 5 /* knots */, 270 /*deg COG*/));
53 117
}
54
-
55 118
Iterable<GPSFixMoving> candidates = approximation.approximate(start.getTimePoint(), next.getTimePoint());
56 119
assertTrue(Util.size(candidates) >= 1);
57 120
for (final GPSFixMoving candidate : candidates) {
... ...
@@ -68,5 +131,4 @@ public class CourseChangeBasedTrackApproximationTest {
68 131
return new GPSFixMovingImpl(fix.getPosition().translateGreatCircle(new DegreeBearingImpl(cogDeg), new KnotSpeedImpl(speedInKnots).travel(new MillisecondsDurationImpl(durationInMillis))),
69 132
fix.getTimePoint().plus(durationInMillis), new KnotSpeedWithBearingImpl(speedInKnots, new DegreeBearingImpl(cogDeg)), /* optionalTrueHeading */ null);
70 133
}
71
-
72 134
}
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java
... ...
@@ -743,7 +743,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
743 743
}
744 744
745 745
@Override
746
- public List<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to) {
746
+ public List<GPSFixMoving> approximate(Competitor competitor, TimePoint from, TimePoint to) {
747 747
return null;
748 748
}
749 749
java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java
... ...
@@ -371,7 +371,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
371 371
}
372 372
373 373
@Override
374
- public List<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to) {
374
+ public List<GPSFixMoving> approximate(Competitor competitor, TimePoint from, TimePoint to) {
375 375
return null;
376 376
}
377 377
java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ApproximatedFixesCalculatorImpl.java
... ...
@@ -19,9 +19,8 @@ public class ApproximatedFixesCalculatorImpl implements ApproximatedFixesCalcula
19 19
@Override
20 20
public Iterable<GPSFixMoving> approximate(TimePoint earliestStart, TimePoint latestEnd) {
21 21
return trackedRace.approximate(competitor,
22
- trackedRace.getRace().getBoatOfCompetitor(competitor).getBoatClass()
23
- .getMaximumDistanceForCourseApproximation(),
24
- earliestStart, latestEnd);
22
+ earliestStart,
23
+ latestEnd);
25 24
}
26 25
27 26
}
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DummyTrackedRace.java
... ...
@@ -315,7 +315,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
315 315
}
316 316
317 317
@Override
318
- public List<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to) {
318
+ public List<GPSFixMoving> approximate(Competitor competitor, TimePoint from, TimePoint to) {
319 319
return null;
320 320
}
321 321
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java
... ...
@@ -659,7 +659,7 @@ public interface TrackedRace
659 659
* If the precondition that the {@code competitor} must be {@link RaceDefinition#getCompetitors() part of} the
660 660
* {@link #getRace() race} isn't met, a {@code NullPointerException} will result.
661 661
*/
662
- Iterable<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to);
662
+ Iterable<GPSFixMoving> approximate(Competitor competitor, TimePoint from, TimePoint to);
663 663
664 664
/**
665 665
* @return a non-<code>null</code> but perhaps empty list of the maneuvers that <code>competitor</code> performed in
java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
... ...
@@ -343,8 +343,8 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
343 343
private transient SmartFutureCache<Competitor, List<Maneuver>, EmptyUpdateInterval> maneuverCache;
344 344
345 345
/**
346
- * The values of this map are used by the {@link #approximate(Competitor, Distance, TimePoint, TimePoint)} method and
347
- * maintain state to accelerate the {@link #approximate(Competitor, Distance, TimePoint, TimePoint)} method, also in
346
+ * The values of this map are used by the {@link #approximate(Competitor, TimePoint, TimePoint)} method and
347
+ * maintain state to accelerate the {@link #approximate(Competitor, TimePoint, TimePoint)} method, also in
348 348
* live scenarios when the contents of the competitors' {@link #tracks} changes dynamically.
349 349
*/
350 350
private final Map<Competitor, CourseChangeBasedTrackApproximation> maneuverApproximators;
... ...
@@ -2880,10 +2880,10 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
2880 2880
}
2881 2881
2882 2882
@Override
2883
- public Iterable<GPSFixMoving> approximate(Competitor competitor, Distance maxDistance, TimePoint from, TimePoint to) {
2883
+ public Iterable<GPSFixMoving> approximate(Competitor competitor, TimePoint from, TimePoint to) {
2884 2884
return maneuverApproximators.get(competitor).approximate(from, to);
2885 2885
}
2886
-
2886
+
2887 2887
protected void triggerManeuverCacheRecalculationForAllCompetitors() {
2888 2888
if (cachesSuspended) {
2889 2889
triggerManeuverCacheInvalidationForAllCompetitors = true;
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java
... ...
@@ -211,7 +211,7 @@ public interface SailingService extends RemoteService, RemoteReplicationService
211 211
SwissTimingEventRecordDTO getRacesOfSwissTimingEvent(String eventJsonURL) throws UnauthorizedException, Exception;
212 212
213 213
Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> getDouglasPoints(
214
- RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, TimeRange> competitorTimeRanges, double meters)
214
+ RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, TimeRange> competitorTimeRanges)
215 215
throws NoWindException, UnauthorizedException;
216 216
217 217
Map<CompetitorDTO, List<ManeuverDTO>> getManeuvers(RegattaAndRaceIdentifier raceIdentifier,
java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java
... ...
@@ -206,7 +206,6 @@ import com.sap.sailing.domain.common.dto.TrackedRaceDTO;
206 206
import com.sap.sailing.domain.common.impl.KilometersPerHourSpeedImpl;
207 207
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
208 208
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
209
-import com.sap.sailing.domain.common.impl.MeterDistance;
210 209
import com.sap.sailing.domain.common.impl.WindSourceImpl;
211 210
import com.sap.sailing.domain.common.media.MediaTrack;
212 211
import com.sap.sailing.domain.common.orc.ImpliedWindSource;
... ...
@@ -2809,13 +2808,12 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
2809 2808
2810 2809
@Override
2811 2810
public Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> getDouglasPoints(
2812
- RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, TimeRange> competitorTimeRanges, double meters)
2811
+ RegattaAndRaceIdentifier raceIdentifier, Map<CompetitorDTO, TimeRange> competitorTimeRanges)
2813 2812
throws NoWindException {
2814 2813
final Map<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> result = new HashMap<>();
2815 2814
final TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
2816 2815
getSecurityService().checkCurrentUserReadPermission(trackedRace);
2817 2816
if (trackedRace != null) {
2818
- final MeterDistance maxDistance = new MeterDistance(meters);
2819 2817
for (Competitor competitor : trackedRace.getRace().getCompetitors()) {
2820 2818
final CompetitorDTO competitorDTO = baseDomainFactory.convertToCompetitorDTO(competitor);
2821 2819
if (competitorTimeRanges.containsKey(competitorDTO)) {
... ...
@@ -2823,8 +2821,8 @@ public class SailingServiceImpl extends ResultCachingProxiedRemoteServiceServlet
2823 2821
final GPSFixTrack<Competitor, GPSFixMoving> gpsFixTrack = trackedRace.getTrack(competitor);
2824 2822
// Distance for DouglasPeucker
2825 2823
final TimeRange timeRange = competitorTimeRanges.get(competitorDTO);
2826
- final Iterable<GPSFixMoving> gpsFixApproximation = trackedRace.approximate(competitor, maxDistance,
2827
- timeRange.from(), timeRange.to());
2824
+ final Iterable<GPSFixMoving> gpsFixApproximation = trackedRace.approximate(competitor, timeRange.from(),
2825
+ timeRange.to());
2828 2826
final List<GPSFixDTOWithSpeedWindTackAndLegType> gpsFixDouglasList = new ArrayList<>();
2829 2827
GPSFix fix = null;
2830 2828
for (GPSFix next : gpsFixApproximation) {
java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTracTrac.java
... ...
@@ -216,7 +216,7 @@ public class PathGeneratorTracTrac extends PathGeneratorBase {
216 216
Wind endWind = trackedRace.getWind(endPosition, endTime);
217 217
this.raceCourse.addLast(new TimedPositionWithSpeedImpl(endTime, endPosition, endWind));
218 218
219
- Iterable<GPSFixMoving> gpsFixes = trackedRace.approximate(competitor, maxDistance, startTime, endTime);
219
+ Iterable<GPSFixMoving> gpsFixes = trackedRace.approximate(competitor, startTime, endTime);
220 220
Iterator<GPSFixMoving> gpsIter = gpsFixes.iterator();
221 221
222 222
while (gpsIter.hasNext()) {