diff --git a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/WindFixesFinderTest.java b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/WindFixesFinderTest.java index 486fa85b4c9..de4ff28eaaa 100644 --- a/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/WindFixesFinderTest.java +++ b/java/com.sap.sailing.domain.shared.android.test/src/com/sap/sailing/domain/racelog/analyzing/test/WindFixesFinderTest.java @@ -29,7 +29,7 @@ public class WindFixesFinderTest extends RaceLogAnalyzerTest { raceLog.add(event1); - List windList = analyzer.analyze(); + List windList = analyzer.analyze(); assertNotNull(windList); assertEquals(0, windList.size()); } @@ -46,10 +46,10 @@ public class WindFixesFinderTest extends RaceLogAnalyzerTest { raceLog.add(event2); raceLog.add(event3); - List windList = analyzer.analyze(); + List windList = analyzer.analyze(); assertNotNull(windList); assertEquals(2, windList.size()); - assertEquals(event3.getWindFix(), windList.get(0)); - assertEquals(event2.getWindFix(), windList.get(1)); + assertEquals(event3.getWindFix(), windList.get(0).getWindFix()); + assertEquals(event2.getWindFix(), windList.get(1).getWindFix()); } } diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/WindFixesFinder.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/WindFixesFinder.java index d797cf46f44..f01b4243af2 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/WindFixesFinder.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/abstractlog/race/analyzing/impl/WindFixesFinder.java @@ -6,24 +6,22 @@ import java.util.List; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogWindFixEvent; -import com.sap.sailing.domain.common.Wind; -public class WindFixesFinder extends RaceLogAnalyzer> { +public class WindFixesFinder extends RaceLogAnalyzer> { public WindFixesFinder(RaceLog raceLog) { super(raceLog); } @Override - protected List performAnalysis() { - final List windFixes = new ArrayList(); + protected List performAnalysis() { + final List windFixes = new ArrayList<>(); for (RaceLogEvent event : getAllEventsDescending()) { if (event instanceof RaceLogWindFixEvent) { RaceLogWindFixEvent windFixEvent = (RaceLogWindFixEvent) event; - windFixes.add(windFixEvent.getWindFix()); + windFixes.add(windFixEvent); } } return windFixes; } - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceLogWindFixDeclinationHelper.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceLogWindFixDeclinationHelper.java new file mode 100644 index 00000000000..5c1104994ab --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/RaceLogWindFixDeclinationHelper.java @@ -0,0 +1,43 @@ +package com.sap.sailing.domain.tracking; + +import java.io.IOException; +import java.text.ParseException; +import java.util.logging.Level; +import java.util.logging.Logger; + +import com.sap.sailing.declination.Declination; +import com.sap.sailing.declination.DeclinationService; +import com.sap.sailing.domain.abstractlog.race.RaceLogWindFixEvent; +import com.sap.sailing.domain.common.Wind; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.impl.WindImpl; +import com.sap.sse.common.Duration; + +public class RaceLogWindFixDeclinationHelper { + private static final Logger logger = Logger.getLogger(RaceLogWindFixDeclinationHelper.class.getName()); + + public Wind getOptionallyDeclinationCorrectedWind(RaceLogWindFixEvent event) { + Wind wind; + if (event.isMagnetic()) { + final Wind magneticNorthBasedWindFix = event.getWindFix(); + final DeclinationService declinationService = DeclinationService.INSTANCE; + Declination declination; + try { + declination = declinationService.getDeclination(magneticNorthBasedWindFix.getTimePoint(), + magneticNorthBasedWindFix.getPosition(), /* timeoutForOnlineFetchInMilliseconds */ + Duration.ONE_SECOND.times(5).asMillis()); + wind = new WindImpl(magneticNorthBasedWindFix.getPosition(), magneticNorthBasedWindFix.getTimePoint(), + new KnotSpeedWithBearingImpl(magneticNorthBasedWindFix.getKnots(), magneticNorthBasedWindFix + .getBearing().add( + declination.getBearingCorrectedTo(magneticNorthBasedWindFix.getTimePoint())))); + } catch (IOException | ParseException e) { + logger.log(Level.SEVERE, "Error trying to correct magnetic wind fix " + magneticNorthBasedWindFix + + " by declination. " + "Using uncorrected magnetic wind direction instead.", e); + wind = magneticNorthBasedWindFix; + } + } else { + wind = event.getWindFix(); + } + return wind; + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java index 879f70c4de1..6cc5d3df7e5 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java @@ -780,7 +780,7 @@ DynamicTrackedRace, GPSTrackListener { courseDesignChangedListener.courseDesignChanged(newCourseDesign); } } catch (IOException e) { - e.printStackTrace(); + logger.log(Level.INFO, "Exception trying to notify race course design change listeners about course design change", e); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java index 3cfdbd05f06..98677fd1b8f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java @@ -1,14 +1,9 @@ package com.sap.sailing.domain.tracking.impl; -import java.io.IOException; -import java.text.ParseException; import java.util.HashSet; import java.util.Set; -import java.util.logging.Level; import java.util.logging.Logger; -import com.sap.sailing.declination.Declination; -import com.sap.sailing.declination.DeclinationService; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogCourseDesignChangedEvent; import com.sap.sailing.domain.abstractlog.race.RaceLogDependentStartTimeEvent; @@ -34,13 +29,11 @@ import com.sap.sailing.domain.base.CourseBase; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; -import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; -import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.markpassingcalculation.MarkPassingUpdateListener; import com.sap.sailing.domain.tracking.DynamicTrackedRace; +import com.sap.sailing.domain.tracking.RaceLogWindFixDeclinationHelper; import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util.Pair; import com.sap.sse.common.Util.Triple; @@ -100,7 +93,8 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor { */ private void initializeWindTrack(RaceLog raceLog) { WindFixesFinder windFixesFinder = new WindFixesFinder(raceLog); - for (Wind wind : windFixesFinder.analyze()) { + for (RaceLogWindFixEvent raceLogWindFixEvent : windFixesFinder.analyze()) { + Wind wind = new RaceLogWindFixDeclinationHelper().getOptionallyDeclinationCorrectedWind(raceLogWindFixEvent); trackedRace.recordWind(wind, raceCommitteeWindSource); } } @@ -131,7 +125,8 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor { */ private void removeAllWindFixesFromWindTrack(RaceLog raceLog) { WindFixesFinder windFixesFinder = new WindFixesFinder(raceLog); - for (Wind wind : windFixesFinder.analyze()) { + for (RaceLogWindFixEvent raceLogWindFixEvent : windFixesFinder.analyze()) { + Wind wind = new RaceLogWindFixDeclinationHelper().getOptionallyDeclinationCorrectedWind(raceLogWindFixEvent); trackedRace.removeWind(wind, raceCommitteeWindSource); } } @@ -234,28 +229,8 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor { @Override public void visit(RaceLogWindFixEvent event) { + Wind wind = new RaceLogWindFixDeclinationHelper().getOptionallyDeclinationCorrectedWind(event); // add the wind fix to the race committee WindTrack - Wind wind; - if (event.isMagnetic()) { - final Wind magneticNorthBasedWindFix = event.getWindFix(); - final DeclinationService declinationService = DeclinationService.INSTANCE; - Declination declination; - try { - declination = declinationService.getDeclination(magneticNorthBasedWindFix.getTimePoint(), - magneticNorthBasedWindFix.getPosition(), /* timeoutForOnlineFetchInMilliseconds */ - Duration.ONE_SECOND.times(5).asMillis()); - wind = new WindImpl(magneticNorthBasedWindFix.getPosition(), magneticNorthBasedWindFix.getTimePoint(), - new KnotSpeedWithBearingImpl(magneticNorthBasedWindFix.getKnots(), magneticNorthBasedWindFix - .getBearing().add( - declination.getBearingCorrectedTo(magneticNorthBasedWindFix.getTimePoint())))); - } catch (IOException | ParseException e) { - logger.log(Level.SEVERE, "Error trying to correct magnetic wind fix "+magneticNorthBasedWindFix+" by declination. "+ - "Using uncorrected magnetic wind direction instead.", e); - wind = magneticNorthBasedWindFix; - } - } else { - wind = event.getWindFix(); - } trackedRace.recordWind(wind, raceCommitteeWindSource); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceContext.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceContext.java index 2a5a788a6d9..8029e830215 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceContext.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/RaceContext.java @@ -9,6 +9,7 @@ import java.util.logging.Logger; import com.google.gwt.core.shared.GwtIncompatible; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogFlagEvent; +import com.sap.sailing.domain.abstractlog.race.RaceLogWindFixEvent; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.AbortingFlagFinder; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver; import com.sap.sailing.domain.abstractlog.race.analyzing.impl.WindFixesFinder; @@ -34,7 +35,6 @@ import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster; import com.sap.sailing.domain.common.confidence.Weigher; import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl; -import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.media.MediaTrack; import com.sap.sailing.domain.common.racelog.FlagPole; import com.sap.sailing.domain.common.racelog.Flags; @@ -42,8 +42,8 @@ import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.RegattaLeaderboard; import com.sap.sailing.domain.leaderboard.ScoreCorrection; +import com.sap.sailing.domain.tracking.RaceLogWindFixDeclinationHelper; import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.gwt.server.HomeServiceUtil; import com.sap.sailing.gwt.ui.shared.race.FlagStateDTO; @@ -133,40 +133,42 @@ public class RaceContext { } private SimpleWindDTO getWindOrNull() { + final SimpleWindDTO result; if (trackedRace != null) { - TimePoint toTimePoint = trackedRace.getEndOfRace() == null ? MillisecondsTimePoint.now().minus( - trackedRace.getDelayToLiveInMillis()) : trackedRace.getEndOfRace(); - TimePoint newestEvent = trackedRace.getTimePointOfNewestEvent(); - if (newestEvent != null && newestEvent.before(toTimePoint)) { - toTimePoint = newestEvent; + TimePoint toTimePoint = getWindToTimePoint(); + WindWithConfidence> averagedWindWithConfidence = getWindFromTrackedRace(toTimePoint); + if (averagedWindWithConfidence != null) { + result = new SimpleWindDTO(averagedWindWithConfidence.getObject().getFrom().getDegrees(), averagedWindWithConfidence.getObject().getKnots()); + } else { + result = null; } - WindTrack windTrack = trackedRace.getOrCreateWindTrack(new WindSourceImpl(WindSourceType.COMBINED)); - if (windTrack != null) { - WindWithConfidence> averagedWindWithConfidence = windTrack - .getAveragedWindWithConfidence(trackedRace.getCenterOfCourse(toTimePoint), toTimePoint); - if (averagedWindWithConfidence != null) { - Wind wind = averagedWindWithConfidence.getObject(); - if (wind.getKnots() >= 0.05d) { - return new SimpleWindDTO(wind.getFrom().getDegrees(), wind.getKnots()); - } - } + } else { + Wind wind = checkForWindFixesFromRaceLog(); + if (wind != null) { + result = new SimpleWindDTO(wind.getFrom().getDegrees(), wind.getKnots()); + } else { + result = null; } } - Wind wind = checkForWindFixesFromRaceLog(); - if (wind != null) { - return new SimpleWindDTO(wind.getFrom().getDegrees(), wind.getKnots()); + return result; + } + + private WindWithConfidence> getWindFromTrackedRace(TimePoint timePoint) { + WindWithConfidence> averagedWindWithConfidence = trackedRace + .getWindWithConfidence(trackedRace.getCenterOfCourse(timePoint), timePoint); + final WindWithConfidence> result; + if (averagedWindWithConfidence != null && averagedWindWithConfidence.getObject().getKnots() >= 0.05d) { + result = averagedWindWithConfidence; + } else { + result = null; } - return null; + return result; } private WindStatisticsDTO getWindStatisticsOrNull() { + final WindStatisticsDTO result; if (trackedRace != null) { - TimePoint toTimePoint = trackedRace.getEndOfRace() == null ? MillisecondsTimePoint.now().minus( - trackedRace.getDelayToLiveInMillis()) : trackedRace.getEndOfRace(); - TimePoint newestEvent = trackedRace.getTimePointOfNewestEvent(); - if (newestEvent != null && newestEvent.before(toTimePoint)) { - toTimePoint = newestEvent; - } + TimePoint toTimePoint = getWindToTimePoint(); final BearingWithConfidenceCluster bwcc = new BearingWithConfidenceCluster( new Weigher() { private static final long serialVersionUID = -5779398785058438328L; @@ -175,48 +177,63 @@ public class RaceContext { return 1; } }); - WindTrack windTrack = trackedRace.getOrCreateWindTrack(new WindSourceImpl(WindSourceType.COMBINED)); - if (windTrack != null) { - TimePoint startTime = getStartTime(); - if (startTime == null) { - startTime = toTimePoint; - } - TimePoint middleOfRace = startTime.plus(startTime.until(toTimePoint).divide(2)); - List pointsToGetWind = Arrays.asList(startTime, middleOfRace, toTimePoint); - Double lowerBoundWindInKnots = null; - Double upperBoundWindInKnots = null; - for (TimePoint timePoint : pointsToGetWind) { - WindWithConfidence> averagedWindWithConfidence = windTrack - .getAveragedWindWithConfidence(trackedRace.getCenterOfCourse(timePoint), timePoint); - if (averagedWindWithConfidence != null) { - Wind wind = averagedWindWithConfidence.getObject(); - - bwcc.add(new BearingWithConfidenceImpl(wind.getBearing(), averagedWindWithConfidence - .getConfidence(), timePoint)); - double currentWindInKnots = wind.getKnots(); - if (currentWindInKnots >= 0.05d) { - if (lowerBoundWindInKnots == null) { - lowerBoundWindInKnots = currentWindInKnots; - upperBoundWindInKnots = currentWindInKnots; - } else { - lowerBoundWindInKnots = Math.min(lowerBoundWindInKnots, currentWindInKnots); - upperBoundWindInKnots = Math.max(upperBoundWindInKnots, currentWindInKnots); - } - } + TimePoint startTime = getStartTime(); + if (startTime == null) { + startTime = toTimePoint; + } + final TimePoint middleOfRace = startTime.plus(startTime.until(toTimePoint).divide(2)); + List pointsToGetWind = Arrays.asList(startTime, middleOfRace, toTimePoint); + Double lowerBoundWindInKnots = null; + Double upperBoundWindInKnots = null; + for (TimePoint timePoint : pointsToGetWind) { + WindWithConfidence> averagedWindWithConfidence = getWindFromTrackedRace(timePoint); + if (averagedWindWithConfidence != null) { + Wind wind = averagedWindWithConfidence.getObject(); + bwcc.add(new BearingWithConfidenceImpl(wind.getBearing(), averagedWindWithConfidence + .getConfidence(), timePoint)); + double currentWindInKnots = wind.getKnots(); + if (lowerBoundWindInKnots == null) { + lowerBoundWindInKnots = currentWindInKnots; + upperBoundWindInKnots = currentWindInKnots; + } else { + lowerBoundWindInKnots = Math.min(lowerBoundWindInKnots, currentWindInKnots); + upperBoundWindInKnots = Math.max(upperBoundWindInKnots, currentWindInKnots); } } - if (lowerBoundWindInKnots != null && upperBoundWindInKnots != null) { - BearingWithConfidence average = bwcc.getAverage(middleOfRace); - return new WindStatisticsDTO(average.getObject().reverse().getDegrees(), lowerBoundWindInKnots, - upperBoundWindInKnots); - } + } + if (lowerBoundWindInKnots != null && upperBoundWindInKnots != null) { + BearingWithConfidence average = bwcc.getAverage(middleOfRace); + result = new WindStatisticsDTO(average.getObject().reverse().getDegrees(), lowerBoundWindInKnots, + upperBoundWindInKnots); + } else { + result = null; + } + } else { + Wind wind = checkForWindFixesFromRaceLog(); + if (wind != null) { + result = new WindStatisticsDTO(wind.getFrom().getDegrees(), wind.getKnots(), wind.getKnots()); + } else { + result = null; } } - Wind wind = checkForWindFixesFromRaceLog(); - if (wind != null) { - return new WindStatisticsDTO(wind.getFrom().getDegrees(), wind.getKnots(), wind.getKnots()); + return result; + } + + /** + * Determines the end of the interval for which to compute wind data for the race. Usually, this will be the + * {@link TrackedRace#getEndOfRace() end of the race}; however, if this time point is not (yet) known, e.g., during + * a live race that is still ongoing, instead the current time point minus the live delay is chosen. If, however, + * the {@link TrackedRace#getTimePointOfNewestEvent() the race's newest event} is older than this time point, + * the time point of the race's newest event is used. + */ + private TimePoint getWindToTimePoint() { + TimePoint toTimePoint = trackedRace.getEndOfRace() == null ? MillisecondsTimePoint.now().minus( + trackedRace.getDelayToLiveInMillis()) : trackedRace.getEndOfRace(); + TimePoint newestEvent = trackedRace.getTimePointOfNewestEvent(); + if (newestEvent != null && newestEvent.before(toTimePoint)) { + toTimePoint = newestEvent; } - return null; + return toTimePoint; } private FlagStateDTO getFlagStateOrNull() { @@ -538,16 +555,21 @@ public class RaceContext { } private Wind checkForWindFixesFromRaceLog() { - if(raceLog == null) { - return null; + final Wind result; + if (raceLog == null) { + result = null; + } else { + WindFixesFinder windFixesFinder = new WindFixesFinder(raceLog); + List windList = windFixesFinder.analyze(); + if (!windList.isEmpty()) { + result = new RaceLogWindFixDeclinationHelper().getOptionallyDeclinationCorrectedWind(windList.get(windList.size() - 1)); + } else { + result = null; + } } - WindFixesFinder windFixesFinder = new WindFixesFinder(raceLog); - List windList = windFixesFinder.analyze(); - if (windList.size() > 0) { - return windList.get(windList.size() - 1); - } - return null; + return result; } + private RaceLogFlagEvent checkForAbortFlagEvent() { RaceLogFlagEvent result = null;