diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java index 59f3d3f2d6d..0562aa8fd63 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java @@ -23,7 +23,7 @@ public class ConfidenceBasedAveragerFactoryImpl implements ConfidenceFactory { @Override public Weigher createLinearTimeDifferenceWeigher(long halfConfidenceAfterMilliseconds) { - return new LinearTimeDifferenceWeigher(halfConfidenceAfterMilliseconds); + return new HyperbolicTimeDifferenceWeigher(halfConfidenceAfterMilliseconds); } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/LinearTimeDifferenceWeigher.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/HyperbolicTimeDifferenceWeigher.java similarity index 87% rename from java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/LinearTimeDifferenceWeigher.java rename to java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/HyperbolicTimeDifferenceWeigher.java index 26d8c445896..68317dd8058 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/LinearTimeDifferenceWeigher.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/HyperbolicTimeDifferenceWeigher.java @@ -3,10 +3,10 @@ package com.sap.sailing.domain.confidence.impl; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.confidence.Weigher; -public class LinearTimeDifferenceWeigher implements Weigher { +public class HyperbolicTimeDifferenceWeigher implements Weigher { private final long halfConfidenceAfterMilliseconds; - public LinearTimeDifferenceWeigher(long halfConfidenceAfterMilliseconds) { + public HyperbolicTimeDifferenceWeigher(long halfConfidenceAfterMilliseconds) { this.halfConfidenceAfterMilliseconds = halfConfidenceAfterMilliseconds; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java index 41ae6314eaa..a32e3efed0e 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java @@ -92,15 +92,22 @@ public class BearingWithConfidenceCluster { return (BearingWithConfidenceCluster[]) new BearingWithConfidenceCluster[2]; } + /** + * To avoid that a bearing with low confidence decides about the clustering, the difference between two bearings + * is scaled by their confidences. This scaled distance is then maximized for those bearings at least + * minimumDegreeDifferenceBetweenTacks degrees apart. + */ private Pair, BearingWithConfidence> getExtremeBearings(double minimumDegreeDifferenceBetweenTacks) { assert bearings.size() >= 2; - double maxAbsDegDiff = minimumDegreeDifferenceBetweenTacks; + double maxAbsDegDiff = 0; Pair, BearingWithConfidence> result = null; for (int i=0; i= maxAbsDegDiff) { + final double confidenceScaledDifference = getConfidenceScaledDifference(bearings.get(i), bearings.get(j)); + if (Math.abs(bearings.get(i).getObject().getDifferenceTo(bearings.get(j).getObject()).getDegrees()) >= minimumDegreeDifferenceBetweenTacks + && confidenceScaledDifference > maxAbsDegDiff) { result = new Pair, BearingWithConfidence>(bearings.get(i), bearings.get(j)); - maxAbsDegDiff = Math.abs(bearings.get(i).getObject().getDifferenceTo(bearings.get(j).getObject()).getDegrees()); + maxAbsDegDiff = confidenceScaledDifference; assert Math.abs(bearings.get(i).getObject().getDifferenceTo(bearings.get(j).getObject()).getDegrees()) <= 180.; } } @@ -108,6 +115,12 @@ public class BearingWithConfidenceCluster { return result; } + private double getConfidenceScaledDifference(BearingWithConfidence bearingWithConfidence1, + BearingWithConfidence bearingWithConfidence2) { + return bearingWithConfidence1.getObject().getDifferenceTo(bearingWithConfidence2.getObject()).getDegrees() * + bearingWithConfidence1.getConfidence() * bearingWithConfidence2.getConfidence(); + } + public boolean isEmpty() { return bearings.isEmpty(); } 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 ca77acda94a..7310123e9fa 100755 --- 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 @@ -57,7 +57,7 @@ import com.sap.sailing.domain.confidence.ConfidenceBasedAverager; import com.sap.sailing.domain.confidence.ConfidenceFactory; import com.sap.sailing.domain.confidence.HasConfidence; import com.sap.sailing.domain.confidence.Weigher; -import com.sap.sailing.domain.confidence.impl.LinearTimeDifferenceWeigher; +import com.sap.sailing.domain.confidence.impl.HyperbolicTimeDifferenceWeigher; import com.sap.sailing.domain.tracking.GPSFix; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.GPSFixTrack; @@ -191,6 +191,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { windTracks.put(trackBasedWindSource, windStore.getWindTrack(trackedEvent, this, trackBasedWindSource, millisecondsOverWhichToAverageWind)); WindSource webWindSource = new WindSourceImpl(WindSourceType.WEB); windTracks.put(webWindSource, windStore.getWindTrack(trackedEvent, this, webWindSource, millisecondsOverWhichToAverageWind)); + // FIXME need to do something to load expedition wind that was stored in DB because no recordWind will be called this.trackedEvent = trackedEvent; competitorRankings = new HashMap>(); } @@ -792,7 +793,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { // TODO confidences need to be computed not only based on timePoint but also on position: boats far away don't contribute as confidently as boats close by private Map> clusterBearingsForWindEstimation(TimePoint timePoint, Position position, DummyMarkPassingWithTimePointOnly dummyMarkPassingForNow, Weigher weigher) { - Weigher weigherForMarkPassingProximity = new LinearTimeDifferenceWeigher(getMillisecondsOverWhichToAverageSpeed()*5); + Weigher weigherForMarkPassingProximity = new HyperbolicTimeDifferenceWeigher(getMillisecondsOverWhichToAverageSpeed()*5); Map> bearings = new HashMap>(); for (LegType legType : LegType.values()) { bearings.put(legType, new BearingWithConfidenceCluster(weigher)); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindChart.java index 4bbbf557250..d6f0973a6e0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindChart.java @@ -47,7 +47,7 @@ import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent; public class WindChart implements Component, RaceSelectionChangeListener, TimeListener { private static final int LINE_WIDTH = 1; private final StringMessages stringMessages; - private final Set windSourcesToDisplay; + private final Set windSourceTypesToDisplay; /** * Holds one series for each wind source for which data has been received. @@ -84,7 +84,7 @@ public class WindChart implements Component, RaceSelectionCha this.windSourceDirectionSeries = new HashMap(); this.windSourceSpeedSeries = new HashMap(); this.colorMap = new ColorMap(); - this.windSourcesToDisplay = new HashSet(); + this.windSourceTypesToDisplay = new HashSet(); this.timer = timer; chart = new Chart() .setZoomType(Chart.ZoomType.X) @@ -143,7 +143,7 @@ public class WindChart implements Component, RaceSelectionCha visible.add(series); } for (Map.Entry e : windSourceDirectionSeries.entrySet()) { - if (windSourcesToDisplay.contains(e.getKey().getType())) { + if (windSourceTypesToDisplay.contains(e.getKey().getType())) { if (!visible.contains(e.getValue())) { chart.addSeries(e.getValue()); } else { @@ -152,7 +152,7 @@ public class WindChart implements Component, RaceSelectionCha } } for (Map.Entry e : windSourceSpeedSeries.entrySet()) { - if (windSourcesToDisplay.contains(e.getKey().getType())) { + if (windSourceTypesToDisplay.contains(e.getKey().getType())) { if (!visible.contains(e.getValue())) { chart.addSeries(e.getValue()); } else { @@ -165,15 +165,26 @@ public class WindChart implements Component, RaceSelectionCha } } + /** + * Creates the series for the windSource specified. If the series is created and needs to be visible + * based on the {@link #windSourceTypesToDisplay}, it is added to the chart. + */ private Series getOrCreateSpeedSeries(WindSource windSource) { Series result = windSourceSpeedSeries.get(windSource); if (result == null) { result = createSpeedSeries(windSource); windSourceSpeedSeries.put(windSource, result); + if (windSourceTypesToDisplay.contains(windSource.getType())) { + chart.addSeries(result); + } } return result; } + /** + * Only creates the series but doesn't add it to the chart. See also {@link #getOrCreateDirectionSeries(WindSource)} and + * {@link #showVisibleSeries()} + */ private Series createDirectionSeries(WindSource windSource) { Series newSeries = chart .createSeries() @@ -184,6 +195,10 @@ public class WindChart implements Component, RaceSelectionCha return newSeries; } + /** + * Only creates the series but doesn't add it to the chart. See also {@link #getOrCreateSpeedSeries(WindSource)} and + * {@link #showVisibleSeries()} + */ private Series createSpeedSeries(WindSource windSource) { Series newSeries = chart .createSeries() @@ -206,6 +221,7 @@ public class WindChart implements Component, RaceSelectionCha WindSource windSource = e.getKey(); Series directionSeries = getOrCreateDirectionSeries(windSource); Series speedSeries = getOrCreateSpeedSeries(windSource); + // FIXME probably need to add the series to the chart... WindTrackInfoDTO windTrackInfo = e.getValue(); Point[] directionPoints = new Point[windTrackInfo.windFixes.size()]; Point[] speedPoints = new Point[windTrackInfo.windFixes.size()]; @@ -252,7 +268,7 @@ public class WindChart implements Component, RaceSelectionCha @Override public SettingsDialogComponent getSettingsDialogComponent() { - return new WindChartSettingsDialogComponent(new WindChartSettings(windSourcesToDisplay)); + return new WindChartSettingsDialogComponent(new WindChartSettings(windSourceTypesToDisplay)); } /** @@ -261,26 +277,33 @@ public class WindChart implements Component, RaceSelectionCha */ @Override public void updateSettings(WindChartSettings newSettings) { - windSourcesToDisplay.clear(); - windSourcesToDisplay.addAll(newSettings.getWindSourceTypesToDisplay()); + windSourceTypesToDisplay.clear(); + windSourceTypesToDisplay.addAll(newSettings.getWindSourceTypesToDisplay()); chart.removeAllSeries(/* redraw */ false); for (Map.Entry e : windSourceDirectionSeries.entrySet()) { - if (windSourcesToDisplay.contains(e.getKey().getType())) { + if (windSourceTypesToDisplay.contains(e.getKey().getType())) { chart.addSeries(e.getValue()); } } for (Map.Entry e : windSourceDirectionSeries.entrySet()) { - if (windSourcesToDisplay.contains(e.getKey().getType())) { + if (windSourceTypesToDisplay.contains(e.getKey().getType())) { chart.addSeries(e.getValue()); } } } + /** + * Creates the series for the windSource specified. If the series is created and needs to be visible + * based on the {@link #windSourceTypesToDisplay}, it is added to the chart. + */ private Series getOrCreateDirectionSeries(WindSource windSource) { Series result = windSourceDirectionSeries.get(windSource); if (result == null) { result = createDirectionSeries(windSource); windSourceDirectionSeries.put(windSource, result); + if (windSourceTypesToDisplay.contains(windSource.getType())) { + chart.addSeries(result); + } } return result; }