diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/scalablevalue/impl/ScalablePosition.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/scalablevalue/impl/ScalablePosition.java index 2d0ed6be94c..6115de11082 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/scalablevalue/impl/ScalablePosition.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/scalablevalue/impl/ScalablePosition.java @@ -14,7 +14,7 @@ public class ScalablePosition implements ScalableValueWithDistance maneuvers = windTrack.getAllManeuvers(/* waitForLatest */ true); + final int numberOfClusters = 16; + KMeansMappingClusterer, Pair, ScalableBearingAndScalableDouble> clusterer = + new KMeansMappingClusterer<>(numberOfClusters, + windTrack.getManeuverClassifications(maneuvers), + (mc)->new ScalableBearingAndScalableDouble(mc.getMiddleManeuverCourse(), mc.getManeuverAngleDeg()), // maps maneuver classification to cluster metric + // use an evenly distributed set of cluster seeds for clustering wind direction estimations + Stream.concat(IntStream.range(0, numberOfClusters/2).mapToObj((i)-> + new Pair<>(new DegreeBearingImpl(((double) i)*360./(double) numberOfClusters/2), 45.)), + IntStream.range(0, numberOfClusters/2).mapToObj((i)-> + new Pair<>(new DegreeBearingImpl(((double) i)*360./(double) numberOfClusters/2), -45.)))); + final Set, Pair, ScalableBearingAndScalableDouble>> clusters = clusterer.getClusters(); + assertEquals(8, clusters.size()); + } private Wind getManeuverBasedAverageWind() throws NotEnoughDataHasBeenAddedException { ManeuverBasedWindEstimationTrackImpl windTrack = new ManeuverBasedWindEstimationTrackImpl(new PolarDataServiceImpl(Executors.newFixedThreadPool(4)), diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java index 0e7c5a2830b..ad0c3eb2529 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java @@ -454,12 +454,15 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { /** * For each maneuver from maneuvers, a {@link ManeuverClassification object} is created. */ - private Stream getManeuverClassifications(final Map maneuvers) { + Stream getManeuverClassifications(final Map maneuvers) { return maneuvers.entrySet().stream().map((maneuverAndCompetitor)-> new ManeuverClassification(maneuverAndCompetitor.getValue(), maneuverAndCompetitor.getKey())); } - private Map getAllManeuvers(boolean waitForLatest) { + /** + * Package scope to let test fragment access it + */ + Map getAllManeuvers(boolean waitForLatest) { Map maneuvers = new HashMap<>(); final Waypoint firstWaypoint = trackedRace.getRace().getCourse().getFirstWaypoint(); final Waypoint lastWaypoint = trackedRace.getRace().getCourse().getLastWaypoint(); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ScalableBearingAndScalableDouble.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ScalableBearingAndScalableDouble.java new file mode 100755 index 00000000000..13b26e4dc2b --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ScalableBearingAndScalableDouble.java @@ -0,0 +1,61 @@ +package com.sap.sailing.polars.windestimation; + +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.confidence.impl.ScalableDouble; +import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing; +import com.sap.sse.common.Util.Pair; +import com.sap.sse.common.scalablevalue.ScalableValue; +import com.sap.sse.common.scalablevalue.ScalableValueWithDistance; + +/** + * Two {@link Bearings} combined in one scalable object, like a vector of two bearings. {@link #add(ScalableValue) + * Addition} works component wise, and so does {@link #multiply(double) multiplication} and {@link #divide(double) + * division}. The {@link #getDistance(Pair) distance} is calculated as the Euklidian two-dimensional distance between + * the two bearing pairs, with "degrees" as conceptual unit. For example, another object of this type that is 2 and 3 degrees + * apart from this object will have a distance of {@link Math#sqrt(double) Math.sqrt(13)}. + * + * @author Axel Uhl (D043530) + * + */ +public class ScalableBearingAndScalableDouble implements + ScalableValueWithDistance, Pair> { + private final ScalableBearing b1; + private final ScalableDouble b2; + + public ScalableBearingAndScalableDouble(Bearing b1, double b2) { + this(new ScalableBearing(b1), new ScalableDouble(b2)); + } + + private ScalableBearingAndScalableDouble(ScalableBearing b1, ScalableDouble b2) { + super(); + this.b1 = b1; + this.b2 = b2; + } + + @Override + public Pair divide(double divisor) { + return new Pair<>(b1.divide(divisor), b2.divide(divisor)); + } + + @Override + public Pair getValue() { + return new Pair<>(b1, b2); + } + + @Override + public double getDistance(Pair other) { + final double b1DiffDeg = b1.divide(1).getDifferenceTo(other.getA()).getDegrees(); + final double b2DiffDeg = Math.abs(b2.divide(1)-other.getB()); + return Math.sqrt(b1DiffDeg*b1DiffDeg + b2DiffDeg*b2DiffDeg); + } + + @Override + public ScalableBearingAndScalableDouble add(ScalableValue, Pair> t) { + return new ScalableBearingAndScalableDouble(b1.add(t.getValue().getA()), b2.add(t.getValue().getB())); + } + + @Override + public ScalableBearingAndScalableDouble multiply(double factor) { + return new ScalableBearingAndScalableDouble(b1.multiply(factor), b2.multiply(factor)); + } +} diff --git a/java/com.sap.sse/src/com/sap/sse/util/kmeans/Cluster.java b/java/com.sap.sse/src/com/sap/sse/util/kmeans/Cluster.java index a2d280f62c8..b383b62be8d 100755 --- a/java/com.sap.sse/src/com/sap/sse/util/kmeans/Cluster.java +++ b/java/com.sap.sse/src/com/sap/sse/util/kmeans/Cluster.java @@ -115,7 +115,11 @@ public class ClustergetDistanceFromMean(e)*getDistanceFromMean(e)).average().getAsDouble(); } public Stream stream() {