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.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;
|
||||
}
|
||||
|
||||
|
||||
+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.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);
|
||||
|
||||
}
|
||||
|
||||
+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.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);
|
||||
|
||||
+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.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;
|
||||
}
|
||||
|
||||
|
||||
+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.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;
|
||||
}
|
||||
|
||||
|
||||
+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.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);
|
||||
}
|
||||
|
||||
+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);
|
||||
|
||||
/**
|
||||
* 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();
|
||||
|
||||
}
|
||||
+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.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);
|
||||
|
||||
+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.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;
|
||||
|
||||
+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.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;
|
||||
}
|
||||
|
||||
|
||||
+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.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;
|
||||
}
|
||||
|
||||
|
||||
Reference in new issue
Block a user