diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java index 1eab1c2ace2..9d525058fea 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java @@ -3,10 +3,13 @@ package com.sap.sailing.domain.common.dto; import java.io.Serializable; import java.util.List; +import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sse.common.Duration; +import com.sap.sse.common.impl.MillisecondsDurationImpl; /** * Holds a single competitor's scoring details for a single race. It may optionally contain @@ -78,6 +81,8 @@ public class LeaderboardEntryDTO implements Serializable { */ public Duration averageSamplingInterval; + public Double averageRideHeightInMeters; + /** * The time gap to the competitor leading the race; for one-design races this is the time the competitor is expected * to need to reach the leader's (windward) position (if in the same leg) or the difference between the time at @@ -132,6 +137,44 @@ public class LeaderboardEntryDTO implements Serializable { public boolean hasScoreCorrection() { return totalPointsCorrected || (reasonForMaxPoints != null && reasonForMaxPoints != MaxPointsReason.NONE); } + + public Duration getTimeSailedInMillis() { + final Duration result; + if (legDetails != null) { + long timeInMilliseconds = 0; + for (LegEntryDTO legDetail : legDetails) { + if (legDetail != null) { + if (legDetail.distanceTraveledInMeters != null && legDetail.timeInMilliseconds != null) { + timeInMilliseconds += legDetail.timeInMilliseconds; + } else { + timeInMilliseconds = 0; + break; + } + } + } + result = new MillisecondsDurationImpl(timeInMilliseconds); + } else { + result = null; + } + return result; + } + + public Distance getDistanceTraveledInMeters() { + Distance result = null; + if (legDetails != null) { + for (LegEntryDTO legDetail : legDetails) { + if (legDetail != null) { + if (legDetail.distanceTraveledInMeters != null) { + if (result == null) { + result = Distance.NULL; + } + result = result.add(new MeterDistance(legDetail.distanceTraveledInMeters)); + } + } + } + } + return result; + } @Override public int hashCode() { @@ -174,6 +217,7 @@ public class LeaderboardEntryDTO implements Serializable { result = prime * result + ((speedOverGroundAtStartOfRaceInKnots == null) ? 0 : speedOverGroundAtStartOfRaceInKnots.hashCode()); result = prime * result + ((startTack == null) ? 0 : startTack.hashCode()); + result = prime * result + ((averageRideHeightInMeters == null) ? 0 : averageRideHeightInMeters.hashCode()); result = prime * result + ((averageSamplingInterval == null) ? 0 : averageSamplingInterval.hashCode()); result = prime * result + ((timeSinceLastPositionFixInSeconds == null) ? 0 : timeSinceLastPositionFixInSeconds.hashCode()); result = prime * result + ((netPoints == null) ? 0 : netPoints.hashCode()); @@ -282,6 +326,11 @@ public class LeaderboardEntryDTO implements Serializable { return false; if (startTack != other.startTack) return false; + if (averageRideHeightInMeters == null) { + if (other.averageRideHeightInMeters != null) + return false; + } else if (!averageRideHeightInMeters.equals(other.averageRideHeightInMeters)) + return false; if (averageSamplingInterval == null) { if (other.averageSamplingInterval != null) return false; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java index 1f5036c8083..a5db8b41249 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardRowDTO.java @@ -4,6 +4,9 @@ import java.io.Serializable; import java.util.Date; import java.util.Map; +import com.sap.sailing.domain.common.Distance; +import com.sap.sse.common.Duration; + /** * Holds data about one competitor and all races represented by the owning {@link LeaderboardDTO leaderboard}. * @@ -117,6 +120,28 @@ public class LeaderboardRowDTO implements Serializable { return true; } + public Distance getDistanceTraveledInMeters(String raceColumnName) { + final Distance result; + LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName); + if (fieldsForRace != null) { + result = fieldsForRace.getDistanceTraveledInMeters(); + } else { + result = null; + } + return result; + } + + public Duration getTimeSailed(String raceColumnName) { + final Duration result; + LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName); + if (fieldsForRace != null) { + result = fieldsForRace.getTimeSailedInMillis(); + } else { + result = null; + } + return result; + } + @Override public String toString() { return "LeaderboardRowDTO for competitor "+competitor; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoFix.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoFix.java index 73dd3504886..22564e68cc3 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoFix.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/BravoFix.java @@ -1,5 +1,7 @@ package com.sap.sailing.domain.common.tracking; +import com.sap.sailing.domain.common.Distance; + /** * Specific {@link SensorFix} for bravo devices (http://www.bravosystems.es/). * @@ -12,5 +14,5 @@ public interface BravoFix extends SensorFix { * * @return the ride height */ - double getRideHeight(); + Distance getRideHeight(); } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoFixImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoFixImpl.java index 99ba3015f75..c83402e7545 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoFixImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/BravoFixImpl.java @@ -1,5 +1,7 @@ package com.sap.sailing.domain.common.tracking.impl; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata; import com.sap.sailing.domain.common.tracking.BravoFix; import com.sap.sailing.domain.common.tracking.DoubleVectorFix; @@ -32,8 +34,8 @@ public class BravoFixImpl implements BravoFix { } @Override - public double getRideHeight() { - return fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightColumn); + public Distance getRideHeight() { + return new MeterDistance(fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightColumn)); } } diff --git a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java index 0fe9590a085..0d5f03e9a81 100644 --- a/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java +++ b/java/com.sap.sailing.domain.racelogtrackingadapter.test/src/com/sap/sailing/domain/racelogtracking/test/impl/SensorFixStoreAndLoadTest.java @@ -49,9 +49,11 @@ import com.sap.sailing.domain.base.impl.CourseImpl; import com.sap.sailing.domain.base.impl.RaceDefinitionImpl; import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.WaypointImpl; +import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata; import com.sap.sailing.domain.common.tracking.DoubleVectorFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; @@ -95,9 +97,9 @@ public class SensorFixStoreAndLoadTest { private static final long FIX_TIMESTAMP2 = 120; private static final long FIX_TIMESTAMP3 = 210; private static final long AFTER_LAST_FIX = FIX_TIMESTAMP3 + 1; - private static final double FIX_RIDE_HEIGHT = 1337.0; - private static final double FIX_RIDE_HEIGHT2 = 1338.0; - private static final double FIX_RIDE_HEIGHT3 = 1336.0; + private static final Distance FIX_RIDE_HEIGHT = new MeterDistance(1337.0); + private static final Distance FIX_RIDE_HEIGHT2 = new MeterDistance(1338.0); + private static final Distance FIX_RIDE_HEIGHT3 = new MeterDistance(1336.0); private static final double FIX_TEST_VALUE = 12.0; protected final MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory(); protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a"); @@ -384,20 +386,14 @@ public class SensorFixStoreAndLoadTest { public void testBravoFixIsCorrectlyWrapped() throws InterruptedException { regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp, device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING))); - addBravoFixes(); - FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker(); - trackedRace.attachRaceLog(raceLog); trackedRace.attachRegattaLog(regattaLog); - trackedRace.waitForLoadingToFinish(); - BravoFixTrack bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME); assertEquals(FIX_RIDE_HEIGHT, bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP)).getRideHeight()); - fixLoaderAndTracker.stop(true); } @@ -482,9 +478,9 @@ public class SensorFixStoreAndLoadTest { } private void addBravoFixes() { - store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP, FIX_RIDE_HEIGHT)); - store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2)); - store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3)); + store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP, FIX_RIDE_HEIGHT.getMeters())); + store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2.getMeters())); + store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3.getMeters())); } private FixLoaderAndTracker createFixLoaderAndTracker() { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index e0119115587..ba165f37cc1 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -541,15 +541,12 @@ public class MockedTrackedRace implements DynamicTrackedRace { @Override public boolean isControlTrackingFromStartAndFinishTimes() { - // TODO Auto-generated method stub return false; } @Override public void setControlTrackingFromStartAndFinishTimes( boolean controlTrackingFromStartAndFinishTimes) { - // TODO Auto-generated method stub - } }; } @@ -1130,4 +1127,9 @@ public class MockedTrackedRace implements DynamicTrackedRace { public Iterable getAttachedRaceLogs() { return Collections.emptySet(); } + + @Override + public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) { + return null; + } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java index fefad810f2b..30cdeb5e6ac 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java @@ -681,4 +681,9 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { public Iterable getAttachedRaceLogs() { return Collections.emptySet(); } + + @Override + public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) { + return null; + } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackSerializationTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackSerializationTest.java index 2a0f19e5877..0ab5678b885 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackSerializationTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/tracking/impl/BravoFixTrackSerializationTest.java @@ -82,8 +82,8 @@ public class BravoFixTrackSerializationTest { assertRideHeight(track, testFixData.timePoint, testFixData.rideHeight); } - private void assertRideHeight(BravoFixTrack track, TimePoint timePoint, double expected) { - assertEquals(expected, track.getRideHeight(timePoint), 0.0); + private void assertRideHeight(BravoFixTrack track, TimePoint timePoint, double expectedRideHeightInMeters) { + assertEquals(expectedRideHeightInMeters, track.getRideHeight(timePoint).getMeters(), 0.0); } @SuppressWarnings("unchecked") diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 083e5915d18..471d4f5ff89 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -1437,6 +1437,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race } else { entryDTO.timeSinceLastPositionFixInSeconds = null; } + final Distance averageRideHeight = trackedRace.getAverageRideHeight(competitor, timePoint); + entryDTO.averageRideHeightInMeters = averageRideHeight == null ? null : averageRideHeight.getMeters(); } if (addLegDetails && trackedRace != null) { try { @@ -1721,11 +1723,13 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race Double speedOverGroundInKnots; if (trackedLeg.hasFinishedLeg(timePoint)) { speedOverGroundInKnots = averageSpeedOverGround == null ? null : averageSpeedOverGround.getKnots(); - result.currentRideHeightInMeters = trackedLeg.getAverageRideHeight(timePoint); + final Distance averageRideHeight = trackedLeg.getAverageRideHeight(timePoint); + result.currentRideHeightInMeters = averageRideHeight == null ? null : averageRideHeight.getMeters(); } else { final SpeedWithBearing speedOverGround = trackedLeg.getSpeedOverGround(timePoint); speedOverGroundInKnots = speedOverGround == null ? null : speedOverGround.getKnots(); - result.currentRideHeightInMeters = trackedLeg.getRideHeight(timePoint); + final Distance rideHeight = trackedLeg.getRideHeight(timePoint); + result.currentRideHeightInMeters = rideHeight == null ? null : rideHeight.getMeters(); } result.currentSpeedOverGroundInKnots = speedOverGroundInKnots == null ? null : speedOverGroundInKnots; Distance distanceTraveled = trackedLeg.getDistanceTraveled(timePoint); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/BravoFixTrack.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/BravoFixTrack.java index fcd4163de00..c5b77ab96b1 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/BravoFixTrack.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/BravoFixTrack.java @@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking; import java.io.Serializable; +import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.tracking.BravoFix; import com.sap.sse.common.TimePoint; import com.sap.sse.common.WithID; @@ -22,7 +23,7 @@ public interface BravoFixTrack extends S * @return the ride height for the given {@link TimePoint} or null if the {@link TimePoint} is not in the range * where we have fixes for. */ - Double getRideHeight(TimePoint timePoint); + Distance getRideHeight(TimePoint timePoint); /** * Calculates the average ride height for the given time range. @@ -34,5 +35,5 @@ public interface BravoFixTrack extends S * @return the average ride height for the given time range or null if there are no fixes in the given * time range. */ - Double getAverageRideHeight(TimePoint from, TimePoint to); + Distance getAverageRideHeight(TimePoint from, TimePoint to); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java index c4a8ab28c45..dc47af1cce1 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java @@ -97,7 +97,7 @@ public interface TrackedLegOfCompetitor extends Serializable { * timePoint. If the competitor already completed the leg at timePoint and the respective * mark passing event was already received, the average ride height for the entire leg (and no further) is computed. */ - Double getAverageRideHeight(TimePoint timePoint); + Distance getAverageRideHeight(TimePoint timePoint); /** * @return null if the competitor hasn't started this leg yet, otherwise the fix where the maximum speed was @@ -237,7 +237,7 @@ public interface TrackedLegOfCompetitor extends Serializable { * timePoint, returns the current ride height for this time point. If the competitor has already * finished the leg, the ride height at the time the competitor finished the leg is returned. */ - Double getRideHeight(TimePoint at); + Distance getRideHeight(TimePoint at); /** * Computes the distance along the wind track to the wind-projected position of the race's overall leader. If leader diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index b9f36f9e1aa..d864ae66e80 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -47,6 +47,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.common.tracking.SensorFix; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; +import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.ranking.RankingMetric; import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache; @@ -685,6 +686,8 @@ public interface TrackedRace extends Serializable, IsManagedByCache extends S } @Override - public Double getRideHeight(TimePoint timePoint) { + public Distance getRideHeight(TimePoint timePoint) { BravoFix fixAfter = getFirstFixAtOrAfter(timePoint); if (fixAfter != null && fixAfter.getTimePoint().compareTo(timePoint) == 0) { // exact match of timepoint -> no interpolation necessary @@ -54,17 +52,24 @@ public class BravoFixTrackImpl extends S } @Override - public Double getAverageRideHeight(TimePoint from, TimePoint to) { + public Distance getAverageRideHeight(TimePoint from, TimePoint to) { + final Distance result; lockForRead(); try { - Spliterator fixes = getFixes(from, true, to, true).spliterator(); - OptionalDouble average = StreamSupport.stream(fixes, false).mapToDouble(BravoFix::getRideHeight).average(); - if (average.isPresent()) { - return average.getAsDouble(); + Distance sum = Distance.NULL; + int count = 0; + for (final BravoFix fix : getFixes(from, true, to, true)) { + sum = sum.add(fix.getRideHeight()); + count++; + } + if (count > 0) { + result = sum.scale(1./count); + } else { + result = null; } } finally { unlockAfterRead(); } - return null; + return result; } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java index 1807ba07df7..fba23c8af4a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java @@ -174,8 +174,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { } if (timePointToUse != null) { Distance d = getDistanceTraveled(timePointToUse); - long millis = timePointToUse.asMillis() - legStart.getTimePoint().asMillis(); - result = d.inTime(millis); + result = d.inTime(legStart.getTimePoint().until(timePointToUse)); } else { result = null; } @@ -184,7 +183,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { } @Override - public Double getAverageRideHeight(TimePoint timePoint) { + public Distance getAverageRideHeight(TimePoint timePoint) { MarkPassing legStart = getMarkPassingForLegStart(); if (legStart != null) { BravoFixTrack track = getTrackedRace() @@ -677,15 +676,17 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { } @Override - public Double getRideHeight(TimePoint at) { + public Distance getRideHeight(TimePoint at) { + final Distance result; if (hasStartedLeg(at)) { TimePoint timePoint =hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at; BravoFixTrack track = getTrackedRace() .> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME); - return track == null ? null : track.getRideHeight(timePoint); + result = track == null ? null : track.getRideHeight(timePoint); } else { - return null; + result = null; } + return result; } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index da48741957d..bcb33ab5215 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -1538,6 +1538,22 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl return result; } + @Override + public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) { + final Distance result; + BravoFixTrack track = getSensorTrack(competitor, BravoFixTrack.TRACK_NAME); + final Leg firstLeg; + final TrackedLegOfCompetitor firstTrackedLeg; + if (track != null && (firstLeg = getRace().getCourse().getFirstLeg()) != null && (firstTrackedLeg = getTrackedLeg(competitor, firstLeg)).hasStartedLeg(timePoint)) { + final TrackedLegOfCompetitor lastTrackedLeg = getTrackedLegFinishingAt(getRace().getCourse().getLastWaypoint()).getTrackedLeg(competitor); + TimePoint endTimePoint = lastTrackedLeg.hasFinishedLeg(timePoint) ? lastTrackedLeg.getFinishTime() : timePoint; + result = track.getAverageRideHeight(firstTrackedLeg.getStartTime(), endTimePoint); + } else { + result = null; + } + return result; + } + @Override public TrackedLegOfCompetitor getCurrentLeg(Competitor competitor, TimePoint timePoint) { // If the mark passing that starts a leg happened exactly at timePoint, the MarkPassingByTimeComparator won't consider diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java index b58478a3a82..00050caffca 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java @@ -860,4 +860,9 @@ public class MockedTrackedRace implements DynamicTrackedRace { public Iterable getAttachedRaceLogs() { return Collections.emptySet(); } + + @Override + public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) { + return null; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index a6ab0f59476..3d4056386e1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -58,6 +58,7 @@ import com.google.gwt.view.client.MultiSelectionModel; import com.google.gwt.view.client.SelectionChangeEvent; import com.google.gwt.view.client.SelectionChangeEvent.Handler; import com.sap.sailing.domain.common.DetailType; +import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.InvertibleComparator; import com.sap.sailing.domain.common.LeaderboardNameConstants; import com.sap.sailing.domain.common.MaxPointsReason; @@ -97,6 +98,7 @@ import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil; import com.sap.sailing.gwt.ui.leaderboard.DetailTypeColumn.LegDetailField; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies; import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO; +import com.sap.sse.common.Duration; import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; import com.sap.sse.common.filter.BinaryOperator; @@ -972,10 +974,10 @@ public class LeaderboardPanel extends SimplePanel implements Component { @Override public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null && fieldsForRace.legDetails != null) { - double distanceTraveledInMeters = 0; - long timeInMilliseconds = 0; - for (LegEntryDTO legDetail : fieldsForRace.legDetails) { - if (legDetail != null) { - if (legDetail.distanceTraveledInMeters != null && legDetail.timeInMilliseconds != null) { - distanceTraveledInMeters += legDetail.distanceTraveledInMeters; - timeInMilliseconds += legDetail.timeInMilliseconds; - } else { - distanceTraveledInMeters = 0; - timeInMilliseconds = 0; - break; - } - } - } - if (timeInMilliseconds != 0) { - result = distanceTraveledInMeters / (double) timeInMilliseconds * 1000 * 3600 / 1852; - } + final Distance distanceTraveledInMeters = row.getDistanceTraveledInMeters(getRaceColumnName()); + final Duration time = row.getTimeSailed(getRaceColumnName()); + final Double result; + if (distanceTraveledInMeters != null && time != null) { + result = distanceTraveledInMeters.inTime(time).getKnots(); + } else { + result = null; } return result; } @@ -1299,21 +1288,7 @@ public class LeaderboardPanel extends SimplePanel implements Component { @Override public Double get(LeaderboardRowDTO row) { - Double result = null; - LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null && fieldsForRace.legDetails != null) { - for (LegEntryDTO legDetail : fieldsForRace.legDetails) { - if (legDetail != null) { - if (legDetail.distanceTraveledInMeters != null) { - if (result == null) { - result = 0.0; - } - result += legDetail.distanceTraveledInMeters; - } - } - } - } - return result; + return row.getDistanceTraveledInMeters(getRaceColumnName()).getMeters(); } } @@ -1446,27 +1421,48 @@ public class LeaderboardPanel extends SimplePanel implements Component { + private class RaceCurrentSpeedOverGroundInKnots extends AbstractLastLegDetailField { @Override - protected Double get(LegEntryDTO lastLegDetail) { - return lastLegDetail.currentSpeedOverGroundInKnots; + protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) { + return currentLegDetail.currentSpeedOverGroundInKnots; + } + + @Override + protected Double getAfterLastLegFinished(LeaderboardRowDTO row) { + return new RaceAverageSpeedInKnots().get(row); } } - private class CurrentRideHeightInMeters extends AbstractLastLegDetailField { + private class RaceCurrentRideHeightInMeters extends AbstractLastLegDetailField { @Override - protected Double get(LegEntryDTO lastLegDetail) { - return lastLegDetail.currentRideHeightInMeters; + protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) { + return currentLegDetail.currentRideHeightInMeters; + } + + @Override + protected Double getAfterLastLegFinished(LeaderboardRowDTO row) { + Double result = null; + LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); + if (fieldsForRace != null) { + result = fieldsForRace.averageRideHeightInMeters; + } + return result; } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index cdcc7d02788..ffcf6f905f3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -2941,7 +2941,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final BravoFixTrack bravoFixTrack = trackedRace .> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME); if (bravoFixTrack != null) { - result = bravoFixTrack.getRideHeight(timePoint); + final Distance rideHeight = bravoFixTrack.getRideHeight(timePoint); + result = rideHeight == null ? null : rideHeight.getMeters(); } break; default: diff --git a/java/com.sap.sailing.server.trackfiles.test/src/com/sap/sailing/server/trackfiles/test/BravoDataImportTest.java b/java/com.sap.sailing.server.trackfiles.test/src/com/sap/sailing/server/trackfiles/test/BravoDataImportTest.java index 5fe5f150b5f..df3e94c8a5d 100644 --- a/java/com.sap.sailing.server.trackfiles.test/src/com/sap/sailing/server/trackfiles/test/BravoDataImportTest.java +++ b/java/com.sap.sailing.server.trackfiles.test/src/com/sap/sailing/server/trackfiles/test/BravoDataImportTest.java @@ -19,12 +19,12 @@ public class BravoDataImportTest { private final DoubleVectorFixImporter bravoDataImporter = new BravoDataImporterImpl(); private int callbackCallCount = 0; - private double sumRideHeight = 0.0; + private double sumRideHeightInMeters = 0.0; @Before public void setUp() { this.callbackCallCount = 0; - this.sumRideHeight = 0.0; + this.sumRideHeightInMeters = 0.0; } @Test @@ -51,12 +51,12 @@ public class BravoDataImportTest { bravoDataImporter.importFixes(importData.getInputStream(), (fixes, device) -> { for (DoubleVectorFix fix : fixes) { callbackCallCount++; - sumRideHeight += new BravoFixImpl(fix).getRideHeight(); + sumRideHeightInMeters += new BravoFixImpl(fix).getRideHeight().getMeters(); } }, "filename", "source"); Assert.assertEquals(importData.expectedFixesCount, callbackCallCount); Assert.assertEquals(importData.expectedAverageRideHeight, - callbackCallCount == 0 ? sumRideHeight : sumRideHeight / callbackCallCount, 0.00001); + callbackCallCount == 0 ? sumRideHeightInMeters : sumRideHeightInMeters / callbackCallCount, 0.00001); } private enum ImportData { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java index c5be8205189..f7c5d1462db 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java @@ -656,4 +656,9 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { public Iterable getAttachedRaceLogs() { return Collections.emptySet(); } + + @Override + public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) { + return null; + } }