mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-18 02:00:58 +00:00
Merge branch 'master' into bug4370
This commit is contained in:
commit
85c5866d90
11 files changed
+151
-27
No files matched your search
+6
@@ -1165,4 +1165,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
|
||||
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+6
@@ -718,4 +718,10 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
||||
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
|
||||
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+1
-1
@@ -869,7 +869,7 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard {
|
||||
if (trackedLeg.hasFinishedLeg(timePoint)) {
|
||||
velocityMadeGood = trackedLeg.getAverageVelocityMadeGood(timePoint);
|
||||
} else {
|
||||
velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT);
|
||||
velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache);
|
||||
}
|
||||
result.velocityMadeGoodInKnots = velocityMadeGood == null ? null : velocityMadeGood.getKnots();
|
||||
Distance windwardDistanceToGo = trackedLeg.getWindwardDistanceToGo(timePoint, WindPositionMode.LEG_MIDDLE);
|
||||
|
||||
+1
-1
@@ -211,7 +211,7 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
* Same as {@link #getVelocityMadeGood(TimePoint, WindPositionMode)}, only that a cache for wind data and leg type and bearing
|
||||
* is passed.
|
||||
*/
|
||||
Speed getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache);
|
||||
SpeedWithBearing getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache);
|
||||
|
||||
|
||||
/**
|
||||
|
||||
@@ -46,6 +46,7 @@ import com.sap.sailing.domain.common.racelog.RacingProcedureType;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.common.tracking.SensorFix;
|
||||
import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache;
|
||||
import com.sap.sailing.domain.markpassingcalculation.MarkPassingCalculator;
|
||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
@@ -1033,4 +1034,42 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
||||
* time point up and until to compute the speed
|
||||
*/
|
||||
Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Computes the competitor's speed projected onto the wind (if wind data is available and the competitor is not
|
||||
* between start and finish (not racing) or not on a {@link LegType#REACHING reaching} leg; if outside a race and no
|
||||
* wind information is available, {@code null} is returned. Otherwise, the speed at {@code timePoint} is projected
|
||||
* onto the course. The wind direction at the {@link WindPositionMode#EXACT exact} competitor position at
|
||||
* {@code timePoint} is used for the calculation.
|
||||
*/
|
||||
default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint) {
|
||||
return getVelocityMadeGood(competitor, timePoint, new LeaderboardDTOCalculationReuseCache(timePoint));
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a {@link WindPositionMode}
|
||||
* other than the default {@link WindPositionMode#EXACT}. If {@link WindPositionMode#LEG_MIDDLE} is used and the
|
||||
* competitor is not currently sailing on a leg (hasn't started or has already finished), {@code null} is returned.
|
||||
*/
|
||||
default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) {
|
||||
return getVelocityMadeGood(competitor, timePoint, windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint));
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a cache that can
|
||||
* accelerate requests for wind directions, the leg type and the competitor's current leg's bearing.
|
||||
*/
|
||||
default SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) {
|
||||
return getVelocityMadeGood(competitor, timePoint, WindPositionMode.EXACT, cache);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like {@link #getVelocityMadeGood(Competitor, TimePoint)}, but allowing callers to specify a
|
||||
* {@link WindPositionMode} other than the default {@link WindPositionMode#EXACT} as well as a cache that can
|
||||
* accelerate requests for wind directions, the leg type and the competitor's current leg's bearing. If
|
||||
* {@link WindPositionMode#LEG_MIDDLE} is used and the competitor is not currently sailing on a leg (hasn't started
|
||||
* or has already finished), {@code null} is returned.
|
||||
*/
|
||||
SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode,
|
||||
WindLegTypeAndLegBearingCache cache);
|
||||
}
|
||||
+1
-2
@@ -44,7 +44,6 @@ 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;
|
||||
import com.sap.sailing.domain.tracking.TrackedRaceStatus;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
@@ -98,7 +97,7 @@ public class TrackedLegImpl implements TrackedLeg {
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackedRace getTrackedRace() {
|
||||
public TrackedRaceImpl getTrackedRace() {
|
||||
return trackedRace;
|
||||
}
|
||||
|
||||
|
||||
+19
-17
@@ -29,7 +29,6 @@ import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sse.common.Duration;
|
||||
@@ -68,7 +67,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
return trackedLeg.getLeg();
|
||||
}
|
||||
|
||||
private TrackedRace getTrackedRace() {
|
||||
private TrackedRaceImpl getTrackedRace() {
|
||||
return getTrackedLeg().getTrackedRace();
|
||||
}
|
||||
|
||||
@@ -268,29 +267,28 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
|
||||
/**
|
||||
* Projects <code>speed</code> onto the wind direction for upwind/downwind legs to see how fast a boat travels
|
||||
* "along the wind's direction." For reaching legs (neither upwind nor downwind), the speed is projected onto
|
||||
* the leg's direction.
|
||||
* @param windPositionMode see {@link #getWind(Position, TimePoint, Set)}
|
||||
* "along the wind's direction." For reaching legs (neither upwind nor downwind), the speed is projected onto the
|
||||
* leg's direction.
|
||||
*
|
||||
* @throws NoWindException in case the wind direction is not known
|
||||
* @param speed
|
||||
* if {@code null} then {@code null} will be returned
|
||||
* @param windPositionMode
|
||||
* see {@link #getWind(Position, TimePoint, Set)}
|
||||
*
|
||||
* @throws NoWindException
|
||||
* in case the wind direction is not known
|
||||
*/
|
||||
private SpeedWithBearing getWindwardSpeed(SpeedWithBearing speed, final TimePoint at, WindPositionMode windPositionMode,
|
||||
WindLegTypeAndLegBearingCache cache) {
|
||||
SpeedWithBearing result = null;
|
||||
final SpeedWithBearing result;
|
||||
if (speed != null) {
|
||||
Bearing projectToBearing;
|
||||
try {
|
||||
if (cache.getLegType(getTrackedLeg(), at) != LegType.REACHING) {
|
||||
final Wind wind;
|
||||
if (windPositionMode == WindPositionMode.EXACT) {
|
||||
wind = cache.getWind(getTrackedRace(), getCompetitor(), at);
|
||||
} else {
|
||||
wind = getTrackedRace().getWind(
|
||||
getTrackedLeg().getEffectiveWindPosition(
|
||||
() -> getTrackedRace().getTrack(getCompetitor())
|
||||
.getEstimatedPosition(at, false), at, windPositionMode), at);
|
||||
}
|
||||
final Wind wind = getTrackedRace().getWind(windPositionMode, getTrackedLeg(), getCompetitor(), at, cache);
|
||||
if (wind == null) {
|
||||
// This is not really likely to happen because wind==null would have let the call
|
||||
// to cache.getLegType(...) fail with a NoWindException
|
||||
throw new NoWindException("Need at least wind direction to determine windward speed");
|
||||
}
|
||||
projectToBearing = wind.getBearing();
|
||||
@@ -307,7 +305,11 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
projectToBearing = projectToBearing.reverse();
|
||||
}
|
||||
result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -645,7 +647,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
public SpeedWithBearing getVelocityMadeGood(TimePoint at, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
if (hasStartedLeg(at)) {
|
||||
TimePoint timePoint;
|
||||
if (hasFinishedLeg(at)) {
|
||||
|
||||
+60
-4
@@ -3608,12 +3608,14 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
|
||||
@Override
|
||||
public Speed getSpeed(Competitor competitor, long millisecondsBeforeRaceStart) {
|
||||
final Speed result;
|
||||
if (getStartOfRace() == null) {
|
||||
return null;
|
||||
result = null;
|
||||
} else {
|
||||
TimePoint beforeStart = new MillisecondsTimePoint(getStartOfRace().asMillis() - millisecondsBeforeRaceStart);
|
||||
result = getTrack(competitor).getEstimatedSpeed(beforeStart);
|
||||
}
|
||||
|
||||
TimePoint beforeStart = new MillisecondsTimePoint(getStartOfRace().asMillis() - millisecondsBeforeRaceStart);
|
||||
return getTrack(competitor).getEstimatedSpeed(beforeStart);
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -4245,4 +4247,58 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
|
||||
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
TrackedLegOfCompetitor trackedLeg = getTrackedLeg(competitor, timePoint);
|
||||
final SpeedWithBearing result;
|
||||
if (trackedLeg != null) {
|
||||
result = trackedLeg.getVelocityMadeGood(timePoint, windPositionMode, cache);
|
||||
} else {
|
||||
// check if wind information is available; if so, compute a VMG only based on wind data:
|
||||
if (windPositionMode == WindPositionMode.LEG_MIDDLE) {
|
||||
result = null;
|
||||
} else {
|
||||
final Wind wind = getWind(windPositionMode, /* trackedLeg */ null, competitor, timePoint, cache);
|
||||
result = projectOnto(getTrack(competitor).getEstimatedSpeed(timePoint), wind.getBearing());
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
SpeedWithBearing projectOnto(SpeedWithBearing speed, Bearing projectToBearing) {
|
||||
final SpeedWithBearing result;
|
||||
if (speed.getBearing() != null && projectToBearing != null) {
|
||||
double cos = Math.cos(speed.getBearing().getRadians() - projectToBearing.getRadians());
|
||||
if (cos < 0) {
|
||||
projectToBearing = projectToBearing.reverse();
|
||||
}
|
||||
result = new KnotSpeedWithBearingImpl(Math.abs(speed.getKnots() * cos), projectToBearing);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param trackedLeg
|
||||
* The caller is expected to obtain the {@link #getTrackedLeg(Competitor, TimePoint) tracked leg} the
|
||||
* {@code competitor} is sailing in at time point {@code at}. If {@code null}, any
|
||||
* non-{@link WindPositionMode#EXACT exact} wind position mode will use {@code null} for the wind
|
||||
* position, defaulting to the "COMBINED" wind source at the middle of the course. In particular, it then
|
||||
* obviously makes no real sense to request {@link WindPositionMode#LEG_MIDDLE} because no leg is known.
|
||||
*/
|
||||
Wind getWind(WindPositionMode windPositionMode, TrackedLegImpl trackedLeg, Competitor competitor, TimePoint at, WindLegTypeAndLegBearingCache cache) {
|
||||
final Wind wind;
|
||||
if (windPositionMode == WindPositionMode.EXACT) {
|
||||
wind = cache.getWind(this, competitor, at);
|
||||
} else {
|
||||
wind = getWind(trackedLeg == null ? null :
|
||||
trackedLeg.getEffectiveWindPosition(
|
||||
() -> getTrack(competitor)
|
||||
.getEstimatedPosition(at, false), at, windPositionMode), at);
|
||||
}
|
||||
return wind;
|
||||
}
|
||||
}
|
||||
+6
@@ -896,4 +896,10 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
|
||||
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
+6
-2
@@ -2897,10 +2897,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
}
|
||||
break;
|
||||
case VELOCITY_MADE_GOOD_IN_KNOTS:
|
||||
final Speed velocityMadeGood;
|
||||
if (trackedLeg != null) {
|
||||
Speed velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache);
|
||||
result = (velocityMadeGood == null) ? null : velocityMadeGood.getKnots();
|
||||
velocityMadeGood = trackedLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache);
|
||||
} else {
|
||||
// check if wind information is available; if so, compute a VMG only based on wind data:
|
||||
velocityMadeGood = trackedRace.getVelocityMadeGood(competitor, timePoint, cache);
|
||||
}
|
||||
result = (velocityMadeGood == null) ? null : velocityMadeGood.getKnots();
|
||||
break;
|
||||
case DISTANCE_TRAVELED:
|
||||
if (trackedLeg != null) {
|
||||
|
||||
+6
@@ -699,4 +699,10 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
public Speed getAverageSpeedOverGround(Competitor competitor, TimePoint timePoint) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getVelocityMadeGood(Competitor competitor, TimePoint timePoint,
|
||||
WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) {
|
||||
return null;
|
||||
}
|
||||
}
|
||||
Reference in new issue
Block a user