mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-16 09:11:02 +00:00
moved scalablevalue stuff and KMeansClusterer to SSE; added better maneuver-based wind estimation reporting features
This commit is contained in:
1 parent
14412c8290
commit
dd60863858
35 files changed
+183
-78
No files matched your search
@@ -7,6 +7,7 @@ Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.6
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Export-Package: com.sap.sailing.domain.common,
|
||||
com.sap.sailing.domain.common.abstractlog,
|
||||
com.sap.sailing.domain.common.confidence,
|
||||
com.sap.sailing.domain.common.confidence.impl,
|
||||
com.sap.sailing.domain.common.configuration,
|
||||
@@ -18,9 +19,7 @@ Export-Package: com.sap.sailing.domain.common,
|
||||
com.sap.sailing.domain.common.racelog,
|
||||
com.sap.sailing.domain.common.racelog.tracking,
|
||||
com.sap.sailing.domain.common.racelog.utils,
|
||||
com.sap.sailing.domain.common.scalablevalue,
|
||||
com.sap.sailing.domain.common.scalablevalue.impl,
|
||||
com.sap.sailing.domain.common.trackfiles,
|
||||
com.sap.sailing.domain.common.abstractlog
|
||||
com.sap.sailing.domain.common.trackfiles
|
||||
Require-Bundle: com.sap.sse.datamining.shared,
|
||||
com.sap.sse.common
|
||||
+1
-1
@@ -3,8 +3,8 @@ package com.sap.sailing.domain.common.confidence;
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
/**
|
||||
* Some values, particularly those obtained from real-world measurements, are not always accurate. Some values are
|
||||
|
||||
+1
-1
@@ -1,6 +1,6 @@
|
||||
package com.sap.sailing.domain.common.confidence;
|
||||
|
||||
import com.sap.sailing.domain.common.scalablevalue.IsScalable;
|
||||
import com.sap.sse.common.scalablevalue.IsScalable;
|
||||
|
||||
public interface HasConfidenceAndIsScalable<ValueType, BaseType, RelativeTo> extends IsScalable<ValueType, BaseType>,
|
||||
HasConfidence<ValueType, BaseType, RelativeTo> {
|
||||
|
||||
+2
-2
@@ -3,9 +3,9 @@ package com.sap.sailing.domain.common.confidence.impl;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.DoublePair;
|
||||
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
|
||||
import com.sap.sailing.domain.common.scalablevalue.IsScalable;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing;
|
||||
import com.sap.sse.common.scalablevalue.IsScalable;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
public class BearingWithConfidenceImpl<RelativeTo> extends HasConfidenceImpl<DoublePair, Bearing, RelativeTo>
|
||||
implements BearingWithConfidence<RelativeTo>, IsScalable<DoublePair, Bearing> {
|
||||
|
||||
+1
-1
@@ -4,8 +4,8 @@ import com.sap.sailing.domain.common.confidence.ConfidenceBasedAverager;
|
||||
import com.sap.sailing.domain.common.confidence.HasConfidence;
|
||||
import com.sap.sailing.domain.common.confidence.HasConfidenceAndIsScalable;
|
||||
import com.sap.sailing.domain.common.confidence.Weigher;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
|
||||
/**
|
||||
|
||||
+2
-2
@@ -1,7 +1,7 @@
|
||||
package com.sap.sailing.domain.common.confidence.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.scalablevalue.AbstractScalarValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sse.common.scalablevalue.AbstractScalarValue;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
public class ScalableDouble implements AbstractScalarValue<Double> {
|
||||
private final double value;
|
||||
|
||||
+2
-2
@@ -3,8 +3,8 @@ package com.sap.sailing.domain.common.scalablevalue.impl;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.DoublePair;
|
||||
import com.sap.sailing.domain.common.impl.RadianBearingImpl;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValueWithDistance;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValueWithDistance;
|
||||
|
||||
public class ScalableBearing implements ScalableValueWithDistance<DoublePair, Bearing> {
|
||||
private final double sin;
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ package com.sap.sailing.domain.common.scalablevalue.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.impl.RadianPosition;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValueWithDistance;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValueWithDistance;
|
||||
|
||||
public class ScalablePosition implements ScalableValueWithDistance<ScalablePosition, Position> {
|
||||
private final double x, y, z;
|
||||
|
||||
+2
-2
@@ -2,8 +2,8 @@ package com.sap.sailing.domain.common.scalablevalue.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValueWithDistance;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValueWithDistance;
|
||||
|
||||
public class ScalableSpeed implements ScalableValueWithDistance<Double, Speed> {
|
||||
private final double knots;
|
||||
|
||||
+1
-1
@@ -6,8 +6,8 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.RadianBearingImpl;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
/**
|
||||
* Separately scales speed and bearing. Instead of considering speed and bearing a single vector that can be scaled, the
|
||||
|
||||
+1
-1
@@ -18,7 +18,6 @@ import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableSpeed;
|
||||
@@ -44,6 +43,7 @@ import com.sap.sailing.domain.tracking.impl.DynamicTrackImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
/**
|
||||
* Receives Igtimi {@link Fix}es and tries to generate a {@link Wind} object from each {@link AWS} fix. For this to
|
||||
|
||||
+1
-1
@@ -30,7 +30,6 @@ import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
|
||||
import com.sap.sailing.domain.common.impl.RadianBearingImpl;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition;
|
||||
import com.sap.sailing.domain.confidence.ConfidenceBasedWindAverager;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
@@ -40,6 +39,7 @@ import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
public class ConfidenceTest {
|
||||
private static class ScalableBearing implements ScalableValue<ScalableBearing, Bearing> {
|
||||
|
||||
@@ -14,10 +14,10 @@ import org.junit.Test;
|
||||
|
||||
import com.sap.sailing.domain.base.ScalableInteger;
|
||||
import com.sap.sailing.domain.common.confidence.impl.ScalableDouble;
|
||||
import com.sap.sailing.util.kmeans.Cluster;
|
||||
import com.sap.sailing.util.kmeans.KMeansClusterer;
|
||||
import com.sap.sailing.util.kmeans.KMeansClustererWithEquidistantInitialization;
|
||||
import com.sap.sailing.util.kmeans.KMeansMappingClusterer;
|
||||
import com.sap.sse.util.kmeans.Cluster;
|
||||
import com.sap.sse.util.kmeans.KMeansClusterer;
|
||||
import com.sap.sse.util.kmeans.KMeansClustererWithEquidistantInitialization;
|
||||
import com.sap.sse.util.kmeans.KMeansMappingClusterer;
|
||||
|
||||
public class KMeansTest {
|
||||
@Test
|
||||
|
||||
@@ -27,8 +27,7 @@ Export-Package: com.sap.sailing.domain.base,
|
||||
com.sap.sailing.domain.tracking,
|
||||
com.sap.sailing.domain.tracking.impl,
|
||||
com.sap.sailing.util,
|
||||
com.sap.sailing.util.impl;x-friends:="com.sap.sailing.server.replication,com.sap.sailing.domain.test",
|
||||
com.sap.sailing.util.kmeans
|
||||
com.sap.sailing.util.impl;x-friends:="com.sap.sailing.server.replication,com.sap.sailing.domain.test"
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: com.googlecode.java-diff-utils;bundle-version="1.3.0",
|
||||
com.sap.sailing.domain.shared.android;bundle-version="1.0.0",
|
||||
|
||||
@@ -1,7 +1,7 @@
|
||||
package com.sap.sailing.domain.base;
|
||||
|
||||
import com.sap.sailing.domain.common.scalablevalue.AbstractScalarValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sse.common.scalablevalue.AbstractScalarValue;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
public class ScalableInteger implements AbstractScalarValue<Integer> {
|
||||
|
||||
|
||||
+1
-1
@@ -3,8 +3,8 @@ package com.sap.sailing.domain.base.impl;
|
||||
import com.sap.sailing.domain.base.PositionWithConfidence;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.confidence.impl.HasConfidenceImpl;
|
||||
import com.sap.sailing.domain.common.scalablevalue.IsScalable;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition;
|
||||
import com.sap.sse.common.scalablevalue.IsScalable;
|
||||
|
||||
public class PositionWithConfidenceImpl<RelativeTo> extends HasConfidenceImpl<ScalablePosition, Position, RelativeTo> implements
|
||||
PositionWithConfidence<RelativeTo>, IsScalable<ScalablePosition, Position> {
|
||||
|
||||
+2
-2
@@ -4,10 +4,10 @@ import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.SpeedWithBearing;
|
||||
import com.sap.sailing.domain.common.confidence.impl.HasConfidenceImpl;
|
||||
import com.sap.sailing.domain.common.scalablevalue.IsScalable;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableSpeedWithBearing;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.scalablevalue.IsScalable;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
public class SpeedWithBearingWithConfidenceImpl<RelativeTo> extends
|
||||
HasConfidenceImpl<Util.Triple<Speed, Double, Double>, SpeedWithBearing, RelativeTo> implements
|
||||
|
||||
+1
-1
@@ -3,8 +3,8 @@ package com.sap.sailing.domain.base.impl;
|
||||
import com.sap.sailing.domain.base.SpeedWithConfidence;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.confidence.impl.HasConfidenceImpl;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableSpeed;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
public class SpeedWithConfidenceImpl<RelativeTo> extends HasConfidenceImpl<Double, Speed, RelativeTo> implements SpeedWithConfidence<RelativeTo> {
|
||||
private static final long serialVersionUID = -3122580369653833748L;
|
||||
|
||||
+1
-1
@@ -2,11 +2,11 @@ package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.AbstractPosition;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValue;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableSpeedWithBearing;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValue;
|
||||
|
||||
/**
|
||||
* Wind values are scaled by separately scaling their speed and bearing, and separately scaling their time point, and
|
||||
|
||||
+3
-1
@@ -48,6 +48,7 @@ import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
|
||||
import com.sap.sailing.domain.base.SpeedWithConfidence;
|
||||
import com.sap.sailing.domain.base.Timed;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.base.impl.CourseImpl;
|
||||
import com.sap.sailing.domain.base.impl.DouglasPeucker;
|
||||
import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
@@ -101,12 +102,13 @@ import com.sap.sailing.domain.tracking.MarkPassing;
|
||||
import com.sap.sailing.domain.tracking.Track;
|
||||
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.TrackedRaceWithWindEssentials;
|
||||
import com.sap.sailing.domain.tracking.TrackedRegatta;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
|
||||
+1
-1
@@ -165,7 +165,7 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
||||
|
||||
/**
|
||||
* This method implements the functionality of the {@link #getAveragedWind(Position, TimePoint)} interface method.
|
||||
* It does so by collecting (smoothened, outliers removed)) wind fixes around the <code>at</code> time point up to
|
||||
* It does so by collecting (smoothened, outliers removed) wind fixes around the <code>at</code> time point up to
|
||||
* an interval length as specified by {@link #getMillisecondsOverWhichToAverageWind()}. At least one fix that is
|
||||
* closest to <code>at</code> will be picked up. If the track is empty, <code>null</code> is returned. Otherwise,
|
||||
* the wind fixes in the interval constructed are averaged using a {@link ConfidenceBasedWindAverager}.
|
||||
|
||||
@@ -19,7 +19,8 @@ Require-Bundle:
|
||||
com.sap.sailing.domain,
|
||||
com.sap.sse.datamining,
|
||||
com.sap.sse.datamining.shared;bundle-version="1.0.0",
|
||||
com.sap.sse.common
|
||||
com.sap.sse.common,
|
||||
com.sap.sse
|
||||
Import-Package:
|
||||
com.sap.sailing.util,
|
||||
com.sap.sse.common
|
||||
+83
-25
@@ -34,12 +34,13 @@ import com.sap.sailing.domain.tracking.impl.WindImpl;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl;
|
||||
import com.sap.sailing.polars.PolarDataService;
|
||||
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
|
||||
import com.sap.sailing.util.kmeans.Cluster;
|
||||
import com.sap.sailing.util.kmeans.KMeansMappingClusterer;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.common.Util.Pair;
|
||||
import com.sap.sse.common.Util.Triple;
|
||||
import com.sap.sse.common.impl.MillisecondsTimePoint;
|
||||
import com.sap.sse.util.kmeans.Cluster;
|
||||
import com.sap.sse.util.kmeans.KMeansMappingClusterer;
|
||||
|
||||
/**
|
||||
* Implements a wind estimation based on maneuver classifications.
|
||||
@@ -65,6 +66,12 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
||||
private final PolarDataService polarService;
|
||||
|
||||
private final TrackedRace trackedRace;
|
||||
|
||||
private final Set<Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>> clusters;
|
||||
|
||||
private final Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing> tackCluster;
|
||||
|
||||
private final Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing> jibeCluster;
|
||||
|
||||
public ManeuverBasedWindEstimationTrackImpl(PolarDataService polarService, TrackedRace trackedRace, long millisecondsOverWhichToAverage, boolean waitForLatest)
|
||||
throws NotEnoughDataHasBeenAddedException {
|
||||
@@ -72,7 +79,12 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
||||
/* nameForReadWriteLock */ ManeuverBasedWindEstimationTrackImpl.class.getName());
|
||||
this.polarService = polarService;
|
||||
this.trackedRace = trackedRace;
|
||||
analyzeRace(waitForLatest);
|
||||
Triple<Set<Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>>,
|
||||
Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>,
|
||||
Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>> clusterStructure = analyzeRace(waitForLatest);
|
||||
clusters = clusterStructure.getA();
|
||||
tackCluster = clusterStructure.getB();
|
||||
jibeCluster = clusterStructure.getC();
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -82,7 +94,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
||||
* @author Axel Uhl (D043530)
|
||||
*
|
||||
*/
|
||||
private class ManeuverClassification {
|
||||
public class ManeuverClassification {
|
||||
private final Competitor competitor;
|
||||
private final TimePoint timePoint;
|
||||
private final Position position;
|
||||
@@ -210,30 +222,38 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return toString(/* id */ null);
|
||||
}
|
||||
|
||||
public String toString(String id) {
|
||||
DateFormat df = new SimpleDateFormat("YYYY-MM-dd hh:mm:ss");
|
||||
final String prefix = getCompetitor().getName() + "\t" + df.format(getTimePoint().asDate()) + "\t" + getManeuverAngleDeg()
|
||||
+ "\t" + getSpeedAtManeuverStart().getKnots() + "\t"
|
||||
+ getSpeedAtManeuverStart().getBearing().getDegrees();
|
||||
final StringBuilder result = new StringBuilder();
|
||||
if (id != null) {
|
||||
result.append("ID");
|
||||
result.append(id);
|
||||
result.append('\t');
|
||||
}
|
||||
result.append(prefix);
|
||||
result.append("\t");
|
||||
result.append(getMiddleManeuverCourse().getDegrees());
|
||||
result.append("\t");
|
||||
result.append(getManeuverLoss() == null ? 0.0 : getManeuverLoss().getMeters());
|
||||
return result.toString();
|
||||
return format(this, /* id */ null);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public String getManeuverClassificationColumnHeaders() {
|
||||
return "datapoint\tcompetitor\ttimePoint\tangleDeg\tboatSpeedKn\tcogDeg\tmiddleManeuverCourse\tlossM";
|
||||
}
|
||||
|
||||
public String getManeuverClassificationColumnTypes() {
|
||||
return "infoitem\tstring\tdate\tfloat\tfloat\tfloat\tfloat\tfloat";
|
||||
}
|
||||
|
||||
public String format(ManeuverClassification mc, String id) {
|
||||
DateFormat df = new SimpleDateFormat("YYYY-MM-dd HH:mm:ss");
|
||||
final String prefix = mc.getCompetitor().getName() + "\t" + df.format(mc.getTimePoint().asDate()) + "\t" + mc.getManeuverAngleDeg()
|
||||
+ "\t" + mc.getSpeedAtManeuverStart().getKnots() + "\t"
|
||||
+ mc.getSpeedAtManeuverStart().getBearing().getDegrees();
|
||||
final StringBuilder result = new StringBuilder();
|
||||
if (id != null) {
|
||||
result.append("ID");
|
||||
result.append(id);
|
||||
result.append('\t');
|
||||
}
|
||||
result.append(prefix);
|
||||
result.append("\t");
|
||||
result.append(mc.getMiddleManeuverCourse().getDegrees());
|
||||
result.append("\t");
|
||||
result.append(mc.getManeuverLoss() == null ? 0.0 : mc.getManeuverLoss().getMeters());
|
||||
return result.toString();
|
||||
}
|
||||
|
||||
/**
|
||||
* Fetches all the race's maneuvers and tries to find the tacks and jibes. For each such maneuver identified, a wind
|
||||
* fix will be created based on the average COG into and out of the maneuver.
|
||||
@@ -289,7 +309,9 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
||||
*
|
||||
* TODO with enough candidates at hand, apply temporal and spatial segmentation to account for heterogeneous wind fields
|
||||
*/
|
||||
private void analyzeRace(boolean waitForLatest) throws NotEnoughDataHasBeenAddedException {
|
||||
private Triple<Set<Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>>,
|
||||
Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>,
|
||||
Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>> analyzeRace(boolean waitForLatest) throws NotEnoughDataHasBeenAddedException {
|
||||
final Map<Maneuver, Competitor> maneuvers = getAllManeuvers(waitForLatest);
|
||||
// cluster into eight clusters by middle COG first, then aggregate tack likelihoods for each, and jibe likelihoods for opposite cluster
|
||||
final int numberOfClusters = 8;
|
||||
@@ -300,6 +322,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
||||
// use an evenly distributed set of cluster seeds for clustering wind direction estimations
|
||||
IntStream.range(0, numberOfClusters).mapToObj((i)->new DegreeBearingImpl(((double) i)*360./(double) numberOfClusters)));
|
||||
final Set<Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>> clusters = clusterer.getClusters();
|
||||
assert maneuvers.size() == clusters.stream().map((c)->c.size()).reduce((s1, s2)->s1+s2).get();
|
||||
final Set<Pair<Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>, Double>> clustersAndLikelihoodOfBeingTackCluster =
|
||||
clusters.stream().map((c)->new Pair<>(c, getLikelihoodIsTackCluster(c, clusters))).collect(Collectors.toSet());
|
||||
// find most likely tack cluster, based on proximity of maneuver angles in cluster and opposite cluster as well as the speed ratio
|
||||
@@ -312,11 +335,13 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
||||
if (jibeCluster != null) {
|
||||
addWindFixes(jibeCluster, ManeuverType.JIBE);
|
||||
}
|
||||
return new Triple<>(clusters, dominantClusterAndLikelihoodOfBeingTackCluster.getA(), jibeCluster);
|
||||
}
|
||||
|
||||
private void addWindFixes(
|
||||
Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing> dominantClusterAndLikelihoodOfBeingTackCluster,
|
||||
final ManeuverType maneuverType) {
|
||||
// TODO see bug 1562 comment #8: use WindWithConfidenceImpl internally and implement WindTrack.getAveragedWindWithConfidence
|
||||
for (ManeuverClassification mc : dominantClusterAndLikelihoodOfBeingTackCluster) {
|
||||
final SpeedWithBearingWithConfidence<Void> estimatedWindSpeedAndBearing = mc.getEstimatedWindSpeedAndBearing(maneuverType);
|
||||
if (estimatedWindSpeedAndBearing != null) {
|
||||
@@ -440,4 +465,37 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl {
|
||||
}
|
||||
return Collections.unmodifiableMap(maneuvers);
|
||||
}
|
||||
|
||||
public Set<Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>> getClusters() {
|
||||
return clusters;
|
||||
}
|
||||
|
||||
public Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing> getTackCluster() {
|
||||
return tackCluster;
|
||||
}
|
||||
|
||||
public Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing> getJibeCluster() {
|
||||
return jibeCluster;
|
||||
}
|
||||
|
||||
public String getStringRepresentation(boolean waitForLatest) {
|
||||
return getStringRepresentation(getClusters().stream(), waitForLatest);
|
||||
}
|
||||
|
||||
public String getStringRepresentation(Stream<Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>> clusters, boolean waitForLatest) {
|
||||
StringBuilder stringRepresentation = new StringBuilder();
|
||||
stringRepresentation.delete(0, stringRepresentation.length());
|
||||
stringRepresentation.append(getManeuverClassificationColumnHeaders());
|
||||
stringRepresentation.append('\n');
|
||||
stringRepresentation.append(getManeuverClassificationColumnTypes());
|
||||
stringRepresentation.append('\n');
|
||||
stringRepresentation.append(getManeuverClassificationColumnHeaders());
|
||||
stringRepresentation.append('\n');
|
||||
final int[] id = new int[1];
|
||||
clusters.map((c)->c.stream()).reduce(Stream::concat).get().forEach((i)->{
|
||||
stringRepresentation.append(format(i, ""+id[0]++));
|
||||
stringRepresentation.append('\n');
|
||||
});
|
||||
return stringRepresentation.toString();
|
||||
}
|
||||
}
|
||||
+44
@@ -1,5 +1,8 @@
|
||||
package com.sap.sailing.server.gateway.jaxrs.api;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import javax.ws.rs.GET;
|
||||
import javax.ws.rs.Path;
|
||||
import javax.ws.rs.PathParam;
|
||||
@@ -17,19 +20,24 @@ import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
|
||||
import com.sap.sailing.domain.base.SpeedWithConfidence;
|
||||
import com.sap.sailing.domain.common.Bearing;
|
||||
import com.sap.sailing.domain.common.DoublePair;
|
||||
import com.sap.sailing.domain.common.LegType;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.polars.PolarDataService;
|
||||
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
|
||||
import com.sap.sailing.polars.windestimation.ManeuverBasedWindEstimationTrackImpl;
|
||||
import com.sap.sailing.polars.windestimation.ManeuverBasedWindEstimationTrackImpl.ManeuverClassification;
|
||||
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.PositionJsonSerializer;
|
||||
import com.sap.sailing.server.gateway.serialization.impl.WindJsonSerializer;
|
||||
import com.sap.sse.util.kmeans.Cluster;
|
||||
|
||||
/**
|
||||
* Right now this service is only used for quick debugging and testing of the polar api. It used plain text responses.
|
||||
@@ -126,4 +134,40 @@ public class PolarResource extends AbstractSailingServerResource {
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@GET
|
||||
@Produces("text/plain;charset=UTF-8")
|
||||
@Path("maneuvers/{regattaname}/races/{racename}")
|
||||
public Response getManeuvers(@PathParam("regattaname") String regattaName, @PathParam("racename") String raceName,
|
||||
@QueryParam("cluster") String cluster)
|
||||
throws NotEnoughDataHasBeenAddedException {
|
||||
Regatta regatta = findRegattaByName(regattaName);
|
||||
Response response;
|
||||
if (regatta == null) {
|
||||
response = getBadRegattaErrorResponse(regattaName);
|
||||
} else {
|
||||
RaceDefinition race = findRaceByName(regatta, raceName);
|
||||
if (race == null) {
|
||||
response = getBadRaceErrorResponse(regattaName, raceName);
|
||||
} else {
|
||||
TrackedRace trackedRace = findTrackedRace(regattaName, raceName);
|
||||
PolarDataService service = getService().getPolarDataService();
|
||||
ManeuverBasedWindEstimationTrackImpl maneuverBasedWindEstimationTrackImpl = new ManeuverBasedWindEstimationTrackImpl(
|
||||
service, trackedRace, /* millisecondsOverWhichToAverage */ 30000, /* waitForLatest */ false);
|
||||
final Stream<Cluster<ManeuverClassification, DoublePair, Bearing, ScalableBearing>> clusters;
|
||||
if ("tack".equals(cluster)) {
|
||||
clusters = Stream.of(maneuverBasedWindEstimationTrackImpl.getTackCluster());
|
||||
} else if ("jibe".equals(cluster)) {
|
||||
clusters = Stream.of(maneuverBasedWindEstimationTrackImpl.getTackCluster());
|
||||
} else if (cluster != null && cluster.matches("[0-9][0-9]*")) {
|
||||
clusters = Stream.of(new ArrayList<>(maneuverBasedWindEstimationTrackImpl.getClusters()).get(Integer.valueOf(cluster)));
|
||||
} else {
|
||||
clusters = maneuverBasedWindEstimationTrackImpl.getClusters().stream();
|
||||
}
|
||||
response = Response.ok(maneuverBasedWindEstimationTrackImpl.getStringRepresentation(clusters, /* waitForLatest */ true),
|
||||
MediaType.TEXT_PLAIN).build();
|
||||
}
|
||||
}
|
||||
return response;
|
||||
}
|
||||
}
|
||||
@@ -8,6 +8,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.6
|
||||
Export-Package: com.sap.sse.common,
|
||||
com.sap.sse.common.filter,
|
||||
com.sap.sse.common.impl,
|
||||
com.sap.sse.common.scalablevalue,
|
||||
com.sap.sse.common.search
|
||||
Require-Bundle: com.sap.sse.datamining.shared;bundle-version="1.0.0"
|
||||
Bundle-ActivationPolicy: lazy
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.domain.common.scalablevalue;
|
||||
package com.sap.sse.common.scalablevalue;
|
||||
|
||||
|
||||
/**
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.domain.common.scalablevalue;
|
||||
package com.sap.sse.common.scalablevalue;
|
||||
|
||||
public interface ComparableScalableValueWithDistance<ValueType, AveragesTo extends Comparable<AveragesTo>>
|
||||
extends ScalableValueWithDistance<ValueType, AveragesTo>, Comparable<AveragesTo> {
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.domain.common.scalablevalue;
|
||||
package com.sap.sse.common.scalablevalue;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.domain.common.scalablevalue;
|
||||
package com.sap.sse.common.scalablevalue;
|
||||
|
||||
public interface ScalableValue<ValueType, AveragesTo> {
|
||||
ScalableValue<ValueType, AveragesTo> multiply(double factor);
|
||||
+1
-1
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.domain.common.scalablevalue;
|
||||
package com.sap.sse.common.scalablevalue;
|
||||
|
||||
/**
|
||||
* In addition to being a {@link ScalableValue}, specifies the {@link #getDistance} method which determines
|
||||
@@ -7,6 +7,7 @@ Bundle-Vendor: SAP
|
||||
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
|
||||
Export-Package: com.sap.sse,
|
||||
com.sap.sse.util,
|
||||
com.sap.sse.util.impl
|
||||
com.sap.sse.util.impl,
|
||||
com.sap.sse.util.kmeans
|
||||
Bundle-ActivationPolicy: lazy
|
||||
Require-Bundle: com.sap.sse.common
|
||||
+3
-3
@@ -1,4 +1,4 @@
|
||||
package com.sap.sailing.util.kmeans;
|
||||
package com.sap.sse.util.kmeans;
|
||||
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
@@ -8,7 +8,7 @@ import java.util.function.Consumer;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValueWithDistance;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValueWithDistance;
|
||||
|
||||
/**
|
||||
* A k-means cluster that can determine its centroid (arithmetic mean of all its values) and for each element the
|
||||
@@ -75,7 +75,7 @@ public class Cluster<E, ValueType, AveragesTo, T extends ScalableValueWithDistan
|
||||
|
||||
/**
|
||||
* @return <code>null</code> for an empty cluster; otherwise the average obtained by dividing the element
|
||||
* {@link ScalableValueWithDistance#add(com.sap.sailing.domain.common.scalablevalue.ScalableValue) sum} by
|
||||
* {@link ScalableValueWithDistance#add(com.sap.sse.common.scalablevalue.ScalableValue) sum} by
|
||||
* the number of elements that have been added to this cluster so far
|
||||
*/
|
||||
public AveragesTo getCentroid() {
|
||||
+2
-2
@@ -1,8 +1,8 @@
|
||||
package com.sap.sailing.util.kmeans;
|
||||
package com.sap.sse.util.kmeans;
|
||||
|
||||
import java.util.Iterator;
|
||||
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValueWithDistance;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValueWithDistance;
|
||||
|
||||
/**
|
||||
* Clusters elements of type <code>T</code> into a pre-defined number of clusters such that after clustering the sum of
|
||||
+3
-3
@@ -1,11 +1,11 @@
|
||||
package com.sap.sailing.util.kmeans;
|
||||
package com.sap.sse.util.kmeans;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.Iterator;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.domain.common.scalablevalue.ComparableScalableValueWithDistance;
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValueWithDistance;
|
||||
import com.sap.sse.common.scalablevalue.ComparableScalableValueWithDistance;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValueWithDistance;
|
||||
|
||||
/**
|
||||
* Requires {@link ComparableScalableValueWithDistance comparable scalable values} as elements for which then
|
||||
+4
-4
@@ -1,15 +1,15 @@
|
||||
package com.sap.sailing.util.kmeans;
|
||||
package com.sap.sse.util.kmeans;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashSet;
|
||||
import java.util.Iterator;
|
||||
import java.util.LinkedHashSet;
|
||||
import java.util.List;
|
||||
import java.util.Random;
|
||||
import java.util.Set;
|
||||
import java.util.function.Function;
|
||||
import java.util.stream.Stream;
|
||||
|
||||
import com.sap.sailing.domain.common.scalablevalue.ScalableValueWithDistance;
|
||||
import com.sap.sse.common.scalablevalue.ScalableValueWithDistance;
|
||||
|
||||
/**
|
||||
* Clusters elements of type <code>E</code> into a pre-defined number of clusters such that after clustering the sum of
|
||||
@@ -170,7 +170,7 @@ public class KMeansMappingClusterer<E, ValueType, AveragesTo, T extends Scalable
|
||||
}
|
||||
|
||||
public Set<Cluster<E, ValueType, AveragesTo, T>> getClusters() {
|
||||
Set<Cluster<E, ValueType, AveragesTo, T>> result = new HashSet<>();
|
||||
Set<Cluster<E, ValueType, AveragesTo, T>> result = new LinkedHashSet<>();
|
||||
for (Cluster<E, ValueType, AveragesTo, T> i : clusters) {
|
||||
if (!i.isEmpty()) {
|
||||
result.add(i);
|
||||
Reference in new issue
Block a user