use confidences when splitting a bearing cluster

This commit is contained in:
Axel Uhl committed 2012-03-06 14:48:39 +01:00
1 parent cc55bdd422
commit 354013be1a
5 files changed
+54 -17

No files matched your search

@@ -23,7 +23,7 @@ public class ConfidenceBasedAveragerFactoryImpl implements ConfidenceFactory {
@Override
public Weigher<TimePoint> createLinearTimeDifferenceWeigher(long halfConfidenceAfterMilliseconds) {
return new LinearTimeDifferenceWeigher(halfConfidenceAfterMilliseconds);
return new HyperbolicTimeDifferenceWeigher(halfConfidenceAfterMilliseconds);
}
@Override
@@ -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<TimePoint> {
public class HyperbolicTimeDifferenceWeigher implements Weigher<TimePoint> {
private final long halfConfidenceAfterMilliseconds;
public LinearTimeDifferenceWeigher(long halfConfidenceAfterMilliseconds) {
public HyperbolicTimeDifferenceWeigher(long halfConfidenceAfterMilliseconds) {
this.halfConfidenceAfterMilliseconds = halfConfidenceAfterMilliseconds;
}
@@ -92,15 +92,22 @@ public class BearingWithConfidenceCluster<RelativeTo> {
return (BearingWithConfidenceCluster<RelativeTo>[]) 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
* <code>minimumDegreeDifferenceBetweenTacks</code> degrees apart.
*/
private Pair<BearingWithConfidence<RelativeTo>, BearingWithConfidence<RelativeTo>> getExtremeBearings(double minimumDegreeDifferenceBetweenTacks) {
assert bearings.size() >= 2;
double maxAbsDegDiff = minimumDegreeDifferenceBetweenTacks;
double maxAbsDegDiff = 0;
Pair<BearingWithConfidence<RelativeTo>, BearingWithConfidence<RelativeTo>> result = null;
for (int i=0; i<bearings.size(); i++) {
for (int j=i+1; j<bearings.size(); j++) {
if (Math.abs(bearings.get(i).getObject().getDifferenceTo(bearings.get(j).getObject()).getDegrees()) >= 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<RelativeTo>, BearingWithConfidence<RelativeTo>>(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<RelativeTo> {
return result;
}
private double getConfidenceScaledDifference(BearingWithConfidence<RelativeTo> bearingWithConfidence1,
BearingWithConfidence<RelativeTo> bearingWithConfidence2) {
return bearingWithConfidence1.getObject().getDifferenceTo(bearingWithConfidence2.getObject()).getDegrees() *
bearingWithConfidence1.getConfidence() * bearingWithConfidence2.getConfidence();
}
public boolean isEmpty() {
return bearings.isEmpty();
}
@@ -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<TimePoint, List<Competitor>>();
}
@@ -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<LegType, BearingWithConfidenceCluster<TimePoint>> clusterBearingsForWindEstimation(TimePoint timePoint,
Position position, DummyMarkPassingWithTimePointOnly dummyMarkPassingForNow, Weigher<TimePoint> weigher) {
Weigher<TimePoint> weigherForMarkPassingProximity = new LinearTimeDifferenceWeigher(getMillisecondsOverWhichToAverageSpeed()*5);
Weigher<TimePoint> weigherForMarkPassingProximity = new HyperbolicTimeDifferenceWeigher(getMillisecondsOverWhichToAverageSpeed()*5);
Map<LegType, BearingWithConfidenceCluster<TimePoint>> bearings = new HashMap<LegType, BearingWithConfidenceCluster<TimePoint>>();
for (LegType legType : LegType.values()) {
bearings.put(legType, new BearingWithConfidenceCluster<TimePoint>(weigher));
@@ -47,7 +47,7 @@ import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent;
public class WindChart implements Component<WindChartSettings>, RaceSelectionChangeListener, TimeListener {
private static final int LINE_WIDTH = 1;
private final StringMessages stringMessages;
private final Set<WindSourceType> windSourcesToDisplay;
private final Set<WindSourceType> windSourceTypesToDisplay;
/**
* Holds one series for each wind source for which data has been received.
@@ -84,7 +84,7 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
this.windSourceDirectionSeries = new HashMap<WindSource, Series>();
this.windSourceSpeedSeries = new HashMap<WindSource, Series>();
this.colorMap = new ColorMap<WindSource>();
this.windSourcesToDisplay = new HashSet<WindSourceType>();
this.windSourceTypesToDisplay = new HashSet<WindSourceType>();
this.timer = timer;
chart = new Chart()
.setZoomType(Chart.ZoomType.X)
@@ -143,7 +143,7 @@ public class WindChart implements Component<WindChartSettings>, RaceSelectionCha
visible.add(series);
}
for (Map.Entry<WindSource, Series> 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<WindChartSettings>, RaceSelectionCha
}
}
for (Map.Entry<WindSource, Series> 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<WindChartSettings>, RaceSelectionCha
}
}
/**
* Creates the series for the <code>windSource</code> 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<WindChartSettings>, 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<WindChartSettings>, 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<WindChartSettings>, RaceSelectionCha
@Override
public SettingsDialogComponent<WindChartSettings> getSettingsDialogComponent() {
return new WindChartSettingsDialogComponent(new WindChartSettings(windSourcesToDisplay));
return new WindChartSettingsDialogComponent(new WindChartSettings(windSourceTypesToDisplay));
}
/**
@@ -261,26 +277,33 @@ public class WindChart implements Component<WindChartSettings>, 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<WindSource, Series> e : windSourceDirectionSeries.entrySet()) {
if (windSourcesToDisplay.contains(e.getKey().getType())) {
if (windSourceTypesToDisplay.contains(e.getKey().getType())) {
chart.addSeries(e.getValue());
}
}
for (Map.Entry<WindSource, Series> e : windSourceDirectionSeries.entrySet()) {
if (windSourcesToDisplay.contains(e.getKey().getType())) {
if (windSourceTypesToDisplay.contains(e.getKey().getType())) {
chart.addSeries(e.getValue());
}
}
}
/**
* Creates the series for the <code>windSource</code> 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;
}