Merge branch 'master' into bug4370

This commit is contained in:
Axel Uhl committed 2017-11-22 09:59:00 +01:00
commit 85c5866d90
11 files changed
+151 -27

No files matched your search

@@ -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;
}
}
@@ -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;
}
}
@@ -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);
@@ -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);
}
@@ -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;
}
@@ -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)) {
@@ -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;
}
}
@@ -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;
}
}
@@ -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) {
@@ -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;
}
}