Merge remote branch 'origin/bug3932'

This commit is contained in:
Axel Uhl committed 2016-09-29 20:50:50 +02:00
commit 311f670b27
15 files changed
+341 -97

No files matched your search

@@ -11,6 +11,7 @@ import java.util.Collections;
import org.junit.Before;
import org.junit.Test;
import org.mockito.Matchers;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
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.tracking.DynamicGPSFixTrack;
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.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
@@ -137,7 +139,6 @@ public class StarbordSideOfStartLineRecognitionTest {
Position startStarboardPosition = new DegreePosition(0, 1);
Position windwardPosition = new DegreePosition(10, 0.5);
MockedTrackedRaceImpl trackedRace = createTrackedRaceWithMarkPositions(startPortPosition, startStarboardPosition, windwardPosition);
Position p = trackedRace.getOrCreateTrack(trackedRace.getStartLine(now).getStarboardMarkWhileApproachingLine()).
getEstimatedPosition(now, /* extrapolate */ false);
assertEquals(startStarboardPosition, p);
@@ -184,6 +185,8 @@ public class StarbordSideOfStartLineRecognitionTest {
when(trackedRace.getTrackedLeg(course.getFirstLeg())).thenReturn(trackedLeg);
when(trackedRace.getApproximatePosition(startWaypoint, 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;
}
@@ -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.polars.NotEnoughDataHasBeenAddedException;
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.impl.DynamicTrackedRaceImpl;
import com.sap.sailing.domain.tracking.impl.MarkPositionAtTimePointCacheImpl;
import com.sap.sailing.domain.tracking.impl.TrackedLegImpl;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
@@ -87,9 +89,8 @@ public class TargetTimeEstimationTest {
TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors);
//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);
}
@Test
@@ -123,9 +124,10 @@ public class TargetTimeEstimationTest {
when(leg.getFrom()).thenReturn(from);
when(leg.getTo()).thenReturn(to);
when(trackedRace.getApproximatePosition(from, timepoint)).thenReturn(startOfLeg);
when(trackedRace.getApproximatePosition(to, timepoint)).thenReturn(endOfLeg);
when(trackedRace.getApproximatePosition(eq(from), eq(timepoint))).thenReturn(startOfLeg);
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));
SpeedWithBearingWithConfidence<Void> boatSpeedWithBearingWithConfidencePort = new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearingPort, 1, null);
@@ -139,9 +141,8 @@ public class TargetTimeEstimationTest {
TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors);
//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);
}
@Test
@@ -175,9 +176,10 @@ public class TargetTimeEstimationTest {
when(leg.getFrom()).thenReturn(from);
when(leg.getTo()).thenReturn(to);
when(trackedRace.getApproximatePosition(from, timepoint)).thenReturn(startOfLeg);
when(trackedRace.getApproximatePosition(to, timepoint)).thenReturn(endOfLeg);
when(trackedRace.getApproximatePosition(eq(from), eq(timepoint))).thenReturn(startOfLeg);
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));
SpeedWithBearingWithConfidence<Void> boatSpeedWithBearingWithConfidencePort = new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearingPort, 1, null);
@@ -191,7 +193,7 @@ public class TargetTimeEstimationTest {
TrackedLeg trackedLeg = new TrackedLegImpl(trackedRace, leg, competitors);
//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);
}
@@ -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.impl.GPSFixImpl;
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.impl.DynamicGPSFixTrackImpl;
import com.sap.sailing.domain.tracking.impl.DynamicTrackedRaceImpl;
@@ -50,6 +51,7 @@ public class TrackedRaceCenterTest {
trackedRace = mock(DynamicTrackedRaceImpl.class);
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), Matchers.any(MarkPositionAtTimePointCache.class))).thenCallRealMethod();
RaceDefinition race = mock(RaceDefinition.class);
when(trackedRace.getRace()).thenReturn(race);
course = mock(Course.class);
@@ -63,6 +63,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Maneuver;
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.RaceChangeListener;
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
@@ -612,7 +613,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
}
@Override
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
return null;
}
@@ -42,6 +42,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Maneuver;
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.RaceChangeListener;
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
@@ -218,7 +219,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
}
@Override
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
return null;
}
@@ -39,6 +39,8 @@ import com.sap.sailing.domain.markpassingcalculation.CandidateFinder;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
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.sse.common.Duration;
import com.sap.sse.common.TimePoint;
@@ -611,15 +613,16 @@ public class CandidateFinderImpl implements CandidateFinder {
if (Math.abs(dis.getMeters()) < Math.abs(disBefore.getMeters())
&& Math.abs(dis.getMeters()) < Math.abs(disAfter.getMeters())) {
t = fix.getTimePoint();
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(race, t);
p = fix.getPosition();
Double newProbability = getDistanceBasedProbability(w, t, dis);
Double newProbability = getDistanceBasedProbability(w, t, dis, markPositionCache);
if (newProbability != null) {
// 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;
final Double newStartProbabilityBasedOnOtherCompetitors;
if (isStartWaypoint(w)) {
newStartProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t);
newStartProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t, markPositionCache);
if (newStartProbabilityBasedOnOtherCompetitors != null) {
newProbability *= newStartProbabilityBasedOnOtherCompetitors;
}
@@ -670,6 +673,7 @@ public class CandidateFinderImpl implements CandidateFinder {
private Map<Waypoint, List<Distance>> getDistances(Competitor c, GPSFix fix) {
// TODO Possibly for specific waypoints
Map<Waypoint, List<Distance>> result = distanceCache.get(c).get(fix);
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(race, fix.getTimePoint());
if (result == null) {
// Else calculate distances and put them into the cache
result = new LinkedHashMap<>();
@@ -677,7 +681,7 @@ public class CandidateFinderImpl implements CandidateFinder {
course.lockForRead();
try {
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);
}
} finally {
@@ -818,13 +822,14 @@ public class CandidateFinderImpl implements CandidateFinder {
result = new HashMap<>();
Position p = fix.getPosition();
TimePoint t = fix.getTimePoint();
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(race, t);
Course course = race.getRace().getCourse();
course.lockForRead();
try {
for (Waypoint w : course.getWaypoints()) {
List<Distance> distances = new ArrayList<>();
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) {
distances.add(p.crossTrackError(crossingInfo.getA(), crossingInfo.getB()));
}
@@ -838,20 +843,6 @@ public class CandidateFinderImpl implements CandidateFinder {
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
* 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 TimePoint t = t1.plus((long) (differenceInMillis * ratio));
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 double sidePenalty = getSidePenalty(w, p, t, portMark);
double probability = getDistanceBasedProbability(w, t, d) * sidePenalty;
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(race, t);
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);
// null would mean "unknown"; no penalty for those cases
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;
// 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
startProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t);
startProbabilityBasedOnOtherCompetitors = getProbabilityForStartCandidateBasedOnOtherCompetitorsBehavior(c, t, markPositionCache);
if (startProbabilityBasedOnOtherCompetitors != null) {
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
* 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.
*
* @param t
* 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
* (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
@@ -949,11 +941,13 @@ public class CandidateFinderImpl implements CandidateFinder {
* 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;
if (race.getStartOfRace(/* inferred */ false) == null && race.isGateStart() != Boolean.TRUE) {
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) {
result = 1.0;
} else {
@@ -967,7 +961,7 @@ public class CandidateFinderImpl implements CandidateFinder {
final Position estimatedPositionAtT = track.getEstimatedPosition(t, /* extrapolate */ true);
// consider that a position may not be available for that other competitor, although a track exists
if (estimatedPositionAtT != null) {
final Distance otherCompetitorsDistanceToStartAtT = getMinDistanceOrNull(calculateDistance(estimatedPositionAtT, start, t));
final Distance otherCompetitorsDistanceToStartAtT = getMinDistanceOrNull(calculateDistance(estimatedPositionAtT, start, t, markPositionCache));
if (otherCompetitorsDistanceToStartAtT != null) {
final Distance crossTrackError;
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
* 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;
Distance onWrongSide = new MeterDistance(0);
PassingInstruction instruction = getPassingInstructions(w);
if (instruction == PassingInstruction.Line) {
List<Position> pos = new ArrayList<>();
for (Mark m : w.getMarks()) {
Position po = race.getOrCreateTrack(m).getEstimatedPosition(t, false);
Position po = markPositionCache.getEstimatedPosition(m);
if (po == null) {
isOnRightSide = true;
break;
@@ -1137,11 +1133,11 @@ public class CandidateFinderImpl implements CandidateFinder {
|| instruction == PassingInstruction.FixedBearing || instruction == PassingInstruction.Offset) {
m = w.getMarks().iterator().next();
} 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();
}
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;
if (isOnRightSide == false) {
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.
*/
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;
Distance legLength = getAverageLengthOfAdjacentLegs(t, w);
Distance legLength = getAverageLengthOfAdjacentLegs(t, w, markPositionCache);
if (legLength != null) {
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
@@ -1212,18 +1210,20 @@ public class CandidateFinderImpl implements CandidateFinder {
/**
* @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 Course course = race.getRace().getCourse();
if (course.getNumberOfWaypoints() < 2) {
result = null;
} else if (w == course.getFirstWaypoint()) {
result = race.getTrackedLegStartingAt(w).getGreatCircleDistance(t);
result = race.getTrackedLegStartingAt(w).getGreatCircleDistance(t, markPositionCache);
} else if (w == course.getLastWaypoint()) {
result = race.getTrackedLegFinishingAt(w).getGreatCircleDistance(t);
result = race.getTrackedLegFinishingAt(w).getGreatCircleDistance(t, markPositionCache);
} else {
Distance before = race.getTrackedLegStartingAt(w).getGreatCircleDistance(t);
Distance after = race.getTrackedLegFinishingAt(w).getGreatCircleDistance(t);
Distance before = race.getTrackedLegStartingAt(w).getGreatCircleDistance(t, markPositionCache);
Distance after = race.getTrackedLegFinishingAt(w).getGreatCircleDistance(t, markPositionCache);
if (after != null && before != null) {
result = new MeterDistance(before.add(after).getMeters() / 2);
} else {
@@ -1235,8 +1235,13 @@ public class CandidateFinderImpl implements CandidateFinder {
/**
* 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<>();
PassingInstruction instruction = getPassingInstructions(w);
boolean singleMark = false;
@@ -1248,25 +1253,25 @@ public class CandidateFinderImpl implements CandidateFinder {
singleMark = true;
break;
case Gate:
Util.Pair<Mark, Mark> posGate = getPortAndStarboardMarks(t, w);
Util.Pair<Mark, Mark> posGate = getPortAndStarboardMarks(t, w, markPositionCache);
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);
} else {
distances.add(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);
} else {
distances.add(null);
}
break;
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) {
Position portLinePosition = race.getOrCreateTrack(posLine.getA()).getEstimatedPosition(t, false);
Position starboardLinePosition = race.getOrCreateTrack(posLine.getB()).getEstimatedPosition(t, false);
Position portLinePosition = markPositionCache.getEstimatedPosition(posLine.getA());
Position starboardLinePosition = markPositionCache.getEstimatedPosition(posLine.getB());
distances.add((portLinePosition != null && starboardLinePosition != null) ? p.getDistanceToLine(
portLinePosition, starboardLinePosition).abs() : null);
}
@@ -1282,8 +1287,7 @@ public class CandidateFinderImpl implements CandidateFinder {
break;
}
if (singleMark) {
Position markPosition = race.getOrCreateTrack(w.getMarks().iterator().next())
.getEstimatedPosition(t, false);
Position markPosition = markPositionCache.getEstimatedPosition(w.getMarks().iterator().next());
distances.add(markPosition != null ? p.getDistance(markPosition) : null);
}
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
* 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<>();
PassingInstruction instruction = getPassingInstructions(w);
if (instruction == PassingInstruction.Line) {
Util.Pair<Mark, Mark> marks = getPortAndStarboardMarks(t, w);
Util.Pair<Mark, Mark> marks = getPortAndStarboardMarks(t, w, markPositionCache);
Position portPosition = null;
Bearing b = null;
Mark portMark = marks.getA();
Mark starBoardMark = marks.getB();
if (portMark != null && starBoardMark != null) {
portPosition = race.getOrCreateTrack(portMark).getEstimatedPosition(t, false);
Position starboardPosition = race.getOrCreateTrack(starBoardMark).getEstimatedPosition(t, false);
portPosition = markPositionCache.getEstimatedPosition(portMark);
Position starboardPosition = markPositionCache.getEstimatedPosition(starBoardMark);
if (portPosition != null && starboardPosition != null) {
b = portPosition.getBearingGreatCircle(starboardPosition);
result.add(new Util.Pair<Position, Bearing>(portPosition, b));
}
}
} else if (instruction == PassingInstruction.Gate) {
Position before = race.getApproximatePosition(race.getTrackedLegFinishingAt(w).getLeg().getFrom(), t);
Position after = race.getApproximatePosition(race.getTrackedLegStartingAt(w).getLeg().getTo(), t);
Util.Pair<Mark, Mark> pos = getPortAndStarboardMarks(t, w);
Position before = markPositionCache.getApproximatePosition(race.getTrackedLegFinishingAt(w).getLeg().getFrom());
Position after = markPositionCache.getApproximatePosition(race.getTrackedLegStartingAt(w).getLeg().getTo());
Util.Pair<Mark, Mark> pos = getPortAndStarboardMarks(t, w, markPositionCache);
Mark portMark = pos.getA();
if (portMark != null) {
Position portPosition = race.getOrCreateTrack(portMark).getEstimatedPosition(t, false);
Position portPosition = markPositionCache.getEstimatedPosition(portMark);
Bearing crossingPort = before.getBearingGreatCircle(portPosition).middle(
after.getBearingGreatCircle(portPosition));
result.add(new Util.Pair<Position, Bearing>(portPosition, crossingPort));
}
Mark starboardMark = pos.getB();
if (starboardMark != null) {
Position starboardPosition = race.getOrCreateTrack(starboardMark).getEstimatedPosition(t, false);
Position starboardPosition = markPositionCache.getEstimatedPosition(starboardMark);
Bearing crossingStarboard = before.getBearingGreatCircle(starboardPosition).middle(
after.getBearingGreatCircle(starboardPosition));
result.add(new Util.Pair<Position, Bearing>(starboardPosition, crossingStarboard));
}
} else { // single mark
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) {
b = w.getFixedBearing();
} else {
@@ -1351,23 +1357,23 @@ public class CandidateFinderImpl implements CandidateFinder {
// orthogonally to the adjacent leg:
if (w == race.getRace().getCourse().getFirstWaypoint()) {
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));
b = race.getTrackedLegStartingAt(w).getLegBearing(t).add(new DegreeBearingImpl(270));
b = markPositionCache.getLegBearing(race.getTrackedLegStartingAt(w)).add(new DegreeBearingImpl(270));
} 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()) {
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));
b = race.getTrackedLegFinishingAt(w).getLegBearing(t).add(new DegreeBearingImpl(270));
b = markPositionCache.getLegBearing(race.getTrackedLegFinishingAt(w)).add(new DegreeBearingImpl(270));
} 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 {
Bearing before = race.getTrackedLegFinishingAt(w).getLegBearing(t);
Bearing after = race.getTrackedLegStartingAt(w).getLegBearing(t);
Bearing before = markPositionCache.getLegBearing(race.getTrackedLegFinishingAt(w));
Bearing after = markPositionCache.getLegBearing(race.getTrackedLegStartingAt(w));
if (before != null && after != null) {
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
* 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>();
for (Mark mark : w.getMarks()) {
final Position estimatedMarkPosition = race.getOrCreateTrack(mark).getEstimatedPosition(t, /* extrapolate */
false);
final Position estimatedMarkPosition = markPositionCache.getEstimatedPosition(mark);
if (estimatedMarkPosition == 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);
}
final boolean isStartLine = indexOfWaypoint == 0;
final Bearing legDeterminingDirectionBearing = race.getTrackedLeg(
legs.get(isStartLine ? 0 : indexOfWaypoint - 1)).getLegBearing(t);
final Bearing legDeterminingDirectionBearing = markPositionCache.getLegBearing(race.getTrackedLeg(
legs.get(isStartLine ? 0 : indexOfWaypoint - 1)));
if (legDeterminingDirectionBearing == null) {
return new Util.Pair<Mark, Mark>(null, null);
}
@@ -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);
/**
* 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>,
* 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);
/**
* 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
* 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
* @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
@@ -49,6 +49,7 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.ranking.RankingMetric;
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.sse.common.Duration;
import com.sap.sse.common.IsManagedByCache;
@@ -316,7 +317,21 @@ 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>
* 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
@@ -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
*/
Iterable<RaceLog> getAttachedRaceLogs();
}
@@ -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;
}
}
@@ -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;
}
}
@@ -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.PolarDataService;
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.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -204,8 +205,15 @@ public class TrackedLegImpl implements TrackedLeg {
@Override
public Bearing getLegBearing(TimePoint at) {
Position startMarkPos = getTrackedRace().getApproximatePosition(getLeg().getFrom(), at);
Position endMarkPos = getTrackedRace().getApproximatePosition(getLeg().getTo(), at);
return getLegBearing(at, new MarkPositionAtTimePointCacheImpl(getTrackedRace(), 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;
return legBearing;
}
@@ -337,10 +345,12 @@ public class TrackedLegImpl implements TrackedLeg {
}
@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 Position approximatePositionOfFrom = getTrackedRace().getApproximatePosition(getLeg().getFrom(), timePoint);
final Position approximatePositionOfTo = getTrackedRace().getApproximatePosition(getLeg().getTo(), timePoint);
final Position approximatePositionOfFrom = markPositionCache.getApproximatePosition(getLeg().getFrom());
final Position approximatePositionOfTo = markPositionCache.getApproximatePosition(getLeg().getTo());
if (approximatePositionOfFrom != null && approximatePositionOfTo != null) {
result = approximatePositionOfFrom.getDistance(approximatePositionOfTo);
} else {
@@ -349,6 +359,11 @@ public class TrackedLegImpl implements TrackedLeg {
return result;
}
@Override
public Distance getGreatCircleDistance(TimePoint timePoint) {
return getGreatCircleDistance(timePoint, new NonCachingMarkPositionAtTimePointCache(getTrackedRace(), timePoint));
}
@Override
public Distance getAbsoluteWindwardDistance(Position pos1, Position pos2, TimePoint at, WindPositionMode windPositionMode) {
return getAbsoluteWindwardDistance(pos1, pos2, at, windPositionMode, new LeaderboardDTOCalculationReuseCache(at));
@@ -498,8 +513,10 @@ public class TrackedLegImpl implements TrackedLeg {
}
@Override
public Duration getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint)
public Duration getEstimatedTimeToComplete(PolarDataService polarDataService, TimePoint timepoint, MarkPositionAtTimePointCache markPositionCache)
throws NotEnoughDataHasBeenAddedException, NoWindException {
assert timepoint.equals(markPositionCache.getTimePoint());
assert getTrackedRace() == markPositionCache.getTrackedRace();
Position centralPosition = trackedRace.getCenterOfCourse(timepoint);
Wind wind = trackedRace.getWind(centralPosition, timepoint, getTrackedRace().getWindSources(WindSourceType.TRACK_BASED_ESTIMATION));
Position from = trackedRace.getApproximatePosition(leg.getFrom(), timepoint);
@@ -127,6 +127,7 @@ import com.sap.sailing.domain.tracking.GPSTrackListener;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Maneuver;
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.RaceExecutionOrderProvider;
import com.sap.sailing.domain.tracking.RaceListener;
@@ -1671,10 +1672,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
@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;
for (Mark mark : waypoint.getMarks()) {
Position nextPos = getOrCreateTrack(mark).getEstimatedPosition(timePoint, /* extrapolate */false);
Position nextPos = markPositionCache.getEstimatedPosition(mark);
if (result == null) {
result = nextPos;
} 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.");
}
Duration durationOfAllLegs = Duration.NULL;
final MarkPositionAtTimePointCache markPositionCache = new MarkPositionAtTimePointCacheImpl(this, timepoint);
for (TrackedLeg leg : trackedLegs.values()) {
Duration durationOfLeg = leg.getEstimatedTimeToComplete(polarDataService, timepoint);
Duration durationOfLeg = leg.getEstimatedTimeToComplete(polarDataService, timepoint, markPositionCache);
durationOfAllLegs = durationOfAllLegs.plus(durationOfLeg);
}
return durationOfAllLegs;
@@ -50,6 +50,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Maneuver;
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.RaceChangeListener;
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
@@ -352,7 +353,7 @@ public class MockedTrackedRace implements DynamicTrackedRace {
}
@Override
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
return null;
}
@@ -41,6 +41,7 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Maneuver;
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.RaceChangeListener;
import com.sap.sailing.domain.tracking.RaceExecutionOrderProvider;
@@ -213,7 +214,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
}
@Override
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint) {
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
return null;
}