mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-22 20:13:52 +00:00
Merge remote branch 'origin/bug3932'
This commit is contained in:
commit
311f670b27
15 files changed
+341
-97
No files matched your search
+4
-1
@@ -11,6 +11,7 @@ import java.util.Collections;
|
|||||||
|
|
||||||
import org.junit.Before;
|
import org.junit.Before;
|
||||||
import org.junit.Test;
|
import org.junit.Test;
|
||||||
|
import org.mockito.Matchers;
|
||||||
|
|
||||||
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
|
||||||
import com.sap.sailing.domain.base.Competitor;
|
import com.sap.sailing.domain.base.Competitor;
|
||||||
@@ -27,6 +28,7 @@ import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
|
|||||||
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
import com.sap.sailing.domain.ranking.OneDesignRankingMetric;
|
||||||
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
||||||
import com.sap.sailing.domain.tracking.LineDetails;
|
import com.sap.sailing.domain.tracking.LineDetails;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
|
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
|
||||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||||
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
|
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
|
||||||
@@ -137,7 +139,6 @@ public class StarbordSideOfStartLineRecognitionTest {
|
|||||||
Position startStarboardPosition = new DegreePosition(0, 1);
|
Position startStarboardPosition = new DegreePosition(0, 1);
|
||||||
Position windwardPosition = new DegreePosition(10, 0.5);
|
Position windwardPosition = new DegreePosition(10, 0.5);
|
||||||
MockedTrackedRaceImpl trackedRace = createTrackedRaceWithMarkPositions(startPortPosition, startStarboardPosition, windwardPosition);
|
MockedTrackedRaceImpl trackedRace = createTrackedRaceWithMarkPositions(startPortPosition, startStarboardPosition, windwardPosition);
|
||||||
|
|
||||||
Position p = trackedRace.getOrCreateTrack(trackedRace.getStartLine(now).getStarboardMarkWhileApproachingLine()).
|
Position p = trackedRace.getOrCreateTrack(trackedRace.getStartLine(now).getStarboardMarkWhileApproachingLine()).
|
||||||
getEstimatedPosition(now, /* extrapolate */ false);
|
getEstimatedPosition(now, /* extrapolate */ false);
|
||||||
assertEquals(startStarboardPosition, p);
|
assertEquals(startStarboardPosition, p);
|
||||||
@@ -184,6 +185,8 @@ public class StarbordSideOfStartLineRecognitionTest {
|
|||||||
when(trackedRace.getTrackedLeg(course.getFirstLeg())).thenReturn(trackedLeg);
|
when(trackedRace.getTrackedLeg(course.getFirstLeg())).thenReturn(trackedLeg);
|
||||||
when(trackedRace.getApproximatePosition(startWaypoint, now)).thenCallRealMethod();
|
when(trackedRace.getApproximatePosition(startWaypoint, now)).thenCallRealMethod();
|
||||||
when(trackedRace.getApproximatePosition(windwardWaypoint, now)).thenCallRealMethod();
|
when(trackedRace.getApproximatePosition(windwardWaypoint, now)).thenCallRealMethod();
|
||||||
|
when(trackedRace.getApproximatePosition(Matchers.eq(startWaypoint), Matchers.eq(now), Matchers.any(MarkPositionAtTimePointCache.class))).thenCallRealMethod();
|
||||||
|
when(trackedRace.getApproximatePosition(Matchers.eq(windwardWaypoint), Matchers.eq(now), Matchers.any(MarkPositionAtTimePointCache.class))).thenCallRealMethod();
|
||||||
return trackedRace;
|
return trackedRace;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+13
-11
@@ -37,8 +37,10 @@ import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
|||||||
import com.sap.sailing.domain.common.impl.WindImpl;
|
import com.sap.sailing.domain.common.impl.WindImpl;
|
||||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||||
import com.sap.sailing.domain.polars.PolarDataService;
|
import com.sap.sailing.domain.polars.PolarDataService;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||||
|
import com.sap.sailing.domain.tracking.impl.MarkPositionAtTimePointCacheImpl;
|
||||||
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
|
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
|
||||||
import com.sap.sse.common.Duration;
|
import com.sap.sse.common.Duration;
|
||||||
import com.sap.sse.common.TimePoint;
|
import com.sap.sse.common.TimePoint;
|
||||||
@@ -87,9 +89,8 @@ public class TargetTimeEstimationTest {
|
|||||||
TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors);
|
TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors);
|
||||||
|
|
||||||
//Actual test of functionality
|
//Actual test of functionality
|
||||||
Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint);
|
Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint, new MarkPositionAtTimePointCacheImpl(trackedRace, timepoint));
|
||||||
assertEquals(75494, duration.asMillis(), 100);
|
assertEquals(75494, duration.asMillis(), 100);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -123,9 +124,10 @@ public class TargetTimeEstimationTest {
|
|||||||
when(leg.getFrom()).thenReturn(from);
|
when(leg.getFrom()).thenReturn(from);
|
||||||
when(leg.getTo()).thenReturn(to);
|
when(leg.getTo()).thenReturn(to);
|
||||||
|
|
||||||
when(trackedRace.getApproximatePosition(from, timepoint)).thenReturn(startOfLeg);
|
when(trackedRace.getApproximatePosition(eq(from), eq(timepoint))).thenReturn(startOfLeg);
|
||||||
when(trackedRace.getApproximatePosition(to, timepoint)).thenReturn(endOfLeg);
|
when(trackedRace.getApproximatePosition(eq(to), eq(timepoint))).thenReturn(endOfLeg);
|
||||||
|
when(trackedRace.getApproximatePosition(eq(from), eq(timepoint), any(MarkPositionAtTimePointCache.class))).thenReturn(startOfLeg);
|
||||||
|
when(trackedRace.getApproximatePosition(eq(to), eq(timepoint), any(MarkPositionAtTimePointCache.class))).thenReturn(endOfLeg);
|
||||||
|
|
||||||
SpeedWithBearing speedWithBearingPort = new KnotSpeedWithBearingImpl(6, new DegreeBearingImpl(-45));
|
SpeedWithBearing speedWithBearingPort = new KnotSpeedWithBearingImpl(6, new DegreeBearingImpl(-45));
|
||||||
SpeedWithBearingWithConfidence<Void> boatSpeedWithBearingWithConfidencePort = new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearingPort, 1, null);
|
SpeedWithBearingWithConfidence<Void> boatSpeedWithBearingWithConfidencePort = new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearingPort, 1, null);
|
||||||
@@ -139,9 +141,8 @@ public class TargetTimeEstimationTest {
|
|||||||
TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors);
|
TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors);
|
||||||
|
|
||||||
//Actual test of functionality
|
//Actual test of functionality
|
||||||
Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint);
|
Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint, new MarkPositionAtTimePointCacheImpl(trackedRace, timepoint));
|
||||||
assertEquals(213513, duration.asMillis(), 100);
|
assertEquals(213513, duration.asMillis(), 100);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -175,9 +176,10 @@ public class TargetTimeEstimationTest {
|
|||||||
when(leg.getFrom()).thenReturn(from);
|
when(leg.getFrom()).thenReturn(from);
|
||||||
when(leg.getTo()).thenReturn(to);
|
when(leg.getTo()).thenReturn(to);
|
||||||
|
|
||||||
when(trackedRace.getApproximatePosition(from, timepoint)).thenReturn(startOfLeg);
|
when(trackedRace.getApproximatePosition(eq(from), eq(timepoint))).thenReturn(startOfLeg);
|
||||||
when(trackedRace.getApproximatePosition(to, timepoint)).thenReturn(endOfLeg);
|
when(trackedRace.getApproximatePosition(eq(to), eq(timepoint))).thenReturn(endOfLeg);
|
||||||
|
when(trackedRace.getApproximatePosition(eq(from), eq(timepoint), any(MarkPositionAtTimePointCache.class))).thenReturn(startOfLeg);
|
||||||
|
when(trackedRace.getApproximatePosition(eq(to), eq(timepoint), any(MarkPositionAtTimePointCache.class))).thenReturn(endOfLeg);
|
||||||
|
|
||||||
SpeedWithBearing speedWithBearingPort = new KnotSpeedWithBearingImpl(11, new DegreeBearingImpl(150));
|
SpeedWithBearing speedWithBearingPort = new KnotSpeedWithBearingImpl(11, new DegreeBearingImpl(150));
|
||||||
SpeedWithBearingWithConfidence<Void> boatSpeedWithBearingWithConfidencePort = new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearingPort, 1, null);
|
SpeedWithBearingWithConfidence<Void> boatSpeedWithBearingWithConfidencePort = new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearingPort, 1, null);
|
||||||
@@ -191,7 +193,7 @@ public class TargetTimeEstimationTest {
|
|||||||
TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors);
|
TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors);
|
||||||
|
|
||||||
//Actual test of functionality
|
//Actual test of functionality
|
||||||
Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint);
|
Duration duration = trackedLeg.getEstimatedTimeToComplete(mockedPolars, timepoint, new MarkPositionAtTimePointCacheImpl(trackedRace, timepoint));
|
||||||
assertEquals(95090, duration.asMillis(), 100);
|
assertEquals(95090, duration.asMillis(), 100);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
+2
@@ -22,6 +22,7 @@ import com.sap.sailing.domain.common.impl.DegreePosition;
|
|||||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||||
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
|
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
|
||||||
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
|
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl;
|
||||||
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
|
||||||
@@ -50,6 +51,7 @@ public class TrackedRaceCenterTest {
|
|||||||
trackedRace = mock(DynamicTrackedRaceImpl.class);
|
trackedRace = mock(DynamicTrackedRaceImpl.class);
|
||||||
when(trackedRace.getCenterOfCourse(Matchers.any(TimePoint.class))).thenCallRealMethod();
|
when(trackedRace.getCenterOfCourse(Matchers.any(TimePoint.class))).thenCallRealMethod();
|
||||||
when(trackedRace.getApproximatePosition(Matchers.any(Waypoint.class), Matchers.any(TimePoint.class))).thenCallRealMethod();
|
when(trackedRace.getApproximatePosition(Matchers.any(Waypoint.class), Matchers.any(TimePoint.class))).thenCallRealMethod();
|
||||||
|
when(trackedRace.getApproximatePosition(Matchers.any(Waypoint.class), Matchers.any(TimePoint.class), Matchers.any(MarkPositionAtTimePointCache.class))).thenCallRealMethod();
|
||||||
RaceDefinition race = mock(RaceDefinition.class);
|
RaceDefinition race = mock(RaceDefinition.class);
|
||||||
when(trackedRace.getRace()).thenReturn(race);
|
when(trackedRace.getRace()).thenReturn(race);
|
||||||
course = mock(Course.class);
|
course = mock(Course.class);
|
||||||
|
|||||||
+2
-1
@@ -63,6 +63,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
|
|||||||
import com.sap.sailing.domain.tracking.LineDetails;
|
import com.sap.sailing.domain.tracking.LineDetails;
|
||||||
import com.sap.sailing.domain.tracking.Maneuver;
|
import com.sap.sailing.domain.tracking.Maneuver;
|
||||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
||||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||||
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
||||||
@@ -612,7 +613,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
|
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -42,6 +42,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
|
|||||||
import com.sap.sailing.domain.tracking.LineDetails;
|
import com.sap.sailing.domain.tracking.LineDetails;
|
||||||
import com.sap.sailing.domain.tracking.Maneuver;
|
import com.sap.sailing.domain.tracking.Maneuver;
|
||||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
||||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||||
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
||||||
@@ -218,7 +219,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
|
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+79
-70
@@ -39,6 +39,8 @@ import com.sap.sailing.domain.markpassingcalculation.CandidateFinder;
|
|||||||
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
|
||||||
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
|
||||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
|
import com.sap.sailing.domain.tracking.impl.MarkPositionAtTimePointCacheImpl;
|
||||||
import com.sap.sailing.domain.tracking.impl.TimedComparator;
|
import com.sap.sailing.domain.tracking.impl.TimedComparator;
|
||||||
import com.sap.sse.common.Duration;
|
import com.sap.sse.common.Duration;
|
||||||
import com.sap.sse.common.TimePoint;
|
import com.sap.sse.common.TimePoint;
|
||||||
@@ -611,15 +613,16 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
if (Math.abs(dis.getMeters()) < Math.abs(disBefore.getMeters())
|
if (Math.abs(dis.getMeters()) < Math.abs(disBefore.getMeters())
|
||||||
&& Math.abs(dis.getMeters()) < Math.abs(disAfter.getMeters())) {
|
&& Math.abs(dis.getMeters()) < Math.abs(disAfter.getMeters())) {
|
||||||
t = fix.getTimePoint();
|
t = fix.getTimePoint();
|
||||||
|
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(race, t);
|
||||||
p = fix.getPosition();
|
p = fix.getPosition();
|
||||||
Double newProbability = getDistanceBasedProbability(w, t, dis);
|
Double newProbability = getDistanceBasedProbability(w, t, dis, markPositionCache);
|
||||||
if (newProbability != null) {
|
if (newProbability != null) {
|
||||||
// FIXME why not generate the candidate here where we have all information at hand?
|
// FIXME why not generate the candidate here where we have all information at hand?
|
||||||
final double newOnCorrectSideOfWaypointPenalty = getSidePenalty(w, p, t, portMark);
|
final double newOnCorrectSideOfWaypointPenalty = getSidePenalty(w, p, t, portMark, markPositionCache);
|
||||||
newProbability *= newOnCorrectSideOfWaypointPenalty * PENALTY_FOR_DISTANCE_CANDIDATES;
|
newProbability *= newOnCorrectSideOfWaypointPenalty * PENALTY_FOR_DISTANCE_CANDIDATES;
|
||||||
final Double newStartProbabilityBasedOnOtherCompetitors;
|
final Double newStartProbabilityBasedOnOtherCompetitors;
|
||||||
if (isStartWaypoint(w)) {
|
if (isStartWaypoint(w)) {
|
||||||
newStartProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t);
|
newStartProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t, markPositionCache);
|
||||||
if (newStartProbabilityBasedOnOtherCompetitors != null) {
|
if (newStartProbabilityBasedOnOtherCompetitors != null) {
|
||||||
newProbability *= newStartProbabilityBasedOnOtherCompetitors;
|
newProbability *= newStartProbabilityBasedOnOtherCompetitors;
|
||||||
}
|
}
|
||||||
@@ -670,6 +673,7 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
private Map<Waypoint, List<Distance>> getDistances(Competitor c, GPSFix fix) {
|
private Map<Waypoint, List<Distance>> getDistances(Competitor c, GPSFix fix) {
|
||||||
// TODO Possibly for specific waypoints
|
// TODO Possibly for specific waypoints
|
||||||
Map<Waypoint, List<Distance>> result = distanceCache.get(c).get(fix);
|
Map<Waypoint, List<Distance>> result = distanceCache.get(c).get(fix);
|
||||||
|
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(race, fix.getTimePoint());
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
// Else calculate distances and put them into the cache
|
// Else calculate distances and put them into the cache
|
||||||
result = new LinkedHashMap<>();
|
result = new LinkedHashMap<>();
|
||||||
@@ -677,7 +681,7 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
course.lockForRead();
|
course.lockForRead();
|
||||||
try {
|
try {
|
||||||
for (Waypoint w : course.getWaypoints()) {
|
for (Waypoint w : course.getWaypoints()) {
|
||||||
List<Distance> distances = calculateDistance(fix.getPosition(), w, fix.getTimePoint());
|
List<Distance> distances = calculateDistance(fix.getPosition(), w, fix.getTimePoint(), markPositionCache);
|
||||||
result.put(w, distances);
|
result.put(w, distances);
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
@@ -818,13 +822,14 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
result = new HashMap<>();
|
result = new HashMap<>();
|
||||||
Position p = fix.getPosition();
|
Position p = fix.getPosition();
|
||||||
TimePoint t = fix.getTimePoint();
|
TimePoint t = fix.getTimePoint();
|
||||||
|
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(race, t);
|
||||||
Course course = race.getRace().getCourse();
|
Course course = race.getRace().getCourse();
|
||||||
course.lockForRead();
|
course.lockForRead();
|
||||||
try {
|
try {
|
||||||
for (Waypoint w : course.getWaypoints()) {
|
for (Waypoint w : course.getWaypoints()) {
|
||||||
List<Distance> distances = new ArrayList<>();
|
List<Distance> distances = new ArrayList<>();
|
||||||
result.put(w, distances);
|
result.put(w, distances);
|
||||||
for (Util.Pair<Position, Bearing> crossingInfo : getCrossingInformation(w, t)) {
|
for (Util.Pair<Position, Bearing> crossingInfo : getCrossingInformation(w, t, markPositionCache)) {
|
||||||
if (crossingInfo.getA() != null && crossingInfo.getB() != null) {
|
if (crossingInfo.getA() != null && crossingInfo.getB() != null) {
|
||||||
distances.add(p.crossTrackError(crossingInfo.getA(), crossingInfo.getB()));
|
distances.add(p.crossTrackError(crossingInfo.getA(), crossingInfo.getB()));
|
||||||
}
|
}
|
||||||
@@ -838,20 +843,6 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
|
||||||
* Calculates the cross-track error of each fix to the position and crossing bearing of each waypoint. Gates have
|
|
||||||
* two of these and lines always go from the port mark to the starboard mark.
|
|
||||||
*
|
|
||||||
*
|
|
||||||
* /* private void calculateCrossTrackErrors(Competitor c, Iterable<GPSFix> fixes, Iterable<Waypoint>
|
|
||||||
* waypointsToCalculate) { for (GPSFix fix : fixes) { Position fixPos = fix.getPosition(); TimePoint t =
|
|
||||||
* fix.getTimePoint(); Map<Waypoint, List<Double>> waypointXTE = new HashMap<Waypoint, List<Double>>();
|
|
||||||
* crossTrackErrors.get(c).put(fix, waypointXTE); for (Waypoint w : race.getRace().getCourse().getWaypoints()) {
|
|
||||||
* List<Double> xte = new ArrayList<>(); waypointXTE.put(w, xte); for (Util.Pair<Position, Bearing> crossingInfo :
|
|
||||||
* getCrossingInformation(w, t)) { xte.add(fixPos.crossTrackError(crossingInfo.getA(),
|
|
||||||
* crossingInfo.getB()).getMeters()); } } } }
|
|
||||||
*/
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* {@code xte1} and {@code xte2} are expected to have different signums or both be 0. The method
|
* {@code xte1} and {@code xte2} are expected to have different signums or both be 0. The method
|
||||||
* interpolates between the time points {@code t1} and {@code t2} according to where the XTE is
|
* interpolates between the time points {@code t1} and {@code t2} according to where the XTE is
|
||||||
@@ -863,10 +854,11 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
final double ratio = (Math.abs(xte1) / (Math.abs(xte1) + Math.abs(xte2)));
|
final double ratio = (Math.abs(xte1) / (Math.abs(xte1) + Math.abs(xte2)));
|
||||||
final TimePoint t = t1.plus((long) (differenceInMillis * ratio));
|
final TimePoint t = t1.plus((long) (differenceInMillis * ratio));
|
||||||
final Position p = race.getTrack(c).getEstimatedPosition(t, false);
|
final Position p = race.getTrack(c).getEstimatedPosition(t, false);
|
||||||
final List<Distance> distances = calculateDistance(p, w, t);
|
final List<Distance> distances = calculateDistance(p, w, t, new MarkPositionAtTimePointCacheImpl(race, t));
|
||||||
final Distance d = portMark ? distances.get(0) : distances.get(1);
|
final Distance d = portMark ? distances.get(0) : distances.get(1);
|
||||||
final double sidePenalty = getSidePenalty(w, p, t, portMark);
|
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(race, t);
|
||||||
double probability = getDistanceBasedProbability(w, t, d) * sidePenalty;
|
final double sidePenalty = getSidePenalty(w, p, t, portMark, markPositionCache);
|
||||||
|
double probability = getDistanceBasedProbability(w, t, d, markPositionCache) * sidePenalty;
|
||||||
final Double passesInTheRightDirectionProbability = passesInTheRightDirection(w, xte1, xte2, portMark);
|
final Double passesInTheRightDirectionProbability = passesInTheRightDirection(w, xte1, xte2, portMark);
|
||||||
// null would mean "unknown"; no penalty for those cases
|
// null would mean "unknown"; no penalty for those cases
|
||||||
probability = passesInTheRightDirectionProbability == null ? probability : probability * passesInTheRightDirectionProbability;
|
probability = passesInTheRightDirectionProbability == null ? probability : probability * passesInTheRightDirectionProbability;
|
||||||
@@ -876,7 +868,7 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
// at time point t the other competitors are largely not even close to the start waypoint;
|
// at time point t the other competitors are largely not even close to the start waypoint;
|
||||||
// this will make start candidates much more likely if many other competitors are also very close to the
|
// this will make start candidates much more likely if many other competitors are also very close to the
|
||||||
// start, and it will help ruling out those candidates where someone is practicing a start just for themselves
|
// start, and it will help ruling out those candidates where someone is practicing a start just for themselves
|
||||||
startProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t);
|
startProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t, markPositionCache);
|
||||||
if (startProbabilityBasedOnOtherCompetitors != null) {
|
if (startProbabilityBasedOnOtherCompetitors != null) {
|
||||||
probability *= startProbabilityBasedOnOtherCompetitors;
|
probability *= startProbabilityBasedOnOtherCompetitors;
|
||||||
}
|
}
|
||||||
@@ -939,9 +931,9 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
* A low variance with a positive average (meaning on course side) indicates that everyone else may have started around the
|
* A low variance with a positive average (meaning on course side) indicates that everyone else may have started around the
|
||||||
* same time, making this a probable late starter's candidate. Therefore, low variance with positive average shall increase
|
* same time, making this a probable late starter's candidate. Therefore, low variance with positive average shall increase
|
||||||
* a probability that was low because of a large weighted average distance.
|
* a probability that was low because of a large weighted average distance.
|
||||||
*
|
|
||||||
* @param t
|
* @param t
|
||||||
* the time point at which to consider the other competitors behavior relative to the start waypoint
|
* the time point at which to consider the other competitors behavior relative to the start waypoint
|
||||||
|
*
|
||||||
* @return {@code null} if the {@link #race} has a gate start or the start time is known; a probability between 0..1
|
* @return {@code null} if the {@link #race} has a gate start or the start time is known; a probability between 0..1
|
||||||
* (inclusive) where 0 means that based on all but {@code c}'s relation to the start line it seems
|
* (inclusive) where 0 means that based on all but {@code c}'s relation to the start line it seems
|
||||||
* completely unlikely that a candidate for {@code c} at time {@code t} could have been a start candidate; 1
|
* completely unlikely that a candidate for {@code c} at time {@code t} could have been a start candidate; 1
|
||||||
@@ -949,11 +941,13 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
* a fact that this must have been the start.
|
* a fact that this must have been the start.
|
||||||
*
|
*
|
||||||
*/
|
*/
|
||||||
private Double getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(Competitor c, TimePoint t) {
|
private Double getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(Competitor c, TimePoint t, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert t.equals(markPositionCache.getTimePoint());
|
||||||
|
assert race == markPositionCache.getTrackedRace();
|
||||||
final Double result;
|
final Double result;
|
||||||
if (race.getStartOfRace(/* inferred */ false) == null && race.isGateStart() != Boolean.TRUE) {
|
if (race.getStartOfRace(/* inferred */ false) == null && race.isGateStart() != Boolean.TRUE) {
|
||||||
final Waypoint start = race.getRace().getCourse().getFirstWaypoint();
|
final Waypoint start = race.getRace().getCourse().getFirstWaypoint();
|
||||||
final Iterable<Pair<Position, Bearing>> crossingInformationForStart = getCrossingInformation(start, t);
|
final Iterable<Pair<Position, Bearing>> crossingInformationForStart = getCrossingInformation(start, t, markPositionCache);
|
||||||
if (start == null) {
|
if (start == null) {
|
||||||
result = 1.0;
|
result = 1.0;
|
||||||
} else {
|
} else {
|
||||||
@@ -967,7 +961,7 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
final Position estimatedPositionAtT = track.getEstimatedPosition(t, /* extrapolate */ true);
|
final Position estimatedPositionAtT = track.getEstimatedPosition(t, /* extrapolate */ true);
|
||||||
// consider that a position may not be available for that other competitor, although a track exists
|
// consider that a position may not be available for that other competitor, although a track exists
|
||||||
if (estimatedPositionAtT != null) {
|
if (estimatedPositionAtT != null) {
|
||||||
final Distance otherCompetitorsDistanceToStartAtT = getMinDistanceOrNull(calculateDistance(estimatedPositionAtT, start, t));
|
final Distance otherCompetitorsDistanceToStartAtT = getMinDistanceOrNull(calculateDistance(estimatedPositionAtT, start, t, markPositionCache));
|
||||||
if (otherCompetitorsDistanceToStartAtT != null) {
|
if (otherCompetitorsDistanceToStartAtT != null) {
|
||||||
final Distance crossTrackError;
|
final Distance crossTrackError;
|
||||||
if (startIsLine) {
|
if (startIsLine) {
|
||||||
@@ -1102,14 +1096,16 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
* to be negative. If the passing instructions are {@link PassingInstruction#Line}, it checks whether the boat
|
* to be negative. If the passing instructions are {@link PassingInstruction#Line}, it checks whether the boat
|
||||||
* passed between the two marks.
|
* passed between the two marks.
|
||||||
*/
|
*/
|
||||||
private double getSidePenalty(Waypoint w, Position p, TimePoint t, boolean portMark) {
|
private double getSidePenalty(Waypoint w, Position p, TimePoint t, boolean portMark, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert t.equals(markPositionCache.getTimePoint());
|
||||||
|
assert race == markPositionCache.getTrackedRace();
|
||||||
boolean isOnRightSide = true;
|
boolean isOnRightSide = true;
|
||||||
Distance onWrongSide = new MeterDistance(0);
|
Distance onWrongSide = new MeterDistance(0);
|
||||||
PassingInstruction instruction = getPassingInstructions(w);
|
PassingInstruction instruction = getPassingInstructions(w);
|
||||||
if (instruction == PassingInstruction.Line) {
|
if (instruction == PassingInstruction.Line) {
|
||||||
List<Position> pos = new ArrayList<>();
|
List<Position> pos = new ArrayList<>();
|
||||||
for (Mark m : w.getMarks()) {
|
for (Mark m : w.getMarks()) {
|
||||||
Position po = race.getOrCreateTrack(m).getEstimatedPosition(t, false);
|
Position po = markPositionCache.getEstimatedPosition(m);
|
||||||
if (po == null) {
|
if (po == null) {
|
||||||
isOnRightSide = true;
|
isOnRightSide = true;
|
||||||
break;
|
break;
|
||||||
@@ -1137,11 +1133,11 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
|| instruction == PassingInstruction.FixedBearing || instruction == PassingInstruction.Offset) {
|
|| instruction == PassingInstruction.FixedBearing || instruction == PassingInstruction.Offset) {
|
||||||
m = w.getMarks().iterator().next();
|
m = w.getMarks().iterator().next();
|
||||||
} else if (instruction == PassingInstruction.Gate) {
|
} else if (instruction == PassingInstruction.Gate) {
|
||||||
Util.Pair<Mark, Mark> pair = getPortAndStarboardMarks(t, w);
|
Util.Pair<Mark, Mark> pair = getPortAndStarboardMarks(t, w, markPositionCache);
|
||||||
m = portMark ? pair.getA() : pair.getB();
|
m = portMark ? pair.getA() : pair.getB();
|
||||||
}
|
}
|
||||||
if (m != null) {
|
if (m != null) {
|
||||||
Util.Pair<Position, Bearing> crossingInfo = Util.get(getCrossingInformation(w, t), portMark ? 0 : 1);
|
Util.Pair<Position, Bearing> crossingInfo = Util.get(getCrossingInformation(w, t, markPositionCache), portMark ? 0 : 1);
|
||||||
isOnRightSide = p.crossTrackError(crossingInfo.getA(), crossingInfo.getB().add(new DegreeBearingImpl(90))).getMeters() < 0;
|
isOnRightSide = p.crossTrackError(crossingInfo.getA(), crossingInfo.getB().add(new DegreeBearingImpl(90))).getMeters() < 0;
|
||||||
if (isOnRightSide == false) {
|
if (isOnRightSide == false) {
|
||||||
onWrongSide = p.getDistance(crossingInfo.getA());
|
onWrongSide = p.getDistance(crossingInfo.getA());
|
||||||
@@ -1195,9 +1191,11 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
/**
|
/**
|
||||||
* @return a probability based on the distance to <code>w</code> and the average leg lengths before and after.
|
* @return a probability based on the distance to <code>w</code> and the average leg lengths before and after.
|
||||||
*/
|
*/
|
||||||
private Double getDistanceBasedProbability(Waypoint w, TimePoint t, Distance distance) {
|
private Double getDistanceBasedProbability(Waypoint w, TimePoint t, Distance distance, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert t.equals(markPositionCache.getTimePoint());
|
||||||
|
assert race == markPositionCache.getTrackedRace();
|
||||||
final Double result;
|
final Double result;
|
||||||
Distance legLength = getAverageLengthOfAdjacentLegs(t, w);
|
Distance legLength = getAverageLengthOfAdjacentLegs(t, w, markPositionCache);
|
||||||
if (legLength != null) {
|
if (legLength != null) {
|
||||||
result = 1 / (STRICTNESS_OF_DISTANCE_BASED_PROBABILITY/* Raising this will make it stricter */
|
result = 1 / (STRICTNESS_OF_DISTANCE_BASED_PROBABILITY/* Raising this will make it stricter */
|
||||||
// reduce distance by 2x the typical HDOP, accounting for the possibility that some distance from the mark
|
// reduce distance by 2x the typical HDOP, accounting for the possibility that some distance from the mark
|
||||||
@@ -1212,18 +1210,20 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
/**
|
/**
|
||||||
* @return an average of the estimated legs before and after <code>w</code>.
|
* @return an average of the estimated legs before and after <code>w</code>.
|
||||||
*/
|
*/
|
||||||
private Distance getAverageLengthOfAdjacentLegs(TimePoint t, Waypoint w) {
|
private Distance getAverageLengthOfAdjacentLegs(TimePoint t, Waypoint w, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert t.equals(markPositionCache.getTimePoint());
|
||||||
|
assert race == markPositionCache.getTrackedRace();
|
||||||
final Distance result;
|
final Distance result;
|
||||||
final Course course = race.getRace().getCourse();
|
final Course course = race.getRace().getCourse();
|
||||||
if (course.getNumberOfWaypoints() < 2) {
|
if (course.getNumberOfWaypoints() < 2) {
|
||||||
result = null;
|
result = null;
|
||||||
} else if (w == course.getFirstWaypoint()) {
|
} else if (w == course.getFirstWaypoint()) {
|
||||||
result = race.getTrackedLegStartingAt(w).getGreatCircleDistance(t);
|
result = race.getTrackedLegStartingAt(w).getGreatCircleDistance(t, markPositionCache);
|
||||||
} else if (w == course.getLastWaypoint()) {
|
} else if (w == course.getLastWaypoint()) {
|
||||||
result = race.getTrackedLegFinishingAt(w).getGreatCircleDistance(t);
|
result = race.getTrackedLegFinishingAt(w).getGreatCircleDistance(t, markPositionCache);
|
||||||
} else {
|
} else {
|
||||||
Distance before = race.getTrackedLegStartingAt(w).getGreatCircleDistance(t);
|
Distance before = race.getTrackedLegStartingAt(w).getGreatCircleDistance(t, markPositionCache);
|
||||||
Distance after = race.getTrackedLegFinishingAt(w).getGreatCircleDistance(t);
|
Distance after = race.getTrackedLegFinishingAt(w).getGreatCircleDistance(t, markPositionCache);
|
||||||
if (after != null && before != null) {
|
if (after != null && before != null) {
|
||||||
result = new MeterDistance(before.add(after).getMeters() / 2);
|
result = new MeterDistance(before.add(after).getMeters() / 2);
|
||||||
} else {
|
} else {
|
||||||
@@ -1235,8 +1235,13 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the distance from p to each mark of w at the timepoint t.
|
* Calculates the distance from p to each mark of w at the timepoint t.
|
||||||
|
*
|
||||||
|
* @param markPositionCache
|
||||||
|
* a mark position cache for time point {@code t} for this finder's {@link #race}
|
||||||
*/
|
*/
|
||||||
private List<Distance> calculateDistance(Position p, Waypoint w, TimePoint t) {
|
private List<Distance> calculateDistance(Position p, Waypoint w, TimePoint t, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert t.equals(markPositionCache.getTimePoint());
|
||||||
|
assert race == markPositionCache.getTrackedRace();
|
||||||
final List<Distance> distances = new ArrayList<>();
|
final List<Distance> distances = new ArrayList<>();
|
||||||
PassingInstruction instruction = getPassingInstructions(w);
|
PassingInstruction instruction = getPassingInstructions(w);
|
||||||
boolean singleMark = false;
|
boolean singleMark = false;
|
||||||
@@ -1248,25 +1253,25 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
singleMark = true;
|
singleMark = true;
|
||||||
break;
|
break;
|
||||||
case Gate:
|
case Gate:
|
||||||
Util.Pair<Mark, Mark> posGate = getPortAndStarboardMarks(t, w);
|
Util.Pair<Mark, Mark> posGate = getPortAndStarboardMarks(t, w, markPositionCache);
|
||||||
if (posGate.getA() != null) {
|
if (posGate.getA() != null) {
|
||||||
Position portGatePosition = race.getOrCreateTrack(posGate.getA()).getEstimatedPosition(t, false);
|
Position portGatePosition = markPositionCache.getEstimatedPosition(posGate.getA());
|
||||||
distances.add(portGatePosition != null ? p.getDistance(portGatePosition) : null);
|
distances.add(portGatePosition != null ? p.getDistance(portGatePosition) : null);
|
||||||
} else {
|
} else {
|
||||||
distances.add(null);
|
distances.add(null);
|
||||||
}
|
}
|
||||||
if (posGate.getB() != null) {
|
if (posGate.getB() != null) {
|
||||||
Position starboardGatePosition = race.getOrCreateTrack(posGate.getB()).getEstimatedPosition(t, false);
|
Position starboardGatePosition = markPositionCache.getEstimatedPosition(posGate.getB());
|
||||||
distances.add(starboardGatePosition != null ? p.getDistance(starboardGatePosition) : null);
|
distances.add(starboardGatePosition != null ? p.getDistance(starboardGatePosition) : null);
|
||||||
} else {
|
} else {
|
||||||
distances.add(null);
|
distances.add(null);
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
case Line:
|
case Line:
|
||||||
Util.Pair<Mark, Mark> posLine = getPortAndStarboardMarks(t, w);
|
Util.Pair<Mark, Mark> posLine = getPortAndStarboardMarks(t, w, markPositionCache);
|
||||||
if (posLine.getA() != null && posLine.getB() != null) {
|
if (posLine.getA() != null && posLine.getB() != null) {
|
||||||
Position portLinePosition = race.getOrCreateTrack(posLine.getA()).getEstimatedPosition(t, false);
|
Position portLinePosition = markPositionCache.getEstimatedPosition(posLine.getA());
|
||||||
Position starboardLinePosition = race.getOrCreateTrack(posLine.getB()).getEstimatedPosition(t, false);
|
Position starboardLinePosition = markPositionCache.getEstimatedPosition(posLine.getB());
|
||||||
distances.add((portLinePosition != null && starboardLinePosition != null) ? p.getDistanceToLine(
|
distances.add((portLinePosition != null && starboardLinePosition != null) ? p.getDistanceToLine(
|
||||||
portLinePosition, starboardLinePosition).abs() : null);
|
portLinePosition, starboardLinePosition).abs() : null);
|
||||||
}
|
}
|
||||||
@@ -1282,8 +1287,7 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
if (singleMark) {
|
if (singleMark) {
|
||||||
Position markPosition = race.getOrCreateTrack(w.getMarks().iterator().next())
|
Position markPosition = markPositionCache.getEstimatedPosition(w.getMarks().iterator().next());
|
||||||
.getEstimatedPosition(t, false);
|
|
||||||
distances.add(markPosition != null ? p.getDistance(markPosition) : null);
|
distances.add(markPosition != null ? p.getDistance(markPosition) : null);
|
||||||
}
|
}
|
||||||
return distances;
|
return distances;
|
||||||
@@ -1304,44 +1308,46 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
* @return all possible ways to pass a waypoint, described as a position and a bearing. The line out of those two
|
* @return all possible ways to pass a waypoint, described as a position and a bearing. The line out of those two
|
||||||
* must be crossed.
|
* must be crossed.
|
||||||
*/
|
*/
|
||||||
private Iterable<Util.Pair<Position, Bearing>> getCrossingInformation(Waypoint w, TimePoint t) {
|
private Iterable<Util.Pair<Position, Bearing>> getCrossingInformation(Waypoint w, TimePoint t, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert t.equals(markPositionCache.getTimePoint());
|
||||||
|
assert race == markPositionCache.getTrackedRace();
|
||||||
List<Util.Pair<Position, Bearing>> result = new ArrayList<>();
|
List<Util.Pair<Position, Bearing>> result = new ArrayList<>();
|
||||||
PassingInstruction instruction = getPassingInstructions(w);
|
PassingInstruction instruction = getPassingInstructions(w);
|
||||||
if (instruction == PassingInstruction.Line) {
|
if (instruction == PassingInstruction.Line) {
|
||||||
Util.Pair<Mark, Mark> marks = getPortAndStarboardMarks(t, w);
|
Util.Pair<Mark, Mark> marks = getPortAndStarboardMarks(t, w, markPositionCache);
|
||||||
Position portPosition = null;
|
Position portPosition = null;
|
||||||
Bearing b = null;
|
Bearing b = null;
|
||||||
Mark portMark = marks.getA();
|
Mark portMark = marks.getA();
|
||||||
Mark starBoardMark = marks.getB();
|
Mark starBoardMark = marks.getB();
|
||||||
if (portMark != null && starBoardMark != null) {
|
if (portMark != null && starBoardMark != null) {
|
||||||
portPosition = race.getOrCreateTrack(portMark).getEstimatedPosition(t, false);
|
portPosition = markPositionCache.getEstimatedPosition(portMark);
|
||||||
Position starboardPosition = race.getOrCreateTrack(starBoardMark).getEstimatedPosition(t, false);
|
Position starboardPosition = markPositionCache.getEstimatedPosition(starBoardMark);
|
||||||
if (portPosition != null && starboardPosition != null) {
|
if (portPosition != null && starboardPosition != null) {
|
||||||
b = portPosition.getBearingGreatCircle(starboardPosition);
|
b = portPosition.getBearingGreatCircle(starboardPosition);
|
||||||
result.add(new Util.Pair<Position, Bearing>(portPosition, b));
|
result.add(new Util.Pair<Position, Bearing>(portPosition, b));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if (instruction == PassingInstruction.Gate) {
|
} else if (instruction == PassingInstruction.Gate) {
|
||||||
Position before = race.getApproximatePosition(race.getTrackedLegFinishingAt(w).getLeg().getFrom(), t);
|
Position before = markPositionCache.getApproximatePosition(race.getTrackedLegFinishingAt(w).getLeg().getFrom());
|
||||||
Position after = race.getApproximatePosition(race.getTrackedLegStartingAt(w).getLeg().getTo(), t);
|
Position after = markPositionCache.getApproximatePosition(race.getTrackedLegStartingAt(w).getLeg().getTo());
|
||||||
Util.Pair<Mark, Mark> pos = getPortAndStarboardMarks(t, w);
|
Util.Pair<Mark, Mark> pos = getPortAndStarboardMarks(t, w, markPositionCache);
|
||||||
Mark portMark = pos.getA();
|
Mark portMark = pos.getA();
|
||||||
if (portMark != null) {
|
if (portMark != null) {
|
||||||
Position portPosition = race.getOrCreateTrack(portMark).getEstimatedPosition(t, false);
|
Position portPosition = markPositionCache.getEstimatedPosition(portMark);
|
||||||
Bearing crossingPort = before.getBearingGreatCircle(portPosition).middle(
|
Bearing crossingPort = before.getBearingGreatCircle(portPosition).middle(
|
||||||
after.getBearingGreatCircle(portPosition));
|
after.getBearingGreatCircle(portPosition));
|
||||||
result.add(new Util.Pair<Position, Bearing>(portPosition, crossingPort));
|
result.add(new Util.Pair<Position, Bearing>(portPosition, crossingPort));
|
||||||
}
|
}
|
||||||
Mark starboardMark = pos.getB();
|
Mark starboardMark = pos.getB();
|
||||||
if (starboardMark != null) {
|
if (starboardMark != null) {
|
||||||
Position starboardPosition = race.getOrCreateTrack(starboardMark).getEstimatedPosition(t, false);
|
Position starboardPosition = markPositionCache.getEstimatedPosition(starboardMark);
|
||||||
Bearing crossingStarboard = before.getBearingGreatCircle(starboardPosition).middle(
|
Bearing crossingStarboard = before.getBearingGreatCircle(starboardPosition).middle(
|
||||||
after.getBearingGreatCircle(starboardPosition));
|
after.getBearingGreatCircle(starboardPosition));
|
||||||
result.add(new Util.Pair<Position, Bearing>(starboardPosition, crossingStarboard));
|
result.add(new Util.Pair<Position, Bearing>(starboardPosition, crossingStarboard));
|
||||||
}
|
}
|
||||||
} else { // single mark
|
} else { // single mark
|
||||||
Bearing b = null;
|
Bearing b = null;
|
||||||
Position p = race.getOrCreateTrack(w.getMarks().iterator().next()).getEstimatedPosition(t, false);
|
Position p = markPositionCache.getEstimatedPosition(w.getMarks().iterator().next());
|
||||||
if (instruction == PassingInstruction.FixedBearing) {
|
if (instruction == PassingInstruction.FixedBearing) {
|
||||||
b = w.getFixedBearing();
|
b = w.getFixedBearing();
|
||||||
} else {
|
} else {
|
||||||
@@ -1351,23 +1357,23 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
// orthogonally to the adjacent leg:
|
// orthogonally to the adjacent leg:
|
||||||
if (w == race.getRace().getCourse().getFirstWaypoint()) {
|
if (w == race.getRace().getCourse().getFirstWaypoint()) {
|
||||||
if (instruction == PassingInstruction.None || instruction == PassingInstruction.Single_Unknown) {
|
if (instruction == PassingInstruction.None || instruction == PassingInstruction.Single_Unknown) {
|
||||||
b = race.getTrackedLegStartingAt(w).getLegBearing(t).add(new DegreeBearingImpl(90));
|
b = markPositionCache.getLegBearing(race.getTrackedLegStartingAt(w)).add(new DegreeBearingImpl(90));
|
||||||
result.add(new Pair<>(p, b));
|
result.add(new Pair<>(p, b));
|
||||||
b = race.getTrackedLegStartingAt(w).getLegBearing(t).add(new DegreeBearingImpl(270));
|
b = markPositionCache.getLegBearing(race.getTrackedLegStartingAt(w)).add(new DegreeBearingImpl(270));
|
||||||
} else {
|
} else {
|
||||||
b = race.getTrackedLegStartingAt(w).getLegBearing(t).add(new DegreeBearingImpl(instruction == PassingInstruction.Port ? 90 : 270));
|
b = markPositionCache.getLegBearing(race.getTrackedLegStartingAt(w)).add(new DegreeBearingImpl(instruction == PassingInstruction.Port ? 90 : 270));
|
||||||
}
|
}
|
||||||
} else if (w == race.getRace().getCourse().getLastWaypoint()) {
|
} else if (w == race.getRace().getCourse().getLastWaypoint()) {
|
||||||
if (instruction == PassingInstruction.None || instruction == PassingInstruction.Single_Unknown) {
|
if (instruction == PassingInstruction.None || instruction == PassingInstruction.Single_Unknown) {
|
||||||
b = race.getTrackedLegFinishingAt(w).getLegBearing(t).add(new DegreeBearingImpl(90));
|
b = markPositionCache.getLegBearing(race.getTrackedLegFinishingAt(w)).add(new DegreeBearingImpl(90));
|
||||||
result.add(new Pair<>(p, b));
|
result.add(new Pair<>(p, b));
|
||||||
b = race.getTrackedLegFinishingAt(w).getLegBearing(t).add(new DegreeBearingImpl(270));
|
b = markPositionCache.getLegBearing(race.getTrackedLegFinishingAt(w)).add(new DegreeBearingImpl(270));
|
||||||
} else {
|
} else {
|
||||||
b = race.getTrackedLegFinishingAt(w).getLegBearing(t).add(new DegreeBearingImpl(instruction == PassingInstruction.Port ? 90 : 270));
|
b = markPositionCache.getLegBearing(race.getTrackedLegFinishingAt(w)).add(new DegreeBearingImpl(instruction == PassingInstruction.Port ? 90 : 270));
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Bearing before = race.getTrackedLegFinishingAt(w).getLegBearing(t);
|
Bearing before = markPositionCache.getLegBearing(race.getTrackedLegFinishingAt(w));
|
||||||
Bearing after = race.getTrackedLegStartingAt(w).getLegBearing(t);
|
Bearing after = markPositionCache.getLegBearing(race.getTrackedLegStartingAt(w));
|
||||||
if (before != null && after != null) {
|
if (before != null && after != null) {
|
||||||
b = before.middle(after.reverse());
|
b = before.middle(after.reverse());
|
||||||
}
|
}
|
||||||
@@ -1379,14 +1385,17 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
* @param markPositionCache
|
||||||
|
* a mark position cache for this finder's {@link #race} for time point {@code t}
|
||||||
* @return the marks of a waypoint with two marks in the order port, starboard (when approaching the waypoint from
|
* @return the marks of a waypoint with two marks in the order port, starboard (when approaching the waypoint from
|
||||||
* the direction of the waypoint beforehand.
|
* the direction of the waypoint beforehand.
|
||||||
*/
|
*/
|
||||||
private Util.Pair<Mark, Mark> getPortAndStarboardMarks(TimePoint t, Waypoint w) {
|
private Util.Pair<Mark, Mark> getPortAndStarboardMarks(TimePoint t, Waypoint w, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert t.equals(markPositionCache.getTimePoint());
|
||||||
|
assert race == markPositionCache.getTrackedRace();
|
||||||
List<Position> markPositions = new ArrayList<Position>();
|
List<Position> markPositions = new ArrayList<Position>();
|
||||||
for (Mark mark : w.getMarks()) {
|
for (Mark mark : w.getMarks()) {
|
||||||
final Position estimatedMarkPosition = race.getOrCreateTrack(mark).getEstimatedPosition(t, /* extrapolate */
|
final Position estimatedMarkPosition = markPositionCache.getEstimatedPosition(mark);
|
||||||
false);
|
|
||||||
if (estimatedMarkPosition == null) {
|
if (estimatedMarkPosition == null) {
|
||||||
return new Util.Pair<Mark, Mark>(null, null);
|
return new Util.Pair<Mark, Mark>(null, null);
|
||||||
}
|
}
|
||||||
@@ -1401,8 +1410,8 @@ public class CandidateFinderImpl implements CandidateFinder {
|
|||||||
return new Util.Pair<Mark, Mark>(null, null);
|
return new Util.Pair<Mark, Mark>(null, null);
|
||||||
}
|
}
|
||||||
final boolean isStartLine = indexOfWaypoint == 0;
|
final boolean isStartLine = indexOfWaypoint == 0;
|
||||||
final Bearing legDeterminingDirectionBearing = race.getTrackedLeg(
|
final Bearing legDeterminingDirectionBearing = markPositionCache.getLegBearing(race.getTrackedLeg(
|
||||||
legs.get(isStartLine ? 0 : indexOfWaypoint - 1)).getLegBearing(t);
|
legs.get(isStartLine ? 0 : indexOfWaypoint - 1)));
|
||||||
if (legDeterminingDirectionBearing == null) {
|
if (legDeterminingDirectionBearing == null) {
|
||||||
return new Util.Pair<Mark, Mark>(null, null);
|
return new Util.Pair<Mark, Mark>(null, null);
|
||||||
}
|
}
|
||||||
|
|||||||
+58
@@ -0,0 +1,58 @@
|
|||||||
|
package com.sap.sailing.domain.tracking;
|
||||||
|
|
||||||
|
import com.sap.sailing.domain.base.Mark;
|
||||||
|
import com.sap.sailing.domain.base.Waypoint;
|
||||||
|
import com.sap.sailing.domain.common.Bearing;
|
||||||
|
import com.sap.sailing.domain.common.Position;
|
||||||
|
import com.sap.sse.common.TimePoint;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Some calculations that span several method calls require access to the same {@link Mark} positions for the same
|
||||||
|
* {@link TimePoint} within the scope of the same {@link TrackedRace} repeatedly. Usually, for such a compound
|
||||||
|
* calculation it doesn't matter whether a new fix has arrived in between, making it valid to cache the mark
|
||||||
|
* positions---once calculated---for the remainder of the compound operation. Based on these positions, approximate
|
||||||
|
* waypoint positions and leg bearings can also be calculated and cached.
|
||||||
|
* <p>
|
||||||
|
*
|
||||||
|
* Implementations of this interface may cache the results of computing a mark's position for the duration of this
|
||||||
|
* object.
|
||||||
|
*
|
||||||
|
* @author Axel Uhl (D043530)
|
||||||
|
*
|
||||||
|
*/
|
||||||
|
public interface MarkPositionAtTimePointCache {
|
||||||
|
/**
|
||||||
|
* Gets the non-extrapolated mark position in this cache's tracked race at the time point
|
||||||
|
* specified.
|
||||||
|
*
|
||||||
|
* @see TrackedRace#getOrCreateTrack(Mark)
|
||||||
|
* @see GPSFixTrack#getEstimatedPosition(TimePoint, boolean)
|
||||||
|
*/
|
||||||
|
Position getEstimatedPosition(Mark mark);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the approximate position of a waypoint in the scope of this cache's tracked race at
|
||||||
|
* the time point specified.
|
||||||
|
*
|
||||||
|
* @see TrackedRace#getApproximatePosition(Waypoint, TimePoint)
|
||||||
|
*/
|
||||||
|
Position getApproximatePosition(Waypoint waypoint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Gets the bearing of the tracked leg at the time point specified, optionally based on mark position
|
||||||
|
* information already stored in this cache.
|
||||||
|
*
|
||||||
|
* @see TrackedLeg#getLegBearing(TimePoint)
|
||||||
|
*/
|
||||||
|
Bearing getLegBearing(TrackedLeg trackedLeg);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the time point for which this cache holds values
|
||||||
|
*/
|
||||||
|
TimePoint getTimePoint();
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return the tracked race for which this cache holds values
|
||||||
|
*/
|
||||||
|
TrackedRace getTrackedRace();
|
||||||
|
}
|
||||||
@@ -56,6 +56,16 @@ public interface TrackedLeg extends Serializable {
|
|||||||
|
|
||||||
Bearing getLegBearing(TimePoint at);
|
Bearing getLegBearing(TimePoint at);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link #getLegBearing(TimePoint)}, but giving the caller the possibility to provide a cache for
|
||||||
|
* mark positions and related position and bearing data
|
||||||
|
*
|
||||||
|
* @param markPositionCache
|
||||||
|
* a cache for this tracked leg's {@link MarkPositionAtTimePointCache#getTrackedRace() race} and the
|
||||||
|
* {@link MarkPositionAtTimePointCache#getTimePoint() timePoint} passed
|
||||||
|
*/
|
||||||
|
Bearing getLegBearing(TimePoint at, MarkPositionAtTimePointCache markPositionCache);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Returns the positive (absolute) distance of <code>p</code> to this leg's course middle line at <code>timePoint</code>,
|
* Returns the positive (absolute) distance of <code>p</code> to this leg's course middle line at <code>timePoint</code>,
|
||||||
* based on the position of the waypoints delimiting this leg at that time.
|
* based on the position of the waypoints delimiting this leg at that time.
|
||||||
@@ -80,6 +90,16 @@ public interface TrackedLeg extends Serializable {
|
|||||||
*/
|
*/
|
||||||
Distance getGreatCircleDistance(TimePoint timePoint);
|
Distance getGreatCircleDistance(TimePoint timePoint);
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link #getGreatCircleDistance(TimePoint)}, but giving the caller the possibility to provide a cache for
|
||||||
|
* mark positions and related position and bearing data
|
||||||
|
*
|
||||||
|
* @param markPositionCache
|
||||||
|
* a cache for this tracked leg's {@link MarkPositionAtTimePointCache#getTrackedRace() race} and the
|
||||||
|
* {@link MarkPositionAtTimePointCache#getTimePoint() timePoint} passed
|
||||||
|
*/
|
||||||
|
Distance getGreatCircleDistance(TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* If the current {@link #getLeg() leg} is +/- {@link TrackedLegImpl#UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees
|
* If the current {@link #getLeg() leg} is +/- {@link TrackedLegImpl#UPWIND_DOWNWIND_TOLERANCE_IN_DEG} degrees
|
||||||
* collinear with the wind's bearing, <code>pos1</code> is projected onto the line crossing <code>pos2</code> in the
|
* collinear with the wind's bearing, <code>pos1</code> is projected onto the line crossing <code>pos2</code> in the
|
||||||
@@ -144,7 +164,8 @@ public interface TrackedLeg extends Serializable {
|
|||||||
* is not available
|
* is not available
|
||||||
* @throws NoWindException no wind available. unable to determine legtypes for given timepoint
|
* @throws NoWindException no wind available. unable to determine legtypes for given timepoint
|
||||||
*/
|
*/
|
||||||
Duration getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, NoWindException;
|
Duration getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint, MarkPositionAtTimePointCache markPositionCache)
|
||||||
|
throws NotEnoughDataHasBeenAddedException, NoWindException;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Computes the windward distance (for upwind/downwind legs) or the along-course distance (for reaching legs) at a reference time point
|
* Computes the windward distance (for upwind/downwind legs) or the along-course distance (for reaching legs) at a reference time point
|
||||||
|
|||||||
@@ -49,6 +49,7 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
|||||||
import com.sap.sailing.domain.polars.PolarDataService;
|
import com.sap.sailing.domain.polars.PolarDataService;
|
||||||
import com.sap.sailing.domain.ranking.RankingMetric;
|
import com.sap.sailing.domain.ranking.RankingMetric;
|
||||||
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
||||||
|
import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
|
import com.sap.sailing.domain.tracking.impl.TrackedRaceImpl;
|
||||||
import com.sap.sse.common.Duration;
|
import com.sap.sse.common.Duration;
|
||||||
import com.sap.sse.common.IsManagedByCache;
|
import com.sap.sse.common.IsManagedByCache;
|
||||||
@@ -316,8 +317,22 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
|||||||
* If the <code>waypoint</code> only has one {@link #getMarks() mark}, its position at time <code>timePoint</code>
|
* If the <code>waypoint</code> only has one {@link #getMarks() mark}, its position at time <code>timePoint</code>
|
||||||
* is returned. Otherwise, the center of gravity between the mark positions is computed and returned.
|
* is returned. Otherwise, the center of gravity between the mark positions is computed and returned.
|
||||||
*/
|
*/
|
||||||
Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint);
|
default Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
|
||||||
|
return getApproximatePosition(waypoint, timePoint, new NonCachingMarkPositionAtTimePointCache(this, timePoint));
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Same as {@link #getApproximatePosition(Waypoint, TimePoint)}, but giving the caller the possibility to pass a
|
||||||
|
* cache of mark positions and related information that can help speed up compound operations requiring frequent
|
||||||
|
* access to the same marks in the same race for the same time point.
|
||||||
|
*
|
||||||
|
* @param markPositionCache
|
||||||
|
* a cache for this {@link MarkPositionAtTimePointCache#getTrackedRace() race} and the
|
||||||
|
* {@link MarkPositionAtTimePointCache#getTimePoint() timePoint} passed
|
||||||
|
*/
|
||||||
|
Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint,
|
||||||
|
MarkPositionAtTimePointCache markPositionCache);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Checks whether the {@link Wind#getTimePoint()} is in range of start and end {@link TimePoint}s plus extra time
|
* Checks whether the {@link Wind#getTimePoint()} is in range of start and end {@link TimePoint}s plus extra time
|
||||||
* for wind recording. If, based on a {@link RaceExecutionOrderProvider}, there is no previous race that takes the
|
* for wind recording. If, based on a {@link RaceExecutionOrderProvider}, there is no previous race that takes the
|
||||||
@@ -946,4 +961,5 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
|||||||
* @return all currently attached {@link RaceLog}s or an empty Iterable if there aren't any
|
* @return all currently attached {@link RaceLog}s or an empty Iterable if there aren't any
|
||||||
*/
|
*/
|
||||||
Iterable<RaceLog> getAttachedRaceLogs();
|
Iterable<RaceLog> getAttachedRaceLogs();
|
||||||
|
|
||||||
}
|
}
|
||||||
+62
@@ -0,0 +1,62 @@
|
|||||||
|
package com.sap.sailing.domain.tracking.impl;
|
||||||
|
|
||||||
|
import java.util.HashMap;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
|
import com.sap.sailing.domain.base.Mark;
|
||||||
|
import com.sap.sailing.domain.base.Waypoint;
|
||||||
|
import com.sap.sailing.domain.common.Bearing;
|
||||||
|
import com.sap.sailing.domain.common.Position;
|
||||||
|
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||||
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
|
import com.sap.sse.common.TimePoint;
|
||||||
|
|
||||||
|
public class MarkPositionAtTimePointCacheImpl extends NonCachingMarkPositionAtTimePointCache {
|
||||||
|
private final Map<Mark, Position> estimatedMarkPositions;
|
||||||
|
private final Map<Waypoint, Position> approximateWaypointPositions;
|
||||||
|
private final Map<TrackedLeg, Bearing> legBearings;
|
||||||
|
|
||||||
|
public MarkPositionAtTimePointCacheImpl(TrackedRace trackedRace, TimePoint timePoint) {
|
||||||
|
super(trackedRace, timePoint);
|
||||||
|
estimatedMarkPositions = new HashMap<>();
|
||||||
|
approximateWaypointPositions = new HashMap<>();
|
||||||
|
legBearings = new HashMap<>();
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Position getEstimatedPosition(Mark mark) {
|
||||||
|
final Position result;
|
||||||
|
if (estimatedMarkPositions.containsKey(mark)) {
|
||||||
|
result = estimatedMarkPositions.get(mark);
|
||||||
|
} else {
|
||||||
|
result = super.getEstimatedPosition(mark);
|
||||||
|
estimatedMarkPositions.put(mark, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Position getApproximatePosition(Waypoint waypoint) {
|
||||||
|
final Position result;
|
||||||
|
if (approximateWaypointPositions.containsKey(waypoint)) {
|
||||||
|
result = approximateWaypointPositions.get(waypoint);
|
||||||
|
} else {
|
||||||
|
result = super.getApproximatePosition(waypoint);
|
||||||
|
approximateWaypointPositions.put(waypoint, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bearing getLegBearing(TrackedLeg trackedLeg) {
|
||||||
|
final Bearing result;
|
||||||
|
if (legBearings.containsKey(trackedLeg)) {
|
||||||
|
result = legBearings.get(trackedLeg);
|
||||||
|
} else {
|
||||||
|
result = super.getLegBearing(trackedLeg);
|
||||||
|
legBearings.put(trackedLeg, result);
|
||||||
|
}
|
||||||
|
return result;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+46
@@ -0,0 +1,46 @@
|
|||||||
|
package com.sap.sailing.domain.tracking.impl;
|
||||||
|
|
||||||
|
import com.sap.sailing.domain.base.Mark;
|
||||||
|
import com.sap.sailing.domain.base.Waypoint;
|
||||||
|
import com.sap.sailing.domain.common.Bearing;
|
||||||
|
import com.sap.sailing.domain.common.Position;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
|
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||||
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
|
import com.sap.sse.common.TimePoint;
|
||||||
|
|
||||||
|
public class NonCachingMarkPositionAtTimePointCache implements MarkPositionAtTimePointCache {
|
||||||
|
private final TrackedRace trackedRace;
|
||||||
|
private final TimePoint timePoint;
|
||||||
|
|
||||||
|
public NonCachingMarkPositionAtTimePointCache(TrackedRace trackedRace, TimePoint timePoint) {
|
||||||
|
super();
|
||||||
|
this.trackedRace = trackedRace;
|
||||||
|
this.timePoint = timePoint;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Position getEstimatedPosition(Mark mark) {
|
||||||
|
return trackedRace.getOrCreateTrack(mark).getEstimatedPosition(timePoint, /* extrapolate */ false);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Position getApproximatePosition(Waypoint waypoint) {
|
||||||
|
return trackedRace.getApproximatePosition(waypoint, timePoint, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bearing getLegBearing(TrackedLeg trackedLeg) {
|
||||||
|
return trackedLeg.getLegBearing(timePoint, this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TrackedRace getTrackedRace() {
|
||||||
|
return trackedRace;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public TimePoint getTimePoint() {
|
||||||
|
return timePoint;
|
||||||
|
}
|
||||||
|
}
|
||||||
+23
-6
@@ -39,6 +39,7 @@ import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuse
|
|||||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||||
import com.sap.sailing.domain.polars.PolarDataService;
|
import com.sap.sailing.domain.polars.PolarDataService;
|
||||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
@@ -204,8 +205,15 @@ public class TrackedLegImpl implements TrackedLeg {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Bearing getLegBearing(TimePoint at) {
|
public Bearing getLegBearing(TimePoint at) {
|
||||||
Position startMarkPos = getTrackedRace().getApproximatePosition(getLeg().getFrom(), at);
|
return getLegBearing(at, new MarkPositionAtTimePointCacheImpl(getTrackedRace(), at));
|
||||||
Position endMarkPos = getTrackedRace().getApproximatePosition(getLeg().getTo(), at);
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Bearing getLegBearing(TimePoint at, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert markPositionCache.getTimePoint().equals(at);
|
||||||
|
assert markPositionCache.getTrackedRace() == getTrackedRace();
|
||||||
|
Position startMarkPos = markPositionCache.getApproximatePosition(getLeg().getFrom());
|
||||||
|
Position endMarkPos = markPositionCache.getApproximatePosition(getLeg().getTo());
|
||||||
Bearing legBearing = (startMarkPos != null && endMarkPos != null) ? startMarkPos.getBearingGreatCircle(endMarkPos) : null;
|
Bearing legBearing = (startMarkPos != null && endMarkPos != null) ? startMarkPos.getBearingGreatCircle(endMarkPos) : null;
|
||||||
return legBearing;
|
return legBearing;
|
||||||
}
|
}
|
||||||
@@ -337,10 +345,12 @@ public class TrackedLegImpl implements TrackedLeg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Distance getGreatCircleDistance(TimePoint timePoint) {
|
public Distance getGreatCircleDistance(TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert markPositionCache.getTimePoint().equals(timePoint);
|
||||||
|
assert markPositionCache.getTrackedRace() == getTrackedRace();
|
||||||
final Distance result;
|
final Distance result;
|
||||||
final Position approximatePositionOfFrom = getTrackedRace().getApproximatePosition(getLeg().getFrom(), timePoint);
|
final Position approximatePositionOfFrom = markPositionCache.getApproximatePosition(getLeg().getFrom());
|
||||||
final Position approximatePositionOfTo = getTrackedRace().getApproximatePosition(getLeg().getTo(), timePoint);
|
final Position approximatePositionOfTo = markPositionCache.getApproximatePosition(getLeg().getTo());
|
||||||
if (approximatePositionOfFrom != null && approximatePositionOfTo != null) {
|
if (approximatePositionOfFrom != null && approximatePositionOfTo != null) {
|
||||||
result = approximatePositionOfFrom.getDistance(approximatePositionOfTo);
|
result = approximatePositionOfFrom.getDistance(approximatePositionOfTo);
|
||||||
} else {
|
} else {
|
||||||
@@ -349,6 +359,11 @@ public class TrackedLegImpl implements TrackedLeg {
|
|||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Distance getGreatCircleDistance(TimePoint timePoint) {
|
||||||
|
return getGreatCircleDistance(timePoint, new NonCachingMarkPositionAtTimePointCache(getTrackedRace(), timePoint));
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Distance getAbsoluteWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode) {
|
public Distance getAbsoluteWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode) {
|
||||||
return getAbsoluteWindwardDistance(pos1, pos2, at, windPositionMode, new LeaderboardDTOCalculationReuseCache(at));
|
return getAbsoluteWindwardDistance(pos1, pos2, at, windPositionMode, new LeaderboardDTOCalculationReuseCache(at));
|
||||||
@@ -498,8 +513,10 @@ public class TrackedLegImpl implements TrackedLeg {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Duration getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint)
|
public Duration getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint, MarkPositionAtTimePointCache markPositionCache)
|
||||||
throws NotEnoughDataHasBeenAddedException, NoWindException {
|
throws NotEnoughDataHasBeenAddedException, NoWindException {
|
||||||
|
assert timepoint.equals(markPositionCache.getTimePoint());
|
||||||
|
assert getTrackedRace() == markPositionCache.getTrackedRace();
|
||||||
Position centralPosition = trackedRace.getCenterOfCourse(timepoint);
|
Position centralPosition = trackedRace.getCenterOfCourse(timepoint);
|
||||||
Wind wind = trackedRace.getWind(centralPosition, timepoint, getTrackedRace().getWindSources(WindSourceType.TRACK_BASED_ESTIMATION));
|
Wind wind = trackedRace.getWind(centralPosition, timepoint, getTrackedRace().getWindSources(WindSourceType.TRACK_BASED_ESTIMATION));
|
||||||
Position from = trackedRace.getApproximatePosition(leg.getFrom(), timepoint);
|
Position from = trackedRace.getApproximatePosition(leg.getFrom(), timepoint);
|
||||||
|
|||||||
+7
-3
@@ -127,6 +127,7 @@ import com.sap.sailing.domain.tracking.GPSTrackListener;
|
|||||||
import com.sap.sailing.domain.tracking.LineDetails;
|
import com.sap.sailing.domain.tracking.LineDetails;
|
||||||
import com.sap.sailing.domain.tracking.Maneuver;
|
import com.sap.sailing.domain.tracking.Maneuver;
|
||||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||||
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
||||||
import com.sap.sailing.domain.tracking.RaceListener;
|
import com.sap.sailing.domain.tracking.RaceListener;
|
||||||
@@ -1671,10 +1672,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
|
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
|
assert timePoint.equals(markPositionCache.getTimePoint());
|
||||||
|
assert this == markPositionCache.getTrackedRace();
|
||||||
Position result = null;
|
Position result = null;
|
||||||
for (Mark mark : waypoint.getMarks()) {
|
for (Mark mark : waypoint.getMarks()) {
|
||||||
Position nextPos = getOrCreateTrack(mark).getEstimatedPosition(timePoint, /* extrapolate */false);
|
Position nextPos = markPositionCache.getEstimatedPosition(mark);
|
||||||
if (result == null) {
|
if (result == null) {
|
||||||
result = nextPos;
|
result = nextPos;
|
||||||
} else if (nextPos != null) {
|
} else if (nextPos != null) {
|
||||||
@@ -3893,8 +3896,9 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
|||||||
throw new NotEnoughDataHasBeenAddedException("Target time estimation failed. No polar service available.");
|
throw new NotEnoughDataHasBeenAddedException("Target time estimation failed. No polar service available.");
|
||||||
}
|
}
|
||||||
Duration durationOfAllLegs = Duration.NULL;
|
Duration durationOfAllLegs = Duration.NULL;
|
||||||
|
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(this, timepoint);
|
||||||
for (TrackedLeg leg : trackedLegs.values()) {
|
for (TrackedLeg leg : trackedLegs.values()) {
|
||||||
Duration durationOfLeg = leg.getEstimatedTimeToComplete(polarDataService, timepoint);
|
Duration durationOfLeg = leg.getEstimatedTimeToComplete(polarDataService, timepoint, markPositionCache);
|
||||||
durationOfAllLegs = durationOfAllLegs.plus(durationOfLeg);
|
durationOfAllLegs = durationOfAllLegs.plus(durationOfLeg);
|
||||||
}
|
}
|
||||||
return durationOfAllLegs;
|
return durationOfAllLegs;
|
||||||
|
|||||||
+2
-1
@@ -50,6 +50,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
|
|||||||
import com.sap.sailing.domain.tracking.LineDetails;
|
import com.sap.sailing.domain.tracking.LineDetails;
|
||||||
import com.sap.sailing.domain.tracking.Maneuver;
|
import com.sap.sailing.domain.tracking.Maneuver;
|
||||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
||||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||||
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
||||||
@@ -352,7 +353,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
|
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
-1
@@ -41,6 +41,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
|
|||||||
import com.sap.sailing.domain.tracking.LineDetails;
|
import com.sap.sailing.domain.tracking.LineDetails;
|
||||||
import com.sap.sailing.domain.tracking.Maneuver;
|
import com.sap.sailing.domain.tracking.Maneuver;
|
||||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||||
|
import com.sap.sailing.domain.tracking.MarkPositionAtTimePointCache;
|
||||||
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
import com.sap.sailing.domain.tracking.RaceAbortedListener;
|
||||||
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
import com.sap.sailing.domain.tracking.RaceChangeListener;
|
||||||
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
|
||||||
@@ -213,7 +214,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
|
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
Reference in new issue
Block a user