From 3534be67369bf79ea2bcc4b03b2e5b1a66a8a8de Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 4 Dec 2013 15:18:25 +0100 Subject: [PATCH 01/72] Added polars bundle and polar data interface --- java/com.sap.sailing.polars/.classpath | 7 ++ java/com.sap.sailing.polars/.project | 28 +++++++ .../META-INF/MANIFEST.MF | 14 ++++ java/com.sap.sailing.polars/build.properties | 4 + java/com.sap.sailing.polars/pom.xml | 12 +++ .../sap/sailing/polars/PolarDataService.java | 76 +++++++++++++++++++ .../polars/mocked/MockedPolarDataService.java | 47 ++++++++++++ java/pom.xml | 1 + 8 files changed, 189 insertions(+) create mode 100644 java/com.sap.sailing.polars/.classpath create mode 100644 java/com.sap.sailing.polars/.project create mode 100644 java/com.sap.sailing.polars/META-INF/MANIFEST.MF create mode 100644 java/com.sap.sailing.polars/build.properties create mode 100644 java/com.sap.sailing.polars/pom.xml create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mocked/MockedPolarDataService.java diff --git a/java/com.sap.sailing.polars/.classpath b/java/com.sap.sailing.polars/.classpath new file mode 100644 index 00000000000..751c8f2e504 --- /dev/null +++ b/java/com.sap.sailing.polars/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java/com.sap.sailing.polars/.project b/java/com.sap.sailing.polars/.project new file mode 100644 index 00000000000..b2e142dedfa --- /dev/null +++ b/java/com.sap.sailing.polars/.project @@ -0,0 +1,28 @@ + + + com.sap.sailing.polars + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.jdt.core.javanature + org.eclipse.pde.PluginNature + + diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..c3227142674 --- /dev/null +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -0,0 +1,14 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: Domain +Bundle-SymbolicName: com.sap.sailing.polars +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: SAP +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-ClassPath: . +Export-Package: + com.sap.sailing.polars +Bundle-ActivationPolicy: lazy +Require-Bundle: + com.sap.sailing.domain.common;bundle-version="1.0.0" +Import-Package: com.sap.sailing.domain.base diff --git a/java/com.sap.sailing.polars/build.properties b/java/com.sap.sailing.polars/build.properties new file mode 100644 index 00000000000..34d2e4d2dad --- /dev/null +++ b/java/com.sap.sailing.polars/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/java/com.sap.sailing.polars/pom.xml b/java/com.sap.sailing.polars/pom.xml new file mode 100644 index 00000000000..b42128798f9 --- /dev/null +++ b/java/com.sap.sailing.polars/pom.xml @@ -0,0 +1,12 @@ + + + 4.0.0 + + root + com.sap.sailing + 1.0.0-SNAPSHOT + + com.sap.sailing.polars + eclipse-plugin + diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java new file mode 100644 index 00000000000..3896a3281ce --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -0,0 +1,76 @@ +package com.sap.sailing.polars; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; + +public interface PolarDataService { + + /** + * Queries the {@link PolarDataService} for the optimal upwind speed and angle as calculated from the existing + * database. + * + * @param boatClass + * @return The optimal upwind speed of the given boat class with the corresponding bearing difference to the wind. + * Bear in mind that the Bearing difference is an absolute. It can be applied to both sides of the wind. + */ + SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed); + + /** + * Queries the {@link PolarDataService} for the optimal downwind speed and angle as calculated from the existing + * database. + * + * @param boatClass + * @return The optimal downwind speed of the given boat class with the corresponding bearing difference to the wind. + * Bear in mind that the Bearing difference is an absolute. It can be applied to both sides of the wind. + */ + SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed); + + /** + * Queries the {@link PolarDataService} for the optimal reaching speed at a given bearing difference to the wind. + * This should be used for gathering information about non-up&down-wind legs. For up- and downwind legs use + * {@link #getOptimalUpwindSpeedWithBearingFor(BoatClass, Speed)} and + * {@link #getOptimalDownwindSpeedWithBearingFor(BoatClass, Speed)}. + * + * @param boatClass + * @param bearingDifferenceToWind + * absolute bearing difference of the boat's course to the wind + * @return The optimal speed of the given boat class at the given bearing difference to the wind. + */ + Speed getOptimalReachingSpeedFor(BoatClass boatClass, Speed windSpeed, Bearing bearingDifferenceToWind); + + /** + * Queries the {@link PolarDataService} for the average upwind speed and angle as calculated from the existing + * database. + * + * @param boatClass + * @return The average upwind speed of the given boat class with the corresponding bearing difference to the wind. + * Bear in mind that the Bearing difference is an absolute. It can be applied to both sides of the wind. + */ + SpeedWithBearing getAverageUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed); + + /** + * Queries the {@link PolarDataService} for the average downwind speed and angle as calculated from the existing + * database. + * + * @param boatClass + * @return The average downwind speed of the given boat class with the corresponding bearing difference to the wind. + * Bear in mind that the Bearing difference is an absolute. It can be applied to both sides of the wind. + */ + SpeedWithBearing getAverageDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed); + + /** + * Queries the {@link PolarDataService} for the average reaching speed at a given bearing difference to the wind. + * This should be used for gathering information about non-up&down-wind legs. For up- and downwind legs use + * {@link #getAverageUpwindSpeedWithBearingFor(BoatClass, Speed)} and + * {@link #getAverageDownwindSpeedWithBearingFor(BoatClass, Speed)}. + * + * @param boatClass + * @param bearingDifferenceToWind + * absolute bearing difference of the boat's course to the wind + * @return The average speed of the given boat class at the given bearing difference to the wind. + */ + Speed getAverageReachingSpeedFor(BoatClass boatClass, Speed windSpeed, Bearing bearingDifferenceToWind); + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mocked/MockedPolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mocked/MockedPolarDataService.java new file mode 100644 index 00000000000..17563f6502f --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mocked/MockedPolarDataService.java @@ -0,0 +1,47 @@ +package com.sap.sailing.polars.mocked; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.polars.PolarDataService; + +public class MockedPolarDataService implements PolarDataService { + + @Override + public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Speed getOptimalReachingSpeedFor(BoatClass boatClass, Speed windSpeed, Bearing bearingDifferenceToWind) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SpeedWithBearing getAverageUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SpeedWithBearing getAverageDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + // TODO Auto-generated method stub + return null; + } + + @Override + public Speed getAverageReachingSpeedFor(BoatClass boatClass, Speed windSpeed, Bearing bearingDifferenceToWind) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/java/pom.xml b/java/pom.xml index 1fd46022472..ffcaf8292cf 100755 --- a/java/pom.xml +++ b/java/pom.xml @@ -95,6 +95,7 @@ de.csenk.gwt.ws com.sap.sailing.selenium.test com.sap.sailing.feature.p2build + com.sap.sailing.polars From 9593b550757bd032e1089dc276709fc8ef19712c Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 4 Dec 2013 16:09:11 +0100 Subject: [PATCH 02/72] Started to work on service side polar data service --- .../META-INF/MANIFEST.MF | 4 +++- .../sap/sailing/polars/impl/PolarFixCache.java | 16 ++++++++++++++++ .../polars/impl/PolarFixCacheUpdater.java | 18 ++++++++++++++++++ 3 files changed, 37 insertions(+), 1 deletion(-) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCache.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCacheUpdater.java diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index c3227142674..164377355c2 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -11,4 +11,6 @@ Export-Package: Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0" -Import-Package: com.sap.sailing.domain.base +Import-Package: com.sap.sailing.domain.base, + com.sap.sailing.domain.polarsheets, + com.sap.sailing.util diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCache.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCache.java new file mode 100644 index 00000000000..b5b333bf118 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCache.java @@ -0,0 +1,16 @@ +package com.sap.sailing.polars.impl; + +import java.util.List; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.polarsheets.PolarFix; +import com.sap.sailing.util.SmartFutureCache; + +public class PolarFixCache extends + SmartFutureCache, com.sap.sailing.util.SmartFutureCache.EmptyUpdateInterval> { + + public PolarFixCache() { + super(new PolarFixCacheUpdater(), "polarFixCache"); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCacheUpdater.java new file mode 100644 index 00000000000..ad652613122 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCacheUpdater.java @@ -0,0 +1,18 @@ +package com.sap.sailing.polars.impl; + +import java.util.List; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.polarsheets.PolarFix; +import com.sap.sailing.util.SmartFutureCache.AbstractCacheUpdater; +import com.sap.sailing.util.SmartFutureCache.EmptyUpdateInterval; + +public class PolarFixCacheUpdater extends AbstractCacheUpdater, EmptyUpdateInterval> { + + @Override + public List computeCacheUpdate(BoatClass key, EmptyUpdateInterval updateInterval) throws Exception { + // TODO Auto-generated method stub + return null; + } + +} From 4bca584eb74789951959bd54c1f95086ba3d49dc Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 11 Dec 2013 17:22:05 +0100 Subject: [PATCH 03/72] Refactored existing polar features moved to polars bundle moved test to polars.test bundle split generator into two (segregation of concerns) --- .../domain/common/PolarSheetsData.java | 2 - .../common/impl/PolarSheetsDataImpl.java | 12 +- .../META-INF/MANIFEST.MF | 1 - .../polarsheets/AngleSpeedOddClassifier.java | 24 - .../domain/polarsheets/OddFixClassifier.java | 16 - .../test/PolarSheetGenerationServiceTest.java | 1 - .../META-INF/MANIFEST.MF | 6 +- .../gwt/ui/server/SailingServiceImpl.java | 10 +- java/com.sap.sailing.polars.test/.classpath | 7 + java/com.sap.sailing.polars.test/.project | 28 + .../.settings/org.eclipse.jdt.core.prefs | 7 + .../.settings/org.eclipse.pde.core.prefs | 3 + .../META-INF/MANIFEST.MF | 19 + .../build.properties | 4 + .../test/PolarSheetGenerationTest.java | 118 ++-- .../META-INF/MANIFEST.MF | 13 +- .../sap/sailing/polars/PolarDataService.java | 22 + .../PolarFixAggregationWorker.java} | 20 +- .../aggregation/PolarFixAggregator.java | 132 ++++ .../sailing/polars/caching/PolarFixCache.java | 18 + .../caching/PolarFixCacheRaceInterval.java | 33 + .../polars/caching/PolarFixCacheUpdater.java | 29 + .../data}/BoatAndWindSpeedWithOriginInfo.java | 2 +- .../polars/data/DataPointWithOriginInfo.java | 11 + .../com/sap/sailing/polars/data/PolarFix.java | 18 + .../BoatAndWindSpeedWithOriginInfoImpl.java | 3 +- .../impl/DataPointWithOriginInfoImpl.java} | 11 +- .../polars/data/impl/PolarFixImpl.java} | 12 +- .../factory/PolarDataServiceFactory.java | 16 + .../impl/PolarDataServiceImplFactory.java | 14 + .../generation/PolarSheetGenerator.java} | 661 ++++++++---------- .../PolarSheetHistogramBuilder.java | 13 +- .../PolarDataServiceImpl.java} | 24 +- .../sailing/polars/impl/PolarFixCache.java | 16 - .../polars/impl/PolarFixCacheUpdater.java | 18 - 35 files changed, 778 insertions(+), 566 deletions(-) delete mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/AngleSpeedOddClassifier.java delete mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/OddFixClassifier.java create mode 100644 java/com.sap.sailing.polars.test/.classpath create mode 100644 java/com.sap.sailing.polars.test/.project create mode 100644 java/com.sap.sailing.polars.test/.settings/org.eclipse.jdt.core.prefs create mode 100644 java/com.sap.sailing.polars.test/.settings/org.eclipse.pde.core.prefs create mode 100644 java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF create mode 100644 java/com.sap.sailing.polars.test/build.properties rename java/{com.sap.sailing.domain.test/src/com/sap/sailing/domain => com.sap.sailing.polars.test/src/com/sap/sailing/polars}/test/PolarSheetGenerationTest.java (74%) rename java/{com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PerRaceAndCompetitorPolarSheetGenerationWorker.java => com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregationWorker.java} (90%) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java rename java/{com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets => com.sap.sailing.polars/src/com/sap/sailing/polars/data}/BoatAndWindSpeedWithOriginInfo.java (81%) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/DataPointWithOriginInfo.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/PolarFix.java rename java/{com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets => com.sap.sailing.polars/src/com/sap/sailing/polars/data/impl}/BoatAndWindSpeedWithOriginInfoImpl.java (85%) rename java/{com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/DataPointWithOriginInfo.java => com.sap.sailing.polars/src/com/sap/sailing/polars/data/impl/DataPointWithOriginInfoImpl.java} (64%) rename java/{com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PolarFix.java => com.sap.sailing.polars/src/com/sap/sailing/polars/data/impl/PolarFixImpl.java} (93%) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/PolarDataServiceFactory.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/impl/PolarDataServiceImplFactory.java rename java/{com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PolarSheetGenerationWorker.java => com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetGenerator.java} (69%) rename java/{com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets => com.sap.sailing.polars/src/com/sap/sailing/polars/generation}/PolarSheetHistogramBuilder.java (88%) rename java/com.sap.sailing.polars/src/com/sap/sailing/polars/{mocked/MockedPolarDataService.java => impl/PolarDataServiceImpl.java} (56%) delete mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCache.java delete mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCacheUpdater.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsData.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsData.java index 9b6ac7c36b3..5c06f20c3c5 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsData.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsData.java @@ -8,8 +8,6 @@ public interface PolarSheetsData extends Serializable { Number[][] getAveragedPolarDataByWindSpeed(); int getDataCount(); - - boolean isComplete(); Integer[] getDataCountPerAngleForWindspeed(int beaufort); diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetsDataImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetsDataImpl.java index 2a5c10dca86..8a3696069cb 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetsDataImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetsDataImpl.java @@ -12,8 +12,6 @@ public class PolarSheetsDataImpl implements PolarSheetsData { private Number[][] averagedPolarDataByWindSpeed; - private boolean complete; - private int dataCount; private WindStepping stepping; @@ -25,9 +23,10 @@ public class PolarSheetsDataImpl implements PolarSheetsData { //For GWT Serialization PolarSheetsDataImpl() {}; - public PolarSheetsDataImpl(Number[][] averagedPolarDataByWindSpeed, boolean complete, int dataCount, Map dataCountPerAngleForWindspeed, WindStepping stepping, Map> histogramDataMap) { + public PolarSheetsDataImpl(Number[][] averagedPolarDataByWindSpeed, int dataCount, + Map dataCountPerAngleForWindspeed, WindStepping stepping, + Map> histogramDataMap) { this.averagedPolarDataByWindSpeed = averagedPolarDataByWindSpeed; - this.complete = complete; this.dataCount = dataCount; this.dataCountPerAngleForWindspeed = dataCountPerAngleForWindspeed; this.stepping = stepping; @@ -40,11 +39,6 @@ public class PolarSheetsDataImpl implements PolarSheetsData { return averagedPolarDataByWindSpeed; } - @Override - public boolean isComplete() { - return complete; - } - @Override public int getDataCount() { return dataCount; diff --git a/java/com.sap.sailing.domain/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain/META-INF/MANIFEST.MF index 46828bfb73d..f4c477f08ef 100755 --- a/java/com.sap.sailing.domain/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain/META-INF/MANIFEST.MF @@ -15,7 +15,6 @@ Export-Package: com.sap.sailing.domain.base, com.sap.sailing.domain.leaderboard.impl, com.sap.sailing.domain.leaderboard.meta, com.sap.sailing.domain.masterdataimport, - com.sap.sailing.domain.polarsheets, com.sap.sailing.domain.racelog, com.sap.sailing.domain.racelog.impl, com.sap.sailing.domain.tracking, diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/AngleSpeedOddClassifier.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/AngleSpeedOddClassifier.java deleted file mode 100644 index b6c83ae8499..00000000000 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/AngleSpeedOddClassifier.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.sap.sailing.domain.polarsheets; - -import java.util.logging.Level; -import java.util.logging.Logger; - -public class AngleSpeedOddClassifier implements OddFixClassifier { - - - private static final Logger logger = Logger.getLogger(AngleSpeedOddClassifier.class.getName()); - - @Override - public boolean classifiesAsOdd(PolarFix fix) { - double angleToWind = fix.getAngleToWind(); - double speedInKnots = fix.getBoatSpeed().getKnots(); - if (angleToWind < 20 && angleToWind > -20) { - if (speedInKnots > 2) { - logger.log(Level.INFO, "Boat goes fast against the wind. Data-Point should have been excluded during maneuver check."); - return true; - } - } - return false; - } - -} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/OddFixClassifier.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/OddFixClassifier.java deleted file mode 100644 index 12874568df3..00000000000 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/OddFixClassifier.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.sap.sailing.domain.polarsheets; - - -public interface OddFixClassifier { - - /** - * Used for classifying fixes from a logical perspective. - * - * Example: An OddFixClassifier could check if a boat has high speed when going against the wind. - * - * @param polarFix to be classified - * @return True if odd from a sailing perspective, false if it makes sense. Classification depends on the type of OddFixClassifier used. - */ - public boolean classifiesAsOdd(PolarFix polarFix); - -} diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PolarSheetGenerationServiceTest.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PolarSheetGenerationServiceTest.java index db44811d05b..d3150d3d5b2 100755 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PolarSheetGenerationServiceTest.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/PolarSheetGenerationServiceTest.java @@ -81,7 +81,6 @@ public class PolarSheetGenerationServiceTest { PolarSheetsData results = triggerData.getData(); - Assert.assertTrue(results.isComplete()); Assert.assertNotNull(results); Assert.assertEquals(4, results.getDataCount()); diff --git a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF index 710b6c6c104..05bca768a8d 100755 --- a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF @@ -30,10 +30,12 @@ Require-Bundle: com.sap.sailing.domain, com.google.gwt.osgi;bundle-version="2.5.1" Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator Bundle-ActivationPolicy: lazy -Import-Package: javax.jms;version="1.1.0", +Import-Package: com.sap.sailing.polars, + com.sap.sailing.polars.factory, + javax.jms;version="1.1.0", javax.servlet;version="2.6.0", - javax.servlet.http;version="2.6.0", javax.servlet.descriptor;version="2.6.0", + javax.servlet.http;version="2.6.0", org.json.simple, org.json.simple.parser, org.osgi.framework, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index e53d8a1fcbb..d4724d46066 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -156,7 +156,6 @@ import com.sap.sailing.domain.leaderboard.caching.LiveLeaderboardUpdater; import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.MongoRaceLogStoreFactory; -import com.sap.sailing.domain.polarsheets.PolarSheetGenerationWorker; import com.sap.sailing.domain.racelog.RaceLog; import com.sap.sailing.domain.racelog.RaceLogFlagEvent; import com.sap.sailing.domain.racelog.RaceStateOfSameDayHelper; @@ -252,6 +251,8 @@ import com.sap.sailing.gwt.ui.shared.WaypointDTO; import com.sap.sailing.gwt.ui.shared.WindDTO; import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO; import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO; +import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.factory.PolarDataServiceFactory; import com.sap.sailing.resultimport.ResultUrlProvider; import com.sap.sailing.resultimport.ResultUrlRegistry; import com.sap.sailing.server.RacingEventService; @@ -360,6 +361,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private final SwissTimingReplayService swissTimingReplayService; + private PolarDataService polarDataService; + public SailingServiceImpl() { BundleContext context = Activator.getDefault(); racingEventServiceTracker = createAndOpenRacingEventServiceTracker(context); @@ -399,6 +402,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S /* maximumPoolSize */ THREAD_POOL_SIZE, /* keepAliveTime */ 60, TimeUnit.SECONDS, /* workQueue */ new LinkedBlockingQueue()); + polarDataService = PolarDataServiceFactory.createStandardPolarDataService(); } protected SwissTimingReplayService getSwissTimingReplayService(BundleContext context) { @@ -3097,12 +3101,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S for (RegattaAndRaceIdentifier race : selectedRaces) { trackedRaces.add(service.getTrackedRace(race)); } - PolarSheetGenerationWorker genWorker = new PolarSheetGenerationWorker(trackedRaces, settings, executor); - genWorker.startPolarSheetGeneration(); if (name == null || name.isEmpty()) { name = getCommonBoatClass(trackedRaces); } - PolarSheetsData result = genWorker.get(); + PolarSheetsData result = polarDataService.generatePolarSheet(trackedRaces, settings, executor); return new PolarSheetGenerationResponseImpl(id, name, result); } diff --git a/java/com.sap.sailing.polars.test/.classpath b/java/com.sap.sailing.polars.test/.classpath new file mode 100644 index 00000000000..098194ca4b7 --- /dev/null +++ b/java/com.sap.sailing.polars.test/.classpath @@ -0,0 +1,7 @@ + + + + + + + diff --git a/java/com.sap.sailing.polars.test/.project b/java/com.sap.sailing.polars.test/.project new file mode 100644 index 00000000000..7980f4925e6 --- /dev/null +++ b/java/com.sap.sailing.polars.test/.project @@ -0,0 +1,28 @@ + + + com.sap.sailing.polars.test + + + + + + org.eclipse.jdt.core.javabuilder + + + + + org.eclipse.pde.ManifestBuilder + + + + + org.eclipse.pde.SchemaBuilder + + + + + + org.eclipse.pde.PluginNature + org.eclipse.jdt.core.javanature + + diff --git a/java/com.sap.sailing.polars.test/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sailing.polars.test/.settings/org.eclipse.jdt.core.prefs new file mode 100644 index 00000000000..f42de363afa --- /dev/null +++ b/java/com.sap.sailing.polars.test/.settings/org.eclipse.jdt.core.prefs @@ -0,0 +1,7 @@ +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 diff --git a/java/com.sap.sailing.polars.test/.settings/org.eclipse.pde.core.prefs b/java/com.sap.sailing.polars.test/.settings/org.eclipse.pde.core.prefs new file mode 100644 index 00000000000..f29e940a005 --- /dev/null +++ b/java/com.sap.sailing.polars.test/.settings/org.eclipse.pde.core.prefs @@ -0,0 +1,3 @@ +eclipse.preferences.version=1 +pluginProject.extensions=false +resolve.requirebundle=false diff --git a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF new file mode 100644 index 00000000000..f556d168749 --- /dev/null +++ b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF @@ -0,0 +1,19 @@ +Manifest-Version: 1.0 +Bundle-ManifestVersion: 2 +Bundle-Name: PolarsTest +Bundle-SymbolicName: com.sap.sailing.polars.test +Bundle-Version: 1.0.0.qualifier +Bundle-Vendor: SAP +Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Import-Package: com.sap.sailing.domain.base, + com.sap.sailing.domain.base.impl, + com.sap.sailing.domain.common, + com.sap.sailing.domain.common.impl, + com.sap.sailing.domain.test.mock, + com.sap.sailing.domain.tracking, + com.sap.sailing.domain.tracking.impl, + com.sap.sailing.polars.aggregation, + com.sap.sailing.polars.data, + com.sap.sailing.polars.data.impl, + com.sap.sailing.polars.generation, + org.junit;version="4.8.2" diff --git a/java/com.sap.sailing.polars.test/build.properties b/java/com.sap.sailing.polars.test/build.properties new file mode 100644 index 00000000000..34d2e4d2dad --- /dev/null +++ b/java/com.sap.sailing.polars.test/build.properties @@ -0,0 +1,4 @@ +source.. = src/ +output.. = bin/ +bin.includes = META-INF/,\ + . diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PolarSheetGenerationTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetGenerationTest.java similarity index 74% rename from java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PolarSheetGenerationTest.java rename to java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetGenerationTest.java index 1d6d1b6f49e..7327ab723e6 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/PolarSheetGenerationTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetGenerationTest.java @@ -1,4 +1,4 @@ -package com.sap.sailing.domain.test; +package com.sap.sailing.polars.test; import java.util.ArrayList; import java.util.HashMap; @@ -6,8 +6,10 @@ import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.NavigableSet; +import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentSkipListSet; +import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; @@ -45,10 +47,6 @@ import com.sap.sailing.domain.common.impl.Util; import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; -import com.sap.sailing.domain.polarsheets.DataPointWithOriginInfo; -import com.sap.sailing.domain.polarsheets.PerRaceAndCompetitorPolarSheetGenerationWorker; -import com.sap.sailing.domain.polarsheets.PolarSheetGenerationWorker; -import com.sap.sailing.domain.polarsheets.PolarSheetHistogramBuilder; import com.sap.sailing.domain.test.mock.MockedTrackedRace; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.GPSFixMoving; @@ -62,11 +60,18 @@ import com.sap.sailing.domain.tracking.impl.MarkPassingByTimeComparator; import com.sap.sailing.domain.tracking.impl.MarkPassingImpl; import com.sap.sailing.domain.tracking.impl.WindImpl; import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl; +import com.sap.sailing.polars.aggregation.PolarFixAggregationWorker; +import com.sap.sailing.polars.aggregation.PolarFixAggregator; +import com.sap.sailing.polars.data.DataPointWithOriginInfo; +import com.sap.sailing.polars.data.PolarFix; +import com.sap.sailing.polars.data.impl.DataPointWithOriginInfoImpl; +import com.sap.sailing.polars.generation.PolarSheetGenerator; +import com.sap.sailing.polars.generation.PolarSheetHistogramBuilder; public class PolarSheetGenerationTest { @Test - public void testPolarSheetRawDataGeneration() throws InterruptedException { + public void testPolarSheetRawDataGeneration() throws InterruptedException, ExecutionException { Executor executor = new ThreadPoolExecutor(/* corePoolSize */Runtime.getRuntime().availableProcessors(), /* maximumPoolSize */Runtime.getRuntime().availableProcessors(), /* keepAliveTime */60, TimeUnit.SECONDS, @@ -82,27 +87,26 @@ public class PolarSheetGenerationTest { TimePoint startTime = new MillisecondsTimePoint(9); TimePoint endTime = new MillisecondsTimePoint(80); // Only used for storing and exporting results in this test case: - PolarSheetGenerationWorker resultContainer = new PolarSheetGenerationWorker(new HashSet(), + PolarFixAggregator resultContainer = new PolarFixAggregator(new HashSet(), settings, executor); BoatClass forelle = new BoatClassImpl("Forelle", true); Competitor competitor = new CompetitorImpl(UUID.randomUUID(), "Hans Frantz", Color.RED, new TeamImpl("SAP", null, null), new BoatImpl("Schnelle Forelle", forelle, "GER000")); - PerRaceAndCompetitorPolarSheetGenerationWorker task = new PerRaceAndCompetitorPolarSheetGenerationWorker(race, + PolarFixAggregationWorker task = new PolarFixAggregationWorker(race, resultContainer, startTime, endTime, competitor, settings); executor.execute(task); - double timeUntilTimeout = 1000; while (!task.isDone() && timeUntilTimeout > 0) { Thread.sleep(100); timeUntilTimeout = timeUntilTimeout - 0.1; } + Set fixes = resultContainer.get(); - Assert.assertTrue(task.isDone()); - - PolarSheetsData data = resultContainer.getPolarData(); + PolarSheetGenerator generator = new PolarSheetGenerator(fixes, settings); + PolarSheetsData data = generator.generate(); Assert.assertEquals(4, data.getDataCount()); Assert.assertEquals(4.0, data.getAveragedPolarDataByWindSpeed()[0][45]); Assert.assertEquals(2.0, data.getAveragedPolarDataByWindSpeed()[0][55]); @@ -120,15 +124,15 @@ public class PolarSheetGenerationTest { List rawData = new ArrayList(); - rawData.add(new DataPointWithOriginInfo(1.09, "", "")); - rawData.add(new DataPointWithOriginInfo(1.0, "", "")); - rawData.add(new DataPointWithOriginInfo(1.11, "", "")); - rawData.add(new DataPointWithOriginInfo(1.46, "", "")); - rawData.add(new DataPointWithOriginInfo(1.56, "", "")); - rawData.add(new DataPointWithOriginInfo(2.05, "", "")); - rawData.add(new DataPointWithOriginInfo(2.09, "", "")); - rawData.add(new DataPointWithOriginInfo(3.0, "", "")); - rawData.add(new DataPointWithOriginInfo(2.999, "", "")); + rawData.add(new DataPointWithOriginInfoImpl(1.09, "", "")); + rawData.add(new DataPointWithOriginInfoImpl(1.0, "", "")); + rawData.add(new DataPointWithOriginInfoImpl(1.11, "", "")); + rawData.add(new DataPointWithOriginInfoImpl(1.46, "", "")); + rawData.add(new DataPointWithOriginInfoImpl(1.56, "", "")); + rawData.add(new DataPointWithOriginInfoImpl(2.05, "", "")); + rawData.add(new DataPointWithOriginInfoImpl(2.09, "", "")); + rawData.add(new DataPointWithOriginInfoImpl(3.0, "", "")); + rawData.add(new DataPointWithOriginInfoImpl(2.999, "", "")); PolarSheetsHistogramData result = builder.build(rawData, 0, 0); Number[] xValues = result.getxValues(); Assert.assertEquals(10, xValues.length); @@ -145,49 +149,49 @@ public class PolarSheetGenerationTest { public void testOutlierNeighborhoodAlgorithm() { PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); List values = new ArrayList(); - values.add(new DataPointWithOriginInfo(0., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); - values.add(new DataPointWithOriginInfo(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(0., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); + values.add(new DataPointWithOriginInfoImpl(20., "", "")); int[] count = new int[1]; count[0] = values.size(); Map> valuesInMap = new HashMap>(); valuesInMap.put(0, values); - double pct = PolarSheetGenerationWorker.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 0, - new DataPointWithOriginInfo(0.0, "", ""), settings); + double pct = PolarSheetGenerator.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 0, + new DataPointWithOriginInfoImpl(0.0, "", ""), settings); Assert.assertTrue(pct < 0.05); - pct = PolarSheetGenerationWorker.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 2, - new DataPointWithOriginInfo(20.0, "", ""), settings); + pct = PolarSheetGenerator.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 2, + new DataPointWithOriginInfoImpl(20.0, "", ""), settings); Assert.assertTrue(pct > 0.05); diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index 164377355c2..b42d9436d8f 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -7,10 +7,17 @@ Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ClassPath: . Export-Package: - com.sap.sailing.polars + com.sap.sailing.polars, + com.sap.sailing.polars.aggregation, + com.sap.sailing.polars.data, + com.sap.sailing.polars.data.impl, + com.sap.sailing.polars.factory, + com.sap.sailing.polars.generation Bundle-ActivationPolicy: lazy Require-Bundle: - com.sap.sailing.domain.common;bundle-version="1.0.0" + com.sap.sailing.domain.common;bundle-version="1.0.0", + com.sap.sailing.domain Import-Package: com.sap.sailing.domain.base, - com.sap.sailing.domain.polarsheets, + com.sap.sailing.domain.confidence, + com.sap.sailing.domain.tracking, com.sap.sailing.util diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 3896a3281ce..e93dc0f9924 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -1,9 +1,16 @@ package com.sap.sailing.polars; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; + import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.tracking.TrackedRace; public interface PolarDataService { @@ -73,4 +80,19 @@ public interface PolarDataService { */ Speed getAverageReachingSpeedFor(BoatClass boatClass, Speed windSpeed, Bearing bearingDifferenceToWind); + /** + * Generates a polar sheet for geven races and settings using the provided executor for the worker threads. This + * method does not access a cache for now. + * + * @param trackedRaces + * The set of races to generate the diagram for. + * @param settings + * Settings as supplied by the user. + * @param executor + * The executor to run the worker threads with. + * @return The generated polar sheet with meta data. + */ + PolarSheetsData generatePolarSheet(Set trackedRaces, PolarSheetGenerationSettings settings, + Executor executor) throws InterruptedException, ExecutionException; + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PerRaceAndCompetitorPolarSheetGenerationWorker.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregationWorker.java similarity index 90% rename from java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PerRaceAndCompetitorPolarSheetGenerationWorker.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregationWorker.java index f844782ba9f..f39460e5461 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PerRaceAndCompetitorPolarSheetGenerationWorker.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregationWorker.java @@ -1,4 +1,4 @@ -package com.sap.sailing.domain.polarsheets; +package com.sap.sailing.polars.aggregation; import java.util.ArrayList; import java.util.Collection; @@ -20,21 +20,21 @@ import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.WindWithConfidence; +import com.sap.sailing.polars.data.PolarFix; +import com.sap.sailing.polars.data.impl.PolarFixImpl; /** - * Iterates through the fixes of one competitor in one tracked race and fills the {@link PolarSheetGenerationWorker} + * Iterates through the fixes of one competitor in one tracked race and fills the {@link PolarFixAggregator} * with datapoints that are found for the speed of the boat and its angle to the wind * * @author D054528 Frederik Petersen * */ -public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable { - - // private static final Logger logger = Logger.getLogger(PerRaceAndCompetitorPolarSheetGenerationWorker.class.getName()); +public class PolarFixAggregationWorker implements Runnable { private final TrackedRace race; - private final PolarSheetGenerationWorker polarSheetGenerationWorker; + private final PolarFixAggregator polarSheetGenerationWorker; private TimePoint startTime; @@ -50,8 +50,8 @@ public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable private PolarSheetGenerationSettings settings; - public PerRaceAndCompetitorPolarSheetGenerationWorker(TrackedRace race, - PolarSheetGenerationWorker polarSheetGenerationWorker, TimePoint startTime, TimePoint endTime, + public PolarFixAggregationWorker(TrackedRace race, + PolarFixAggregator polarSheetGenerationWorker, TimePoint startTime, TimePoint endTime, Competitor competitor, PolarSheetGenerationSettings settings) { super(); this.race = race; @@ -157,9 +157,9 @@ public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable .getB(); if (windWithConfidence != null && windWithConfidence.useSpeed() && windWithConfidence.getConfidence() >= settings.getMinimumWindConfidence()) { - PolarFix polarFix = new PolarFix(fix, race, track, windWithConfidence.getObject(), + PolarFix polarFix = new PolarFixImpl(fix, race, track, windWithConfidence.getObject(), settings, windWithSourceIdStringPair.getA()); - polarSheetGenerationWorker.addPolarData(polarFix); + polarSheetGenerationWorker.addPolarFix(polarFix); } } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java new file mode 100644 index 00000000000..5a1d7e52e4d --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java @@ -0,0 +1,132 @@ +package com.sap.sailing.polars.aggregation; + +import java.util.HashSet; +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; +import java.util.concurrent.Future; +import java.util.concurrent.TimeUnit; +import java.util.concurrent.TimeoutException; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.polars.data.PolarFix; + +/** + * Allows extracting data about average speed for each angle creating the foundation of polar-sheet generation. Tasks + * are assigned to the executor per race & per competitor and the results are filled on the go to allow progress + * indication in the polar-sheet itself. + * + * @author D054528 Frederik Petersen + * + */ +public class PolarFixAggregator implements Future> { + + private final Set workers; + + private final Executor executor; + + private final Set fixes = new HashSet(); + + /** + * Will prepare the {@link PolarFixAggregationWorker}s per race & per competitor. This includes + * determining start and end time. + * + * @param trackedRaces + * from which the data is to be collected + * @param settings + * @param executor + * executes the tasks upon {@link #startPolarFixAggregation()} + */ + public PolarFixAggregator(Set trackedRaces, PolarSheetGenerationSettings settings, Executor executor) { + this.executor = executor; + workers = new HashSet(); + for (TrackedRace race : trackedRaces) { + TimePoint startTime = race.getStartOfRace(); + TimePoint endTime = race.getEndOfRace(); + if (endTime == null) { + endTime = race.getTimePointOfNewestEvent(); + } + RaceDefinition raceDefinition = race.getRace(); + Iterable competitors = raceDefinition.getCompetitors(); + + for (Competitor competitor : competitors) { + PolarFixAggregationWorker task = new PolarFixAggregationWorker( + race, this, startTime, endTime, competitor, settings); + workers.add(task); + } + + } + } + + /** + * Starts the {@link PolarFixAggregationWorker}s + */ + public void startPolarFixAggregation() { + for (Runnable task : workers) { + executor.execute(task); + } + } + + /** + * To be called from {@link PolarFixAggregationWorker} for adding a datapoint to the list of + * results + * wind's speed in knots + */ + protected void addPolarFix(PolarFix polarFix) { + fixes.add(polarFix); + } + + private boolean allWorkersDone() { + boolean complete = true; + for (PolarFixAggregationWorker task : workers) { + if (!task.isDone()) { + complete = false; + break; + } + } + return complete; + } + + @Override + public boolean cancel(boolean mayInterruptIfRunning) { + return false; + } + + @Override + public boolean isCancelled() { + return false; + } + + @Override + public boolean isDone() { + return allWorkersDone(); + } + + @Override + public Set get() throws InterruptedException, ExecutionException { + while (!isDone()) { + Thread.sleep(100); + } + return fixes; + } + + @Override + public Set get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, + TimeoutException { + long timeRun = 0; + long timeoutInMillis = unit.toMillis(timeout); + while (!isDone() && timeRun < timeoutInMillis) { + Thread.sleep(100); + timeRun = timeRun + 100; + } + if (timeRun >= timeoutInMillis) { + throw new TimeoutException(); + } + return fixes; + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java new file mode 100644 index 00000000000..d06c1fdce3f --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java @@ -0,0 +1,18 @@ +package com.sap.sailing.polars.caching; + +import java.util.List; +import java.util.Map; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.polars.data.impl.PolarFixImpl; +import com.sap.sailing.util.SmartFutureCache; + +public class PolarFixCache extends + SmartFutureCache>, PolarFixCacheRaceInterval> { + + public PolarFixCache() { + super(new PolarFixCacheUpdater(), "polarFixCache"); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java new file mode 100644 index 00000000000..8a14fad2114 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java @@ -0,0 +1,33 @@ +package com.sap.sailing.polars.caching; + +import java.util.HashSet; +import java.util.Set; + +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.util.SmartFutureCache.UpdateInterval; + +public class PolarFixCacheRaceInterval implements UpdateInterval { + + private final Set races; + + public PolarFixCacheRaceInterval(Set races) { + this.races = races; + } + + public Set getRaces() { + return races; + } + + @Override + public PolarFixCacheRaceInterval join(PolarFixCacheRaceInterval otherUpdateInterval) { + Set joined = new HashSet(); + for (RegattaAndRaceIdentifier race : races) { + joined.add(race); + } + for (RegattaAndRaceIdentifier race : otherUpdateInterval.getRaces()) { + joined.add(race); + } + return new PolarFixCacheRaceInterval(joined); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java new file mode 100644 index 00000000000..8ec0fdcdf3d --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java @@ -0,0 +1,29 @@ +package com.sap.sailing.polars.caching; + +import java.util.List; +import java.util.Map; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.polars.data.impl.PolarFixImpl; +import com.sap.sailing.util.SmartFutureCache.CacheUpdater; + +public class PolarFixCacheUpdater implements + CacheUpdater>, PolarFixCacheRaceInterval> { + + @Override + public Map> computeCacheUpdate(BoatClass key, + PolarFixCacheRaceInterval updateInterval) throws Exception { + // TODO Auto-generated method stub + return null; + } + + @Override + public Map> provideNewCacheValue(BoatClass key, + Map> oldCacheValue, + Map> computedCacheUpdate, PolarFixCacheRaceInterval updateInterval) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/BoatAndWindSpeedWithOriginInfo.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/BoatAndWindSpeedWithOriginInfo.java similarity index 81% rename from java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/BoatAndWindSpeedWithOriginInfo.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/BoatAndWindSpeedWithOriginInfo.java index c3f537e45b4..7e10cb89af0 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/BoatAndWindSpeedWithOriginInfo.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/BoatAndWindSpeedWithOriginInfo.java @@ -1,4 +1,4 @@ -package com.sap.sailing.domain.polarsheets; +package com.sap.sailing.polars.data; import com.sap.sailing.domain.common.Speed; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/DataPointWithOriginInfo.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/DataPointWithOriginInfo.java new file mode 100644 index 00000000000..9d8887e4064 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/DataPointWithOriginInfo.java @@ -0,0 +1,11 @@ +package com.sap.sailing.polars.data; + +public interface DataPointWithOriginInfo extends Comparable { + + public abstract Double getRawData(); + + public abstract String getWindGaugeIdString(); + + public abstract String getDayString(); + +} \ No newline at end of file diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/PolarFix.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/PolarFix.java new file mode 100644 index 00000000000..23f39d0e57d --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/PolarFix.java @@ -0,0 +1,18 @@ +package com.sap.sailing.polars.data; + +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; + +public interface PolarFix { + + public abstract SpeedWithBearing getBoatSpeed(); + + public abstract Speed getWindSpeed(); + + public abstract double getAngleToWind(); + + public abstract String getGaugeIdString(); + + public abstract String getDayString(); + +} \ No newline at end of file diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/BoatAndWindSpeedWithOriginInfoImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/impl/BoatAndWindSpeedWithOriginInfoImpl.java similarity index 85% rename from java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/BoatAndWindSpeedWithOriginInfoImpl.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/impl/BoatAndWindSpeedWithOriginInfoImpl.java index 38523f95363..d9d7cccd76f 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/BoatAndWindSpeedWithOriginInfoImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/impl/BoatAndWindSpeedWithOriginInfoImpl.java @@ -1,6 +1,7 @@ -package com.sap.sailing.domain.polarsheets; +package com.sap.sailing.polars.data.impl; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.polars.data.BoatAndWindSpeedWithOriginInfo; public class BoatAndWindSpeedWithOriginInfoImpl implements BoatAndWindSpeedWithOriginInfo { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/DataPointWithOriginInfo.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/impl/DataPointWithOriginInfoImpl.java similarity index 64% rename from java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/DataPointWithOriginInfo.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/impl/DataPointWithOriginInfoImpl.java index a955bf1f6d3..a0fbe3af361 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/DataPointWithOriginInfo.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/data/impl/DataPointWithOriginInfoImpl.java @@ -1,6 +1,8 @@ -package com.sap.sailing.domain.polarsheets; +package com.sap.sailing.polars.data.impl; -public class DataPointWithOriginInfo implements Comparable{ +import com.sap.sailing.polars.data.DataPointWithOriginInfo; + +public class DataPointWithOriginInfoImpl implements DataPointWithOriginInfo { private Double rawData; @@ -8,20 +10,23 @@ public class DataPointWithOriginInfo implements Comparable track, Wind windSpeed, + public PolarFixImpl(GPSFixMoving fix, TrackedRace race, GPSFixTrack track, Wind windSpeed, PolarSheetGenerationSettings settings, String gaugeIdString) { boatSpeed = track.getEstimatedSpeed(fix.getTimePoint()); Bearing bearing = boatSpeed.getBearing(); @@ -93,22 +94,27 @@ public class PolarFix { return windSourcesToExclude; } + @Override public SpeedWithBearing getBoatSpeed() { return boatSpeed; } + @Override public Speed getWindSpeed() { return windSpeed; } + @Override public double getAngleToWind() { return angleToWind; } + @Override public String getGaugeIdString() { return gaugeIdString; } + @Override public String getDayString() { return dayString; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/PolarDataServiceFactory.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/PolarDataServiceFactory.java new file mode 100644 index 00000000000..3247b032cc7 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/PolarDataServiceFactory.java @@ -0,0 +1,16 @@ +package com.sap.sailing.polars.factory; + +import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.factory.impl.PolarDataServiceImplFactory; + +public abstract class PolarDataServiceFactory { + + public static PolarDataService createStandardPolarDataService() { + PolarDataServiceFactory factory = new PolarDataServiceImplFactory(); + return factory.createPolarDataService(); + } + + protected abstract PolarDataService createPolarDataService(); + + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/impl/PolarDataServiceImplFactory.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/impl/PolarDataServiceImplFactory.java new file mode 100644 index 00000000000..2ea48ceb772 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/impl/PolarDataServiceImplFactory.java @@ -0,0 +1,14 @@ +package com.sap.sailing.polars.factory.impl; + +import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.factory.PolarDataServiceFactory; +import com.sap.sailing.polars.impl.PolarDataServiceImpl; + +public class PolarDataServiceImplFactory extends PolarDataServiceFactory { + + @Override + public PolarDataService createPolarDataService() { + return new PolarDataServiceImpl(); + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PolarSheetGenerationWorker.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetGenerator.java similarity index 69% rename from java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PolarSheetGenerationWorker.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetGenerator.java index 512ec0c81c0..f512a9ca01e 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PolarSheetGenerationWorker.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetGenerator.java @@ -1,387 +1,274 @@ -package com.sap.sailing.domain.polarsheets; - -import java.util.ArrayList; -import java.util.Collections; -import java.util.HashMap; -import java.util.HashSet; -import java.util.List; -import java.util.Map; -import java.util.Set; -import java.util.concurrent.ExecutionException; -import java.util.concurrent.Executor; -import java.util.concurrent.Future; -import java.util.concurrent.TimeUnit; -import java.util.concurrent.TimeoutException; - -import com.sap.sailing.domain.base.Competitor; -import com.sap.sailing.domain.base.RaceDefinition; -import com.sap.sailing.domain.common.PolarSheetGenerationSettings; -import com.sap.sailing.domain.common.PolarSheetsData; -import com.sap.sailing.domain.common.PolarSheetsHistogramData; -import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.TimePoint; -import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; -import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; -import com.sap.sailing.domain.tracking.TrackedRace; - -/** - * Allows extracting data about average speed for each angle creating the foundation of polar-sheet generation. Tasks - * are assigned to the executor per race & per competitor and the results are filled on the go to allow progress - * indication in the polar-sheet itself. - * - * @author D054528 Frederik Petersen - * - */ -public class PolarSheetGenerationWorker implements Future{ - - private final Set workers; - - private final List> polarData; - - private final Executor executor; - - private WindSteppingWithMaxDistance stepping; - - private PolarSheetGenerationSettings settings; - - /** - * Will prepare the {@link PerRaceAndCompetitorPolarSheetGenerationWorker}s per race & per competitor. This includes - * determining start and end time. - * - * @param trackedRaces - * from which the data is to be collected - * @param settings - * @param executor - * executes the tasks upon {@link #startPolarSheetGeneration()} - */ - public PolarSheetGenerationWorker(Set trackedRaces, PolarSheetGenerationSettings settings, - Executor executor) { - this.settings = settings; - stepping = settings.getWindStepping();; - polarData = initializePolarDataContainer(); - this.executor = executor; - workers = new HashSet(); - for (TrackedRace race : trackedRaces) { - TimePoint startTime = race.getStartOfRace(); - TimePoint endTime = race.getEndOfRace(); - if (endTime == null) { - endTime = race.getTimePointOfNewestEvent(); - } - RaceDefinition raceDefinition = race.getRace(); - Iterable competitors = raceDefinition.getCompetitors(); - - for (Competitor competitor : competitors) { - PerRaceAndCompetitorPolarSheetGenerationWorker task = new PerRaceAndCompetitorPolarSheetGenerationWorker( - race, this, startTime, endTime, competitor, settings); - workers.add(task); - } - - } - } - - private List> initializePolarDataContainer() { - List> container = new ArrayList>(); - for (int i = 0; i < 360; i++) { - container.add(new ArrayList()); - } - return container; - } - - /** - * Starts the {@link PerRaceAndCompetitorPolarSheetGenerationWorker}s - */ - public void startPolarSheetGeneration() { - for (Runnable task : workers) { - executor.execute(task); - } - } - - /** - * To be called from {@link PerRaceAndCompetitorPolarSheetGenerationWorker} for adding a datapoint to the list of - * results - * - * @param roundedAngle - * boat's angle to the wind - * @param boatSpeed - * boat's speed in knots - * @param windSpeed - * wind's speed in knots - */ - protected void addPolarData(PolarFix polarFix) { - long roundedAngle = Math.round(polarFix.getAngleToWind()); - Speed boatSpeed = polarFix.getBoatSpeed(); - Speed windSpeed = polarFix.getWindSpeed(); - int angle = (int) roundedAngle; - if (angle < 0) { - angle = (360 + angle); - } - BoatAndWindSpeedWithOriginInfo speeds = new BoatAndWindSpeedWithOriginInfoImpl(boatSpeed, windSpeed, - polarFix.getGaugeIdString(), polarFix.getDayString()); - polarData.get(angle).add(speeds); - } - - /** - * - * @return results already averaged per angle. Also datacount overall and per angle. And a completion flag to show - * if the complete polar sheet has been generated or if the current results are only an intermediate - * product. - */ - public PolarSheetsData getPolarData() { - - // Do this before working through data, since it's possible that remaining data is processed by the workers - // during the process of filling the result data. - boolean complete = allWorkersDone(); - - PolarSheetHistogramBuilder histogramBuilder = new PolarSheetHistogramBuilder(settings); - - int levelCount = stepping.getNumberOfLevels(); - Number[][] averagedPolarDataByWindSpeed = new Number[levelCount][360]; - Integer[] dataCountPerAngle = new Integer[360]; - int dataCount = 0; - Map dataCountPerAngleForWindspeed = new HashMap(); - Map> histogramDataMap = new HashMap>(); - Map totalDataCountPerWindSpeed = new HashMap(); - for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) { - dataCountPerAngleForWindspeed.put(levelIndex, new Integer[360]); - histogramDataMap.put(levelIndex, new HashMap()); - totalDataCountPerWindSpeed.put(levelIndex, 0); - } - for (int angleIndex = 0; angleIndex < 360; angleIndex++) { - // Avoid Concurrent modification of lists, so get current state as an array; lock would slow things down - BoatAndWindSpeedWithOriginInfo[] values = polarData.get(angleIndex).toArray( - new BoatAndWindSpeedWithOriginInfo[polarData.get(angleIndex).size()]); - dataCount = dataCount + values.length; - dataCountPerAngle[angleIndex] = values.length; - double[] sumsPerWindSpeed = new double[levelCount]; - int[] dataCountPerWindSpeed = new int[levelCount]; - Map> dataSetForWindLevel = new HashMap>(); - gatherPolarData(values, sumsPerWindSpeed, dataCountPerWindSpeed, dataSetForWindLevel); - - calculateStatisticalMeasuresAndHistogramData(histogramBuilder, levelCount, averagedPolarDataByWindSpeed, - dataCountPerAngleForWindspeed, histogramDataMap, totalDataCountPerWindSpeed, angleIndex, - sumsPerWindSpeed, dataCountPerWindSpeed, dataSetForWindLevel); - - } - - for (int j = 0; j < levelCount; j++) { - for (PolarSheetsHistogramData histogramData : histogramDataMap.get(j).values()) { - Double polarSheetPointConfidenceMeasure = Math.min(1, histogramData.getCoefficiantOfVariation() - * (histogramData.getDataCount() / (double) totalDataCountPerWindSpeed.get(j)) * 80); - histogramData.setConfidenceMeasure(polarSheetPointConfidenceMeasure); - } - } - - PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, complete, dataCount, - dataCountPerAngleForWindspeed, stepping, histogramDataMap); - - return data; - } - - private void gatherPolarData(BoatAndWindSpeedWithOriginInfo[] values, double[] sumsPerWindSpeed, - int[] dataCountPerWindSpeed, Map> dataSetForWindLevel) { - for (BoatAndWindSpeedWithOriginInfo singleDataPoint : values) { - if (singleDataPoint != null) { - double windSpeed = singleDataPoint.getWindSpeed().getKnots(); - int level = stepping.getLevelIndexForValue(windSpeed); - if (level >= 0) { - double speed = singleDataPoint.getBoatSpeed().getKnots(); - String windGaugesIdString = singleDataPoint.getWindGaugesIdString(); - String dayString = singleDataPoint.getDayString(); - if (!dataSetForWindLevel.containsKey(level)) { - dataSetForWindLevel.put(level, new ArrayList()); - } - dataSetForWindLevel.get(level).add( - new DataPointWithOriginInfo(speed, windGaugesIdString, dayString)); - sumsPerWindSpeed[level] = sumsPerWindSpeed[level] + speed; - dataCountPerWindSpeed[level]++; - } - } - } - } - - private void calculateStatisticalMeasuresAndHistogramData(PolarSheetHistogramBuilder histogramBuilder, - int levelCount, Number[][] averagedPolarDataByWindSpeed, - Map dataCountPerAngleForWindspeed, - Map> histogramDataMap, - Map totalDataCountPerWindSpeed, int angleIndex, double[] sumsPerWindSpeed, - int[] dataCountPerWindSpeed, Map> dataSetForWindLevel) { - for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) { - if (settings.shouldRemoveOutliers() && dataSetForWindLevel.get(levelIndex) != null) { - List withoutOutliers = new ArrayList(); - Collections.sort(dataSetForWindLevel.get(levelIndex)); - if (dataCountPerWindSpeed[levelIndex] > /* Minimum data count for outlier detection */10) { - performOutlierExclusion(sumsPerWindSpeed, dataCountPerWindSpeed, dataSetForWindLevel, - levelIndex, withoutOutliers); - } - - } - - if (dataCountPerWindSpeed[levelIndex] < settings.getMinimumDataCountPerAngle()) { - dataSetForWindLevel.put(levelIndex, new ArrayList()); - sumsPerWindSpeed[levelIndex] = 0; - dataCountPerWindSpeed[levelIndex] = 0; - } - totalDataCountPerWindSpeed.put(levelIndex, totalDataCountPerWindSpeed.get(levelIndex) - + dataCountPerWindSpeed[levelIndex]); - Double average = sumsPerWindSpeed[levelIndex] / dataCountPerWindSpeed[levelIndex]; - if (average.isNaN()) { - average = new Double(0); - } - averagedPolarDataByWindSpeed[levelIndex][angleIndex] = average; - dataCountPerAngleForWindspeed.get(levelIndex)[angleIndex] = dataCountPerWindSpeed[levelIndex]; - double coefficiantOfVariation = 0; - if (dataCountPerWindSpeed[levelIndex] > 0 && average > 0) { - Double variance = sumsPerWindSpeed[levelIndex] / dataCountPerWindSpeed[levelIndex]; - Double standardDeviation = Math.sqrt(variance); - coefficiantOfVariation = standardDeviation / average; - } - List rawData = dataSetForWindLevel.get(levelIndex); - if (rawData == null || rawData.size() <= 0) { - continue; - } - PolarSheetsHistogramData histogramData = histogramBuilder.build(rawData, angleIndex, - coefficiantOfVariation); - histogramDataMap.get(levelIndex).put(angleIndex, histogramData); - - } - } - - private void performOutlierExclusion(double[] sumsPerWindSpeed, int[] dataCountPerWindSpeed, - Map> dataSetForWindLevel, int levelIndex, - List withoutOutliers) { - for (int dataIndex = 0; dataIndex < dataSetForWindLevel.get(levelIndex).size(); dataIndex++) { - DataPointWithOriginInfo dataPoint = dataSetForWindLevel.get(levelIndex).get(dataIndex); - double pct = getNeighboorhoodSizePercentage(dataCountPerWindSpeed, dataSetForWindLevel, - levelIndex, dataIndex, dataPoint, settings); - if (pct >= settings.getOutlierMinimumNeighborhoodPct()) { - withoutOutliers.add(dataPoint); - } else { - sumsPerWindSpeed[levelIndex] = sumsPerWindSpeed[levelIndex] - dataPoint.getRawData(); - dataCountPerWindSpeed[levelIndex]--; - } - } - dataSetForWindLevel.put(levelIndex, withoutOutliers); - } - - /** - * This method calculates the pct of the points neighborhoods based on a distance (in the settings) compared - * to the complete dataset. - */ - public static double getNeighboorhoodSizePercentage(int[] dataCountPerWindSpeed, - Map> dataSetForWindLevel, int levelIndex, int dataIndex, - DataPointWithOriginInfo dataPoint, PolarSheetGenerationSettings settings) { - int neighborCount = 0; - double pct = .0; - - int index = dataIndex; - // Left side - boolean done = false; - while (!done) { - index--; - if (index < 0) { - done = true; - } else { - Double dataPointToCheckForNeighborStatus = dataSetForWindLevel.get(levelIndex).get( - index).getRawData(); - if (dataPoint.getRawData() - dataPointToCheckForNeighborStatus <= settings - .getOutlierDetectionNeighborhoodRadius()) { - neighborCount++; - } else { - done = true; - } - pct = (double) neighborCount / (double) dataCountPerWindSpeed[levelIndex]; - if (pct >= settings.getOutlierMinimumNeighborhoodPct()) { - done = true; - } - } - } - index = dataIndex; - // Right side - done = false; - while (!done) { - index++; - if (index >= dataCountPerWindSpeed[levelIndex]) { - done = true; - } else { - Double dataPointToCheckForNeighborStatus = dataSetForWindLevel.get(levelIndex).get( - index).getRawData(); - if (dataPointToCheckForNeighborStatus - dataPoint.getRawData() <= settings - .getOutlierDetectionNeighborhoodRadius()) { - neighborCount++; - } else { - done = true; - } - pct = (double) neighborCount / (double) dataCountPerWindSpeed[levelIndex]; - if (pct >= settings.getOutlierMinimumNeighborhoodPct()) { - done = true; - } - } - } - pct = (double) neighborCount / (double) dataCountPerWindSpeed[levelIndex]; - return pct; - } - - private boolean allWorkersDone() { - boolean complete = true; - for (PerRaceAndCompetitorPolarSheetGenerationWorker task : workers) { - if (!task.isDone()) { - complete = false; - break; - } - } - return complete; - } - - /** - * Can be used to present more detailed data. - * - * @return The complete set of datapoints that have been added. Can be quite big, depending on the amount of races - * and competitors. - */ - public List> getCompleteData() { - return polarData; - } - - public WindSteppingWithMaxDistance getStepping() { - return stepping; - } - - @Override - public boolean cancel(boolean mayInterruptIfRunning) { - return false; - } - - @Override - public boolean isCancelled() { - return false; - } - - @Override - public boolean isDone() { - return allWorkersDone(); - } - - @Override - public PolarSheetsData get() throws InterruptedException, ExecutionException { - while (!isDone()) { - Thread.sleep(100); - } - return getPolarData(); - } - - @Override - public PolarSheetsData get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, - TimeoutException { - long timeRun = 0; - long timeoutInMillis = unit.toMillis(timeout); - while (!isDone() && timeRun < timeoutInMillis) { - Thread.sleep(100); - timeRun = timeRun + 100; - } - if (timeRun >= timeoutInMillis) { - throw new TimeoutException(); - } - return getPolarData(); - } - -} +package com.sap.sailing.polars.generation; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.domain.common.PolarSheetsHistogramData; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; +import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; +import com.sap.sailing.polars.data.BoatAndWindSpeedWithOriginInfo; +import com.sap.sailing.polars.data.DataPointWithOriginInfo; +import com.sap.sailing.polars.data.PolarFix; +import com.sap.sailing.polars.data.impl.BoatAndWindSpeedWithOriginInfoImpl; +import com.sap.sailing.polars.data.impl.DataPointWithOriginInfoImpl; + +public class PolarSheetGenerator { + + private final List> polarData; + + private WindSteppingWithMaxDistance stepping; + + private PolarSheetGenerationSettings settings; + + private final Set fixes; + + public PolarSheetGenerator(Set fixes, PolarSheetGenerationSettings settings) { + this.settings = settings; + this.fixes = fixes; + stepping = settings.getWindStepping();; + polarData = initializePolarDataContainer(); + } + + private List> initializePolarDataContainer() { + List> container = new ArrayList>(); + for (int i = 0; i < 360; i++) { + container.add(new ArrayList()); + } + return container; + } + + + private void addPolarFix(PolarFix polarFix) { + long roundedAngle = Math.round(polarFix.getAngleToWind()); + Speed boatSpeed = polarFix.getBoatSpeed(); + Speed windSpeed = polarFix.getWindSpeed(); + int angle = (int) roundedAngle; + BoatAndWindSpeedWithOriginInfo speeds = new BoatAndWindSpeedWithOriginInfoImpl(boatSpeed, windSpeed, + polarFix.getGaugeIdString(), polarFix.getDayString()); + polarData.get(angle).add(speeds); + } + + /** + * + * @return results already averaged per angle. Also datacount overall and per angle. And a completion flag to show + * if the complete polar sheet has been generated or if the current results are only an intermediate + * product. + */ + public PolarSheetsData generate() { + + for (PolarFix fix : fixes) { + addPolarFix(fix); + } + + PolarSheetHistogramBuilder histogramBuilder = new PolarSheetHistogramBuilder(settings); + + int levelCount = stepping.getNumberOfLevels(); + Number[][] averagedPolarDataByWindSpeed = new Number[levelCount][360]; + Integer[] dataCountPerAngle = new Integer[360]; + int dataCount = 0; + Map dataCountPerAngleForWindspeed = new HashMap(); + Map> histogramDataMap = new HashMap>(); + Map totalDataCountPerWindSpeed = new HashMap(); + for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) { + dataCountPerAngleForWindspeed.put(levelIndex, new Integer[360]); + histogramDataMap.put(levelIndex, new HashMap()); + totalDataCountPerWindSpeed.put(levelIndex, 0); + } + for (int angleIndex = 0; angleIndex < 360; angleIndex++) { + // Avoid Concurrent modification of lists, so get current state as an array; lock would slow things down + BoatAndWindSpeedWithOriginInfo[] values = polarData.get(angleIndex).toArray( + new BoatAndWindSpeedWithOriginInfo[polarData.get(angleIndex).size()]); + dataCount = dataCount + values.length; + dataCountPerAngle[angleIndex] = values.length; + double[] sumsPerWindSpeed = new double[levelCount]; + int[] dataCountPerWindSpeed = new int[levelCount]; + Map> dataSetForWindLevel = new HashMap>(); + gatherPolarData(values, sumsPerWindSpeed, dataCountPerWindSpeed, dataSetForWindLevel); + + calculateStatisticalMeasuresAndHistogramData(histogramBuilder, levelCount, averagedPolarDataByWindSpeed, + dataCountPerAngleForWindspeed, histogramDataMap, totalDataCountPerWindSpeed, angleIndex, + sumsPerWindSpeed, dataCountPerWindSpeed, dataSetForWindLevel); + + } + + for (int j = 0; j < levelCount; j++) { + for (PolarSheetsHistogramData histogramData : histogramDataMap.get(j).values()) { + Double polarSheetPointConfidenceMeasure = Math.min(1, histogramData.getCoefficiantOfVariation() + * (histogramData.getDataCount() / (double) totalDataCountPerWindSpeed.get(j)) * 80); + histogramData.setConfidenceMeasure(polarSheetPointConfidenceMeasure); + } + } + + PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, dataCount, + dataCountPerAngleForWindspeed, stepping, histogramDataMap); + + return data; + } + + private void gatherPolarData(BoatAndWindSpeedWithOriginInfo[] values, double[] sumsPerWindSpeed, + int[] dataCountPerWindSpeed, Map> dataSetForWindLevel) { + for (BoatAndWindSpeedWithOriginInfo singleDataPoint : values) { + if (singleDataPoint != null) { + double windSpeed = singleDataPoint.getWindSpeed().getKnots(); + int level = stepping.getLevelIndexForValue(windSpeed); + if (level >= 0) { + double speed = singleDataPoint.getBoatSpeed().getKnots(); + String windGaugesIdString = singleDataPoint.getWindGaugesIdString(); + String dayString = singleDataPoint.getDayString(); + if (!dataSetForWindLevel.containsKey(level)) { + dataSetForWindLevel.put(level, new ArrayList()); + } + dataSetForWindLevel.get(level).add( + new DataPointWithOriginInfoImpl(speed, windGaugesIdString, dayString)); + sumsPerWindSpeed[level] = sumsPerWindSpeed[level] + speed; + dataCountPerWindSpeed[level]++; + } + } + } + } + + private void calculateStatisticalMeasuresAndHistogramData(PolarSheetHistogramBuilder histogramBuilder, + int levelCount, Number[][] averagedPolarDataByWindSpeed, + Map dataCountPerAngleForWindspeed, + Map> histogramDataMap, + Map totalDataCountPerWindSpeed, int angleIndex, double[] sumsPerWindSpeed, + int[] dataCountPerWindSpeed, Map> dataSetForWindLevel) { + for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) { + if (settings.shouldRemoveOutliers() && dataSetForWindLevel.get(levelIndex) != null) { + List withoutOutliers = new ArrayList(); + Collections.sort(dataSetForWindLevel.get(levelIndex)); + if (dataCountPerWindSpeed[levelIndex] > /* Minimum data count for outlier detection */10) { + performOutlierExclusion(sumsPerWindSpeed, dataCountPerWindSpeed, dataSetForWindLevel, + levelIndex, withoutOutliers); + } + + } + + if (dataCountPerWindSpeed[levelIndex] < settings.getMinimumDataCountPerAngle()) { + dataSetForWindLevel.put(levelIndex, new ArrayList()); + sumsPerWindSpeed[levelIndex] = 0; + dataCountPerWindSpeed[levelIndex] = 0; + } + totalDataCountPerWindSpeed.put(levelIndex, totalDataCountPerWindSpeed.get(levelIndex) + + dataCountPerWindSpeed[levelIndex]); + Double average = sumsPerWindSpeed[levelIndex] / dataCountPerWindSpeed[levelIndex]; + if (average.isNaN()) { + average = new Double(0); + } + averagedPolarDataByWindSpeed[levelIndex][angleIndex] = average; + dataCountPerAngleForWindspeed.get(levelIndex)[angleIndex] = dataCountPerWindSpeed[levelIndex]; + double coefficiantOfVariation = 0; + if (dataCountPerWindSpeed[levelIndex] > 0 && average > 0) { + Double variance = sumsPerWindSpeed[levelIndex] / dataCountPerWindSpeed[levelIndex]; + Double standardDeviation = Math.sqrt(variance); + coefficiantOfVariation = standardDeviation / average; + } + List rawData = dataSetForWindLevel.get(levelIndex); + if (rawData == null || rawData.size() <= 0) { + continue; + } + PolarSheetsHistogramData histogramData = histogramBuilder.build(rawData, angleIndex, + coefficiantOfVariation); + histogramDataMap.get(levelIndex).put(angleIndex, histogramData); + + } + } + + private void performOutlierExclusion(double[] sumsPerWindSpeed, int[] dataCountPerWindSpeed, + Map> dataSetForWindLevel, int levelIndex, + List withoutOutliers) { + for (int dataIndex = 0; dataIndex < dataSetForWindLevel.get(levelIndex).size(); dataIndex++) { + DataPointWithOriginInfo dataPoint = dataSetForWindLevel.get(levelIndex).get(dataIndex); + double pct = getNeighboorhoodSizePercentage(dataCountPerWindSpeed, dataSetForWindLevel, + levelIndex, dataIndex, dataPoint, settings); + if (pct >= settings.getOutlierMinimumNeighborhoodPct()) { + withoutOutliers.add(dataPoint); + } else { + sumsPerWindSpeed[levelIndex] = sumsPerWindSpeed[levelIndex] - dataPoint.getRawData(); + dataCountPerWindSpeed[levelIndex]--; + } + } + dataSetForWindLevel.put(levelIndex, withoutOutliers); + } + + /** + * This method calculates the pct of the points neighborhoods based on a distance (in the settings) compared + * to the complete dataset. + */ + public static double getNeighboorhoodSizePercentage(int[] dataCountPerWindSpeed, + Map> valuesInMap, int levelIndex, int dataIndex, + DataPointWithOriginInfo dataPoint, PolarSheetGenerationSettings settings) { + int neighborCount = 0; + double pct = .0; + + int index = dataIndex; + // Left side + boolean done = false; + while (!done) { + index--; + if (index < 0) { + done = true; + } else { + Double dataPointToCheckForNeighborStatus = valuesInMap.get(levelIndex).get( + index).getRawData(); + if (dataPoint.getRawData() - dataPointToCheckForNeighborStatus <= settings + .getOutlierDetectionNeighborhoodRadius()) { + neighborCount++; + } else { + done = true; + } + pct = (double) neighborCount / (double) dataCountPerWindSpeed[levelIndex]; + if (pct >= settings.getOutlierMinimumNeighborhoodPct()) { + done = true; + } + } + } + index = dataIndex; + // Right side + done = false; + while (!done) { + index++; + if (index >= dataCountPerWindSpeed[levelIndex]) { + done = true; + } else { + Double dataPointToCheckForNeighborStatus = valuesInMap.get(levelIndex).get( + index).getRawData(); + if (dataPointToCheckForNeighborStatus - dataPoint.getRawData() <= settings + .getOutlierDetectionNeighborhoodRadius()) { + neighborCount++; + } else { + done = true; + } + pct = (double) neighborCount / (double) dataCountPerWindSpeed[levelIndex]; + if (pct >= settings.getOutlierMinimumNeighborhoodPct()) { + done = true; + } + } + } + pct = (double) neighborCount / (double) dataCountPerWindSpeed[levelIndex]; + return pct; + } + + + /** + * Can be used to present more detailed data. + * + * @return The complete set of datapoints that have been added. Can be quite big, depending on the amount of races + * and competitors. + */ + public List> getCompleteData() { + return polarData; + } + + public WindSteppingWithMaxDistance getStepping() { + return stepping; + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PolarSheetHistogramBuilder.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetHistogramBuilder.java similarity index 88% rename from java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PolarSheetHistogramBuilder.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetHistogramBuilder.java index ca980475494..fdbbe434675 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polarsheets/PolarSheetHistogramBuilder.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetHistogramBuilder.java @@ -1,4 +1,4 @@ -package com.sap.sailing.domain.polarsheets; +package com.sap.sailing.polars.generation; import java.util.Collections; import java.util.HashMap; @@ -8,6 +8,7 @@ import java.util.Map; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsHistogramData; import com.sap.sailing.domain.common.impl.PolarSheetsHistogramDataImpl; +import com.sap.sailing.polars.data.DataPointWithOriginInfo; public class PolarSheetHistogramBuilder { @@ -17,10 +18,10 @@ public class PolarSheetHistogramBuilder { this.settings = settings; } - public PolarSheetsHistogramData build(List dataPointsWithOriginInfo, int angleIndex, + public PolarSheetsHistogramData build(List rawData, int angleIndex, double coefficiantOfVariation) { - DataPointWithOriginInfo minPoint = Collections.min(dataPointsWithOriginInfo); - DataPointWithOriginInfo maxPoint = Collections.max(dataPointsWithOriginInfo); + DataPointWithOriginInfo minPoint = Collections.min(rawData); + DataPointWithOriginInfo maxPoint = Collections.max(rawData); Double min = minPoint.getRawData(); Double max = maxPoint.getRawData(); @@ -39,7 +40,7 @@ public class PolarSheetHistogramBuilder { for (int i = 0; i < yValues.length; i++) { yValues[i] = 0; } - for (DataPointWithOriginInfo dataPoint : dataPointsWithOriginInfo) { + for (DataPointWithOriginInfo dataPoint : rawData) { double notRoundedYet = (dataPoint.getRawData() - min) / range; int u = (int) notRoundedYet; if (u == numberOfColumns) { @@ -61,7 +62,7 @@ public class PolarSheetHistogramBuilder { PolarSheetsHistogramData histogramData = new PolarSheetsHistogramDataImpl(angleIndex, xValues, yValues, - yValuesByGaugeIds, yValuesByDay, yValuesByDayAndGaugeId, dataPointsWithOriginInfo.size(), coefficiantOfVariation); + yValuesByGaugeIds, yValuesByDay, yValuesByDayAndGaugeId, rawData.size(), coefficiantOfVariation); return histogramData; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mocked/MockedPolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java similarity index 56% rename from java/com.sap.sailing.polars/src/com/sap/sailing/polars/mocked/MockedPolarDataService.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 17563f6502f..d5a4c344b55 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mocked/MockedPolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -1,12 +1,22 @@ -package com.sap.sailing.polars.mocked; +package com.sap.sailing.polars.impl; + +import java.util.Set; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.aggregation.PolarFixAggregator; +import com.sap.sailing.polars.data.PolarFix; +import com.sap.sailing.polars.generation.PolarSheetGenerator; -public class MockedPolarDataService implements PolarDataService { +public class PolarDataServiceImpl implements PolarDataService { @Override public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { @@ -44,4 +54,14 @@ public class MockedPolarDataService implements PolarDataService { return null; } + @Override + public PolarSheetsData generatePolarSheet(Set trackedRaces, PolarSheetGenerationSettings settings, + Executor executor) throws InterruptedException, ExecutionException { + PolarFixAggregator aggregator = new PolarFixAggregator(trackedRaces, settings, executor); + aggregator.startPolarFixAggregation(); + Set fixes = aggregator.get(); + PolarSheetGenerator generator = new PolarSheetGenerator(fixes, settings); + return generator.generate(); + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCache.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCache.java deleted file mode 100644 index b5b333bf118..00000000000 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCache.java +++ /dev/null @@ -1,16 +0,0 @@ -package com.sap.sailing.polars.impl; - -import java.util.List; - -import com.sap.sailing.domain.base.BoatClass; -import com.sap.sailing.domain.polarsheets.PolarFix; -import com.sap.sailing.util.SmartFutureCache; - -public class PolarFixCache extends - SmartFutureCache, com.sap.sailing.util.SmartFutureCache.EmptyUpdateInterval> { - - public PolarFixCache() { - super(new PolarFixCacheUpdater(), "polarFixCache"); - } - -} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCacheUpdater.java deleted file mode 100644 index ad652613122..00000000000 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarFixCacheUpdater.java +++ /dev/null @@ -1,18 +0,0 @@ -package com.sap.sailing.polars.impl; - -import java.util.List; - -import com.sap.sailing.domain.base.BoatClass; -import com.sap.sailing.domain.polarsheets.PolarFix; -import com.sap.sailing.util.SmartFutureCache.AbstractCacheUpdater; -import com.sap.sailing.util.SmartFutureCache.EmptyUpdateInterval; - -public class PolarFixCacheUpdater extends AbstractCacheUpdater, EmptyUpdateInterval> { - - @Override - public List computeCacheUpdate(BoatClass key, EmptyUpdateInterval updateInterval) throws Exception { - // TODO Auto-generated method stub - return null; - } - -} From e25aeb5daff65b28cafc166ef2914caa410b9612 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 18 Dec 2013 12:00:24 +0100 Subject: [PATCH 04/72] Implemented the polar fix cache updater --- .../gwt/ui/server/SailingServiceImpl.java | 5 +- .../META-INF/MANIFEST.MF | 5 +- .../sap/sailing/polars/PolarDataService.java | 2 + .../sailing/polars/caching/PolarFixCache.java | 9 +-- .../caching/PolarFixCacheRaceInterval.java | 18 +++--- .../polars/caching/PolarFixCacheUpdater.java | 60 +++++++++++++++---- .../factory/PolarDataServiceFactory.java | 8 ++- .../impl/PolarDataServiceImplFactory.java | 6 +- .../polars/impl/PolarDataServiceImpl.java | 16 +++++ .../META-INF/MANIFEST.MF | 3 +- .../META-INF/MANIFEST.MF | 4 +- .../sailing/server/RacingEventService.java | 7 ++- .../server/impl/RacingEventServiceImpl.java | 44 ++++++++++++++ 13 files changed, 145 insertions(+), 42 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 014d9f305d6..c50ad758f4a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -257,7 +257,6 @@ import com.sap.sailing.gwt.ui.shared.WindDTO; import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO; import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO; import com.sap.sailing.polars.PolarDataService; -import com.sap.sailing.polars.factory.PolarDataServiceFactory; import com.sap.sailing.resultimport.ResultUrlProvider; import com.sap.sailing.resultimport.ResultUrlRegistry; import com.sap.sailing.server.RacingEventService; @@ -369,9 +368,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private final SwissTimingReplayService swissTimingReplayService; - private PolarDataService polarDataService; private final BundleContext context; - public SailingServiceImpl() { context = Activator.getDefault(); racingEventServiceTracker = createAndOpenRacingEventServiceTracker(context); @@ -414,7 +411,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S /* maximumPoolSize */ THREAD_POOL_SIZE, /* keepAliveTime */ 60, TimeUnit.SECONDS, /* workQueue */ new LinkedBlockingQueue()); - polarDataService = PolarDataServiceFactory.createStandardPolarDataService(); } protected SwissTimingReplayService getSwissTimingReplayService(BundleContext context) { @@ -3128,6 +3124,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (name == null || name.isEmpty()) { name = getCommonBoatClass(trackedRaces); } + PolarDataService polarDataService = service.getPolarDataService(); PolarSheetsData result = polarDataService.generatePolarSheet(trackedRaces, settings, executor); return new PolarSheetGenerationResponseImpl(id, name, result); } diff --git a/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF index d5eb223095b..c7cefcd62b9 100755 --- a/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF @@ -17,8 +17,9 @@ Require-Bundle: com.sap.sailing.domain, org.json.simple;bundle-version="1.1.0", com.sap.sailing.domain.swisstimingadapter, org.objenesis;bundle-version="1.3.0", - org.junit4;bundle-version="4.8.2", + org.junit4;bundle-version="4.8.2", org.hamcrest;bundle-version="1.1.0", - org.mockito.mockito-core;bundle-version="1.9.5" + org.mockito.mockito-core;bundle-version="1.9.5", + com.sap.sailing.polars;bundle-version="1.0.0" Import-Package: com.sap.sailing.domain.common.impl diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index e93dc0f9924..2bb934d0589 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -95,4 +95,6 @@ public interface PolarDataService { PolarSheetsData generatePolarSheet(Set trackedRaces, PolarSheetGenerationSettings settings, Executor executor) throws InterruptedException, ExecutionException; + void newRaceFinishedTracking(TrackedRace trackedRace); + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java index d06c1fdce3f..f066532dce5 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java @@ -2,17 +2,18 @@ package com.sap.sailing.polars.caching; import java.util.List; import java.util.Map; +import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.polars.data.impl.PolarFixImpl; +import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.util.SmartFutureCache; public class PolarFixCache extends - SmartFutureCache>, PolarFixCacheRaceInterval> { + SmartFutureCache>, PolarFixCacheRaceInterval> { - public PolarFixCache() { - super(new PolarFixCacheUpdater(), "polarFixCache"); + public PolarFixCache(Executor executor) { + super(new PolarFixCacheUpdater(executor), "polarFixCache"); } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java index 8a14fad2114..f26067ecda7 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java @@ -3,30 +3,26 @@ package com.sap.sailing.polars.caching; import java.util.HashSet; import java.util.Set; -import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.util.SmartFutureCache.UpdateInterval; public class PolarFixCacheRaceInterval implements UpdateInterval { - private final Set races; + private final Set races; - public PolarFixCacheRaceInterval(Set races) { + public PolarFixCacheRaceInterval(Set races) { this.races = races; } - public Set getRaces() { + public Set getRaces() { return races; } @Override public PolarFixCacheRaceInterval join(PolarFixCacheRaceInterval otherUpdateInterval) { - Set joined = new HashSet(); - for (RegattaAndRaceIdentifier race : races) { - joined.add(race); - } - for (RegattaAndRaceIdentifier race : otherUpdateInterval.getRaces()) { - joined.add(race); - } + Set joined = new HashSet(); + joined.addAll(races); + joined.addAll(otherUpdateInterval.getRaces()); return new PolarFixCacheRaceInterval(joined); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java index 8ec0fdcdf3d..945802dcbd5 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java @@ -1,29 +1,65 @@ package com.sap.sailing.polars.caching; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Map.Entry; +import java.util.Set; +import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; -import com.sap.sailing.polars.data.impl.PolarFixImpl; +import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.polars.aggregation.PolarFixAggregator; +import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.util.SmartFutureCache.CacheUpdater; public class PolarFixCacheUpdater implements - CacheUpdater>, PolarFixCacheRaceInterval> { + CacheUpdater>, PolarFixCacheRaceInterval> { - @Override - public Map> computeCacheUpdate(BoatClass key, - PolarFixCacheRaceInterval updateInterval) throws Exception { - // TODO Auto-generated method stub - return null; + private final Executor executor; + + public PolarFixCacheUpdater(Executor executor) { + this.executor = executor; } @Override - public Map> provideNewCacheValue(BoatClass key, - Map> oldCacheValue, - Map> computedCacheUpdate, PolarFixCacheRaceInterval updateInterval) { - // TODO Auto-generated method stub - return null; + public Map> computeCacheUpdate(BoatClass key, + PolarFixCacheRaceInterval updateInterval) throws Exception { + Set races = updateInterval.getRaces(); + Map aggregators = new HashMap(); + for (TrackedRace trackedRace : races) { + Set trackedRaces = new HashSet(); + trackedRaces.add(trackedRace); + PolarFixAggregator aggregator = new PolarFixAggregator(trackedRaces, + PolarSheetGenerationSettingsImpl.createStandardPolarSettings(), executor); + aggregator.startPolarFixAggregation(); + aggregators.put(trackedRace.getRaceIdentifier(), aggregator); + } + Map> resultMap = new HashMap>(); + for (Entry entry : aggregators.entrySet()) { + PolarFixAggregator aggregator = entry.getValue(); + Set result = aggregator.get(); + List resultList = new ArrayList(result); + resultMap.put(entry.getKey(), resultList); + } + return resultMap; + + } + + @Override + public Map> provideNewCacheValue(BoatClass key, + Map> oldCacheValue, + Map> computedCacheUpdate, PolarFixCacheRaceInterval updateInterval) { + Map> newCacheValue = new HashMap>(); + newCacheValue.putAll(oldCacheValue); + for (Entry> newEntry : computedCacheUpdate.entrySet()) { + newCacheValue.put(newEntry.getKey(), newEntry.getValue()); + } + return newCacheValue; } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/PolarDataServiceFactory.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/PolarDataServiceFactory.java index 3247b032cc7..0b03c20a553 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/PolarDataServiceFactory.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/PolarDataServiceFactory.java @@ -1,16 +1,18 @@ package com.sap.sailing.polars.factory; +import java.util.concurrent.Executor; + import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.factory.impl.PolarDataServiceImplFactory; public abstract class PolarDataServiceFactory { - public static PolarDataService createStandardPolarDataService() { + public static PolarDataService createStandardPolarDataService(Executor executor) { PolarDataServiceFactory factory = new PolarDataServiceImplFactory(); - return factory.createPolarDataService(); + return factory.createPolarDataService(executor); } - protected abstract PolarDataService createPolarDataService(); + protected abstract PolarDataService createPolarDataService(Executor executor); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/impl/PolarDataServiceImplFactory.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/impl/PolarDataServiceImplFactory.java index 2ea48ceb772..37af01d8343 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/impl/PolarDataServiceImplFactory.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/factory/impl/PolarDataServiceImplFactory.java @@ -1,5 +1,7 @@ package com.sap.sailing.polars.factory.impl; +import java.util.concurrent.Executor; + import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.factory.PolarDataServiceFactory; import com.sap.sailing.polars.impl.PolarDataServiceImpl; @@ -7,8 +9,8 @@ import com.sap.sailing.polars.impl.PolarDataServiceImpl; public class PolarDataServiceImplFactory extends PolarDataServiceFactory { @Override - public PolarDataService createPolarDataService() { - return new PolarDataServiceImpl(); + public PolarDataService createPolarDataService(Executor executor) { + return new PolarDataServiceImpl(executor); } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index d5a4c344b55..04bf28bfc76 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -1,5 +1,6 @@ package com.sap.sailing.polars.impl; +import java.util.HashSet; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -13,11 +14,19 @@ import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.aggregation.PolarFixAggregator; +import com.sap.sailing.polars.caching.PolarFixCache; +import com.sap.sailing.polars.caching.PolarFixCacheRaceInterval; import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.polars.generation.PolarSheetGenerator; public class PolarDataServiceImpl implements PolarDataService { + private final PolarFixCache polarFixCache; + + public PolarDataServiceImpl(Executor executor) { + this.polarFixCache = new PolarFixCache(executor); + } + @Override public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { // TODO Auto-generated method stub @@ -64,4 +73,11 @@ public class PolarDataServiceImpl implements PolarDataService { return generator.generate(); } + @Override + public void newRaceFinishedTracking(TrackedRace trackedRace) { + HashSet set = new HashSet(); + set.add(trackedRace); + polarFixCache.triggerUpdate(trackedRace.getRace().getBoatClass(), new PolarFixCacheRaceInterval(set)); + } + } diff --git a/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF index 1d08244f5d9..1213689ffcf 100644 --- a/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF @@ -20,4 +20,5 @@ Require-Bundle: com.sap.sailing.server, com.sap.sailing.domain.swisstimingadapter, org.json.simple;bundle-version="1.1.0", org.junit4;bundle-version="4.8.2", - org.hamcrest;bundle-version="1.1.0" + org.hamcrest;bundle-version="1.1.0", + com.sap.sailing.polars;bundle-version="1.0.0" diff --git a/java/com.sap.sailing.server/META-INF/MANIFEST.MF b/java/com.sap.sailing.server/META-INF/MANIFEST.MF index b25247faa94..17d204a81ae 100755 --- a/java/com.sap.sailing.server/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server/META-INF/MANIFEST.MF @@ -6,7 +6,9 @@ Bundle-Version: 1.0.0.qualifier Bundle-Activator: com.sap.sailing.server.impl.Activator Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: com.sap.sailing.server.gateway.deserialization, +Import-Package: com.sap.sailing.polars, + com.sap.sailing.polars.factory, + com.sap.sailing.server.gateway.deserialization, com.sap.sailing.server.gateway.deserialization.masterdata.impl, com.sap.sailing.server.gateway.serialization.masterdata.impl, org.json.simple, diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java index 6547f1420cb..343ba6a7acc 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java @@ -58,6 +58,7 @@ import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.TrackedRegattaRegistry; import com.sap.sailing.domain.tracking.TrackerManager; import com.sap.sailing.domain.tracking.WindStore; +import com.sap.sailing.polars.PolarDataService; /** * An OSGi service that can be used to track boat races using a TracTrac connector that pushes @@ -426,7 +427,7 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch UUID id); CourseArea addCourseAreaWithoutReplication(UUID eventId, UUID courseAreaId, String courseAreaName); - + /** * Returns a mobile device's configuration. * @param identifier of the client (may include event) @@ -471,11 +472,13 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch /** * Gets the start time and pass identifier for the queried race. */ - Pair getStartTime(String leaderboardName, String raceColumnName, String fleetName); + Pair getStartTime(String leaderboardName, String raceColumnName, String fleetName); MongoObjectFactory getMongoObjectFactory(); DomainObjectFactory getDomainObjectFactory(); WindStore getWindStore(); + + PolarDataService getPolarDataService(); } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 4df52818506..32c18951af9 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -20,9 +20,12 @@ import java.util.Map.Entry; import java.util.Set; import java.util.UUID; import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.Executor; import java.util.concurrent.Executors; +import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ScheduledExecutorService; import java.util.concurrent.ScheduledFuture; +import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.logging.Logger; @@ -61,6 +64,7 @@ import com.sap.sailing.domain.common.RegattaName; import com.sap.sailing.domain.common.Renamable; import com.sap.sailing.domain.common.ScoringSchemeType; import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.TrackedRaceStatusEnum; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO; @@ -115,9 +119,12 @@ import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tracking.WindTracker; import com.sap.sailing.domain.tracking.WindTrackerFactory; +import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; import com.sap.sailing.expeditionconnector.ExpeditionWindTrackerFactory; import com.sap.sailing.operationaltransformation.Operation; +import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.factory.PolarDataServiceFactory; import com.sap.sailing.server.OperationExecutionListener; import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventServiceOperation; @@ -222,6 +229,8 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen private final WindStore windStore; + private final PolarDataService polarDataService; + /** * If this service runs in the context of an OSGi environment, the activator should {@link #setBundleContext set the bundle context} on this * object so that service lookups become possible. @@ -293,6 +302,13 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen operationExecutionListeners = new ConcurrentHashMap(); courseListeners = new ConcurrentHashMap(); persistentRegattasForRaceIDs = new ConcurrentHashMap(); + final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3); + // TODO find out how many executors we have on server side and how to manage them + Executor polarExecutor = new ThreadPoolExecutor(/* corePoolSize */THREAD_POOL_SIZE, + /* maximumPoolSize */THREAD_POOL_SIZE, + /* keepAliveTime */60, TimeUnit.SECONDS, + /* workQueue */new LinkedBlockingQueue()); + polarDataService = PolarDataServiceFactory.createStandardPolarDataService(polarExecutor); this.raceLogReplicator = new RaceLogReplicator(this); this.raceLogScoringReplicator = new RaceLogScoringReplicator(this); this.mediaDB = mediaDb; @@ -320,6 +336,11 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen loadStoredDeviceConfigurations(); } + @Override + public PolarDataService getPolarDataService() { + return polarDataService; + } + @Override public com.sap.sailing.domain.base.DomainFactory getBaseDomainFactory() { return baseDomainFactory; @@ -1390,11 +1411,34 @@ public class RacingEventServiceImpl implements RacingEventService, RegattaListen replicate(new TrackRegatta(regatta.getRegattaIdentifier())); regattaTrackingCache.put(regatta, result); ensureRegattaIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking(result); + addRaceTrackingFinishedListenerForPolarFixCache(result); } return result; } } + private void addRaceTrackingFinishedListenerForPolarFixCache(DynamicTrackedRegatta result) { + RaceListener raceListenerForPolarFixCacheUpdate = new RaceListener() { + @Override + public void raceRemoved(TrackedRace trackedRace) { + // TODO remove fixes from polar fix cache + } + + @Override + public void raceAdded(final TrackedRace trackedRace) { + trackedRace.addListener(new AbstractRaceChangeListener() { + @Override + public void statusChanged(TrackedRaceStatus newStatus) { + if (newStatus.getStatus() == TrackedRaceStatusEnum.FINISHED) { + polarDataService.newRaceFinishedTracking(trackedRace); + } + } + }); + } + }; + result.addRaceListener(raceListenerForPolarFixCacheUpdate); + } + @Override public DynamicTrackedRegatta getTrackedRegatta(com.sap.sailing.domain.base.Regatta regatta) { return regattaTrackingCache.get(regatta); From f268bb4c9a60e13c10a1d6de94a9c736003cc430 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Thu, 19 Dec 2013 12:49:23 +0100 Subject: [PATCH 05/72] Added polarsheet cache per boat class and first implementation for polar data service api --- .../META-INF/MANIFEST.MF | 2 + .../polars/test/PolarSheetAnalyzerTest.java | 53 +++++++++++++++++++ .../META-INF/MANIFEST.MF | 4 +- .../sap/sailing/polars/PolarDataService.java | 5 ++ .../polars/analysis/PolarSheetAnalyzer.java | 37 +++++++++++++ .../sailing/polars/caching/PolarFixCache.java | 26 +++++++++ .../PolarFixCacheUpdateDoneListener.java | 9 ++++ .../caching/PolarSheetPerBoatClassCache.java | 21 ++++++++ .../PolarSheetPerBoatClassCacheUpdater.java | 36 +++++++++++++ .../polars/impl/PolarDataServiceImpl.java | 28 +++++++++- 10 files changed, 218 insertions(+), 3 deletions(-) create mode 100644 java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdateDoneListener.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarSheetPerBoatClassCache.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarSheetPerBoatClassCacheUpdater.java diff --git a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF index f556d168749..b661e5a3860 100644 --- a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF @@ -13,7 +13,9 @@ Import-Package: com.sap.sailing.domain.base, com.sap.sailing.domain.tracking, com.sap.sailing.domain.tracking.impl, com.sap.sailing.polars.aggregation, + com.sap.sailing.polars.analysis, com.sap.sailing.polars.data, com.sap.sailing.polars.data.impl, com.sap.sailing.polars.generation, + com.sap.sailing.polars.impl, org.junit;version="4.8.2" diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java new file mode 100644 index 00000000000..e31aa06cdde --- /dev/null +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -0,0 +1,53 @@ +package com.sap.sailing.polars.test; + +import java.util.concurrent.Executor; + +import org.junit.Assert; +import org.junit.Test; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; +import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; +import com.sap.sailing.polars.impl.PolarDataServiceImpl; + +public class PolarSheetAnalyzerTest { + + @Test + public void testOptimalUpwindSpeedCalculation() { + PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); + Number[][] averagedPolarDataByWindSpeed = { + {}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.3, 0.4, 0.5, 0.501, 0.502, 0.503, 0.504, 0.505, 0.506, 0.507, + 0.508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, {} }; + PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, + settings.getWindStepping(), null); + PolarSheetAnalyzer analyzer = new PolarSheetAnalyzer(new MockedPolarDataService(null, data)); + SpeedWithBearing result = analyzer.getOptimalUpwindSpeedWithBearingFor(null, new KnotSpeedImpl(6)); + Assert.assertEquals(0.5 * Math.cos(Math.toRadians(45)), result.getKnots(), 0.01); + Assert.assertEquals(45, result.getBearing().getDegrees(), 0.01); + } + + private class MockedPolarDataService extends PolarDataServiceImpl { + + private PolarSheetsData mockedData; + + public MockedPolarDataService(Executor executor, PolarSheetsData mockedData) { + super(executor); + this.mockedData = mockedData; + } + + @Override + public PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass) { + return mockedData; + } + + } + +} diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index b42d9436d8f..ed712a32c95 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -9,10 +9,12 @@ Bundle-ClassPath: . Export-Package: com.sap.sailing.polars, com.sap.sailing.polars.aggregation, + com.sap.sailing.polars.analysis, com.sap.sailing.polars.data, com.sap.sailing.polars.data.impl, com.sap.sailing.polars.factory, - com.sap.sailing.polars.generation + com.sap.sailing.polars.generation, + com.sap.sailing.polars.impl Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 2bb934d0589..0e76a2f67b6 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -11,6 +11,7 @@ import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.polars.data.PolarFix; public interface PolarDataService { @@ -97,4 +98,8 @@ public interface PolarDataService { void newRaceFinishedTracking(TrackedRace trackedRace); + Set getPolarFixesForBoatClass(BoatClass key); + + public PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass); + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java new file mode 100644 index 00000000000..5c83fd501d0 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java @@ -0,0 +1,37 @@ +package com.sap.sailing.polars.analysis; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.polars.PolarDataService; + +public class PolarSheetAnalyzer { + + private PolarDataService polarDataService; + + public PolarSheetAnalyzer(PolarDataService polarDataService) { + this.polarDataService = polarDataService; + } + + public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + PolarSheetsData data = polarDataService.getPolarSheetForBoatClass(boatClass); + Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed(); + int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); + Number[] dataForProvidedWindSpeed = dataPerWindSpeed[windSpeedIndex]; + double optUpwindSpeed = 0; + int optUpwindDeg = 0; + for (int i = 0; i < 90; i++) { + Number speed = dataForProvidedWindSpeed[i]; + double speedUpwind = speed.doubleValue() * Math.cos(Math.toRadians(i)); + if (speedUpwind > optUpwindSpeed) { + optUpwindDeg = i; + optUpwindSpeed = speedUpwind; + } + } + return new KnotSpeedWithBearingImpl(optUpwindSpeed, new DegreeBearingImpl(optUpwindDeg)); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java index f066532dce5..7f2e53e91e6 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java @@ -1,5 +1,6 @@ package com.sap.sailing.polars.caching; +import java.util.ArrayList; import java.util.List; import java.util.Map; import java.util.concurrent.Executor; @@ -12,8 +13,33 @@ import com.sap.sailing.util.SmartFutureCache; public class PolarFixCache extends SmartFutureCache>, PolarFixCacheRaceInterval> { + private final List cacheUpdateDoneListeners = new ArrayList(); + public PolarFixCache(Executor executor) { super(new PolarFixCacheUpdater(executor), "polarFixCache"); } + @Override + protected void cache(BoatClass key, Map> value) { + super.cache(key, value); + synchronized (cacheUpdateDoneListeners) { + for (PolarFixCacheUpdateDoneListener listener : cacheUpdateDoneListeners) { + listener.cacheUpdateDoneForBoatClass(key); + } + } + } + + public void addListener(PolarFixCacheUpdateDoneListener listener) { + synchronized (cacheUpdateDoneListeners) { + cacheUpdateDoneListeners.add(listener); + } + } + + public void removeListener(PolarFixCacheUpdateDoneListener listener) { + synchronized (cacheUpdateDoneListeners) { + if (cacheUpdateDoneListeners.contains(listener)) { + cacheUpdateDoneListeners.remove(listener); + } + } + } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdateDoneListener.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdateDoneListener.java new file mode 100644 index 00000000000..75a32432e04 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdateDoneListener.java @@ -0,0 +1,9 @@ +package com.sap.sailing.polars.caching; + +import com.sap.sailing.domain.base.BoatClass; + +public interface PolarFixCacheUpdateDoneListener { + + void cacheUpdateDoneForBoatClass(BoatClass key); + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarSheetPerBoatClassCache.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarSheetPerBoatClassCache.java new file mode 100644 index 00000000000..94e4fa0a4c8 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarSheetPerBoatClassCache.java @@ -0,0 +1,21 @@ +package com.sap.sailing.polars.caching; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.util.SmartFutureCache; + +public class PolarSheetPerBoatClassCache extends + SmartFutureCache + implements PolarFixCacheUpdateDoneListener { + + public PolarSheetPerBoatClassCache(PolarDataService polarDataService) { + super(new PolarSheetPerBoatClassCacheUpdater(polarDataService), "PolarSheetPerBoatClassCache"); + } + + @Override + public void cacheUpdateDoneForBoatClass(BoatClass key) { + triggerUpdate(key, new EmptyUpdateInterval()); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarSheetPerBoatClassCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarSheetPerBoatClassCacheUpdater.java new file mode 100644 index 00000000000..fb94b05ea47 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarSheetPerBoatClassCacheUpdater.java @@ -0,0 +1,36 @@ +package com.sap.sailing.polars.caching; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; +import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.generation.PolarSheetGenerator; +import com.sap.sailing.util.SmartFutureCache.CacheUpdater; +import com.sap.sailing.util.SmartFutureCache.EmptyUpdateInterval; + +public class PolarSheetPerBoatClassCacheUpdater implements + CacheUpdater { + + private final PolarDataService polarDataService; + + public PolarSheetPerBoatClassCacheUpdater(PolarDataService polarDataService) { + this.polarDataService = polarDataService; + } + + @Override + public PolarSheetsData computeCacheUpdate(BoatClass key, EmptyUpdateInterval updateInterval) throws Exception { + PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); + PolarSheetGenerator generator = new PolarSheetGenerator(polarDataService.getPolarFixesForBoatClass(key), + settings); + PolarSheetsData generationResult = generator.generate(); + return generationResult; + } + + @Override + public PolarSheetsData provideNewCacheValue(BoatClass key, PolarSheetsData oldCacheValue, + PolarSheetsData computedCacheUpdate, EmptyUpdateInterval updateInterval) { + return computedCacheUpdate; + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 04bf28bfc76..cf7e05eac51 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -1,6 +1,8 @@ package com.sap.sailing.polars.impl; import java.util.HashSet; +import java.util.List; +import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -9,28 +11,35 @@ import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.aggregation.PolarFixAggregator; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.caching.PolarFixCache; import com.sap.sailing.polars.caching.PolarFixCacheRaceInterval; +import com.sap.sailing.polars.caching.PolarSheetPerBoatClassCache; import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.polars.generation.PolarSheetGenerator; public class PolarDataServiceImpl implements PolarDataService { private final PolarFixCache polarFixCache; + private final PolarSheetPerBoatClassCache polarSheetPerBoatClassCache; + private final PolarSheetAnalyzer polarSheetAnalyzer; public PolarDataServiceImpl(Executor executor) { this.polarFixCache = new PolarFixCache(executor); + this.polarSheetPerBoatClassCache = new PolarSheetPerBoatClassCache(this); + polarFixCache.addListener(polarSheetPerBoatClassCache); + this.polarSheetAnalyzer = new PolarSheetAnalyzer(this); } @Override public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { - // TODO Auto-generated method stub - return null; + return polarSheetAnalyzer.getOptimalUpwindSpeedWithBearingFor(boatClass, windSpeed); } @Override @@ -80,4 +89,19 @@ public class PolarDataServiceImpl implements PolarDataService { polarFixCache.triggerUpdate(trackedRace.getRace().getBoatClass(), new PolarFixCacheRaceInterval(set)); } + @Override + public Set getPolarFixesForBoatClass(BoatClass key) { + Map> fixesInMap = polarFixCache.get(key, false); + Set resultSet = new HashSet(); + for (List value : fixesInMap.values()) { + resultSet.addAll(value); + } + return resultSet; + } + + @Override + public PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass) { + return polarSheetPerBoatClassCache.get(boatClass, false); + } + } From 9ea07107cb09a08ec0200ad25137b034b6db9ae7 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 8 Jan 2014 08:48:33 +0100 Subject: [PATCH 06/72] Added documentation to polar sheet api --- .../sap/sailing/polars/PolarDataService.java | 20 +++++++++++++++++++ .../polars/analysis/PolarSheetAnalyzer.java | 6 ++++++ .../polars/impl/PolarDataServiceImpl.java | 9 +++++++++ 3 files changed, 35 insertions(+) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 0e76a2f67b6..d0e5d008a15 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -13,6 +13,14 @@ import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.data.PolarFix; +/** + * Public Facade interface allowing access to the polars of {@link TrackedRace}s and per {@link BoatClass}. Methods like + * {@link PolarDataService#getOptimalUpwindSpeedWithBearingFor(BoatClass, Speed)} allow the extraction of typical useful + * information obtainable from polar sheets. + * + * @author Frederik Petersen (D054528) + * + */ public interface PolarDataService { /** @@ -98,8 +106,20 @@ public interface PolarDataService { void newRaceFinishedTracking(TrackedRace trackedRace); + /** + * @param key + * The {@link BoatClass} to obtain fixes for. + * @return All raw polar fixes for the {@link BoatClass}. The implementation is responsible for deciding wether a + * cache is used or not. + */ Set getPolarFixesForBoatClass(BoatClass key); + /** + * + * @param boatClass + * The {@link BoatClass} to obtain the polar sheet for. + * @return The polar sheet for all existing races of the {@link BoatClass}. + */ public PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java index 5c83fd501d0..1bc4c206a71 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java @@ -8,6 +8,12 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.polars.PolarDataService; +/** + * Extracts typical measures from polar sheets. + * + * @author Frederik Petersen (D054528) + * + */ public class PolarSheetAnalyzer { private PolarDataService polarDataService; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index cf7e05eac51..a750cb06b62 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -23,7 +23,16 @@ import com.sap.sailing.polars.caching.PolarFixCacheRaceInterval; import com.sap.sailing.polars.caching.PolarSheetPerBoatClassCache; import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.polars.generation.PolarSheetGenerator; +import com.sap.sailing.util.SmartFutureCache; +/** + * Uses two chained {@link SmartFutureCache}s. One to store {@link PolarFix}es extracted from {@link TrackedRace}s and + * the other one for storing one polar sheet per boat class. This allows quick access to desired measures like optimal + * beat angles. + * + * @author Frederik Petersen (D054528) + * + */ public class PolarDataServiceImpl implements PolarDataService { private final PolarFixCache polarFixCache; From fb9339b8d7f04f587f16e56857cf1e51a40a7df3 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 8 Jan 2014 14:12:56 +0100 Subject: [PATCH 07/72] Polar sheet generation now uses polar fix cache if possible --- .../common/PolarSheetGenerationSettings.java | 2 + .../PolarSheetGenerationSettingsImpl.java | 81 +++++++++++++++++++ .../domain/common/impl/WindSteppingImpl.java | 24 ++++++ .../impl/WindSteppingWithMaxDistance.java | 26 ++++++ .../sap/sailing/polars/PolarDataService.java | 7 ++ .../aggregation/PolarFixAggregator.java | 4 +- .../polars/caching/NoCacheEntryException.java | 27 +++++++ .../sailing/polars/caching/PolarFixCache.java | 21 +++++ .../polars/caching/PolarFixCacheUpdater.java | 4 +- .../generation/PolarSheetGenerator.java | 5 +- .../polars/impl/PolarDataServiceImpl.java | 26 +++++- .../SailingServer (No Proxy).launch | 2 +- ...ingServer (No Proxy, Jetty on 8889).launch | 2 +- ...o Proxy, Jetty on 8889, Monitoring).launch | 2 +- .../SailingServer (Proxy).launch | 2 +- ...ailingServer (Proxy, Jetty on 8889).launch | 2 +- ...Server (Proxy, Remote Debug SAP VM).launch | 2 +- ...Proxy, SwissTiming Live Simulation).launch | 2 +- 18 files changed, 228 insertions(+), 13 deletions(-) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/NoCacheEntryException.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetGenerationSettings.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetGenerationSettings.java index 909f661a131..821ff5cb33d 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetGenerationSettings.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetGenerationSettings.java @@ -30,4 +30,6 @@ public interface PolarSheetGenerationSettings extends Serializable{ boolean splitByWindgauges(); + boolean areDefault(); + } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java index 9d5be9aef52..ca26d292e0f 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java @@ -108,4 +108,85 @@ public class PolarSheetGenerationSettingsImpl implements PolarSheetGenerationSet return splitByWindGauges; } + @Override + public boolean areDefault() { + return createStandardPolarSettings().equals(this); + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + long temp; + temp = Double.doubleToLongBits(minimumConfidenceMeasure); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + ((minimumDataCountPerAngle == null) ? 0 : minimumDataCountPerAngle.hashCode()); + result = prime * result + ((minimumDataCountPerGraph == null) ? 0 : minimumDataCountPerGraph.hashCode()); + temp = Double.doubleToLongBits(minimumWindConfidence); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + ((numberOfHistogramColumns == null) ? 0 : numberOfHistogramColumns.hashCode()); + temp = Double.doubleToLongBits(outlierDetectionNeighboorhoodRadius); + result = prime * result + (int) (temp ^ (temp >>> 32)); + temp = Double.doubleToLongBits(outlierMinimumNeighboorhoodPct); + result = prime * result + (int) (temp ^ (temp >>> 32)); + result = prime * result + (shouldRemoveOutliers ? 1231 : 1237); + result = prime * result + (splitByWindGauges ? 1231 : 1237); + result = prime * result + (useOnlyEstimationForWindDirection ? 1231 : 1237); + result = prime * result + (useOnlyWindGaugesForWindSpeed ? 1231 : 1237); + result = prime * result + ((windStepping == null) ? 0 : windStepping.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + PolarSheetGenerationSettingsImpl other = (PolarSheetGenerationSettingsImpl) obj; + if (Double.doubleToLongBits(minimumConfidenceMeasure) != Double + .doubleToLongBits(other.minimumConfidenceMeasure)) + return false; + if (minimumDataCountPerAngle == null) { + if (other.minimumDataCountPerAngle != null) + return false; + } else if (!minimumDataCountPerAngle.equals(other.minimumDataCountPerAngle)) + return false; + if (minimumDataCountPerGraph == null) { + if (other.minimumDataCountPerGraph != null) + return false; + } else if (!minimumDataCountPerGraph.equals(other.minimumDataCountPerGraph)) + return false; + if (Double.doubleToLongBits(minimumWindConfidence) != Double.doubleToLongBits(other.minimumWindConfidence)) + return false; + if (numberOfHistogramColumns == null) { + if (other.numberOfHistogramColumns != null) + return false; + } else if (!numberOfHistogramColumns.equals(other.numberOfHistogramColumns)) + return false; + if (Double.doubleToLongBits(outlierDetectionNeighboorhoodRadius) != Double + .doubleToLongBits(other.outlierDetectionNeighboorhoodRadius)) + return false; + if (Double.doubleToLongBits(outlierMinimumNeighboorhoodPct) != Double + .doubleToLongBits(other.outlierMinimumNeighboorhoodPct)) + return false; + if (shouldRemoveOutliers != other.shouldRemoveOutliers) + return false; + if (splitByWindGauges != other.splitByWindGauges) + return false; + if (useOnlyEstimationForWindDirection != other.useOnlyEstimationForWindDirection) + return false; + if (useOnlyWindGaugesForWindSpeed != other.useOnlyWindGaugesForWindSpeed) + return false; + if (windStepping == null) { + if (other.windStepping != null) + return false; + } else if (!windStepping.equals(other.windStepping)) + return false; + return true; + } + + } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingImpl.java index c6b0bb04082..7dabefdcf67 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingImpl.java @@ -1,5 +1,7 @@ package com.sap.sailing.domain.common.impl; +import java.util.Arrays; + import com.sap.sailing.domain.common.WindStepping; public class WindSteppingImpl implements WindStepping { @@ -44,4 +46,26 @@ public class WindSteppingImpl implements WindStepping { return result; } + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + Arrays.hashCode(levels); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + WindSteppingImpl other = (WindSteppingImpl) obj; + if (!Arrays.equals(levels, other.levels)) + return false; + return true; + } + } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingWithMaxDistance.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingWithMaxDistance.java index 68e9353db0e..8fea549479e 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingWithMaxDistance.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingWithMaxDistance.java @@ -39,4 +39,30 @@ public class WindSteppingWithMaxDistance extends WindSteppingImpl { public double getMaxDistance() { return maxDistance; } + + @Override + public int hashCode() { + final int prime = 31; + int result = super.hashCode(); + long temp; + temp = Double.doubleToLongBits(maxDistance); + result = prime * result + (int) (temp ^ (temp >>> 32)); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (!super.equals(obj)) + return false; + if (getClass() != obj.getClass()) + return false; + WindSteppingWithMaxDistance other = (WindSteppingWithMaxDistance) obj; + if (Double.doubleToLongBits(maxDistance) != Double.doubleToLongBits(other.maxDistance)) + return false; + return true; + } + + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index d0e5d008a15..78fcf1f171e 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -122,4 +122,11 @@ public interface PolarDataService { */ public PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass); + /** + * + * @return The {@link BoatClass}es for which there are polar sheets available via + * {@link PolarDataService#getPolarSheetForBoatClass(BoatClass)} + */ + public Set getAllBoatClassesWithPolarSheetsAvailable(); + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java index 5a1d7e52e4d..01b60650700 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java @@ -77,7 +77,9 @@ public class PolarFixAggregator implements Future> { * wind's speed in knots */ protected void addPolarFix(PolarFix polarFix) { - fixes.add(polarFix); + synchronized (fixes) { + fixes.add(polarFix); + } } private boolean allWorkersDone() { diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/NoCacheEntryException.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/NoCacheEntryException.java new file mode 100644 index 00000000000..b4cfec4d06a --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/NoCacheEntryException.java @@ -0,0 +1,27 @@ +package com.sap.sailing.polars.caching; + +import java.util.Set; + +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.polars.data.PolarFix; + +public class NoCacheEntryException extends Exception { + + private static final long serialVersionUID = 5547683568438235668L; + private final Set notCached; + private final Set resultList; + + public NoCacheEntryException(Set notCached, Set resultList) { + this.notCached = notCached; + this.resultList = resultList; + } + + public Set getNotCached() { + return notCached; + } + + public Set getCachedResultList() { + return resultList; + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java index 7f2e53e91e6..61234ebef1f 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java @@ -1,12 +1,15 @@ package com.sap.sailing.polars.caching; import java.util.ArrayList; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.util.SmartFutureCache; @@ -42,4 +45,22 @@ public class PolarFixCache extends } } } + + public Set getFixesForTrackedRaces(Set trackedRaces) throws NoCacheEntryException { + Set resultList = new HashSet(); + Set notCached = new HashSet(); + for (TrackedRace trackedRace : trackedRaces) { + Map> result = get(trackedRace.getRace().getBoatClass(), false); + List resultForRace = result.get(trackedRace.getRaceIdentifier()); + if (resultForRace == null) { + notCached.add(trackedRace); + } + resultList.addAll(resultForRace); + } + if (notCached.size() > 0) { + throw new NoCacheEntryException(notCached, resultList); + } + return resultList; + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java index 945802dcbd5..ab1e286c993 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java @@ -55,7 +55,9 @@ public class PolarFixCacheUpdater implements Map> oldCacheValue, Map> computedCacheUpdate, PolarFixCacheRaceInterval updateInterval) { Map> newCacheValue = new HashMap>(); - newCacheValue.putAll(oldCacheValue); + if (oldCacheValue != null) { + newCacheValue.putAll(oldCacheValue); + } for (Entry> newEntry : computedCacheUpdate.entrySet()) { newCacheValue.put(newEntry.getKey(), newEntry.getValue()); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetGenerator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetGenerator.java index f512a9ca01e..ef920e65ceb 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetGenerator.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/generation/PolarSheetGenerator.java @@ -32,7 +32,7 @@ public class PolarSheetGenerator { public PolarSheetGenerator(Set fixes, PolarSheetGenerationSettings settings) { this.settings = settings; this.fixes = fixes; - stepping = settings.getWindStepping();; + stepping = settings.getWindStepping(); polarData = initializePolarDataContainer(); } @@ -50,6 +50,9 @@ public class PolarSheetGenerator { Speed boatSpeed = polarFix.getBoatSpeed(); Speed windSpeed = polarFix.getWindSpeed(); int angle = (int) roundedAngle; + if (angle < 0) { + angle = (360 + angle); + } BoatAndWindSpeedWithOriginInfo speeds = new BoatAndWindSpeedWithOriginInfoImpl(boatSpeed, windSpeed, polarFix.getGaugeIdString(), polarFix.getDayString()); polarData.get(angle).add(speeds); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index a750cb06b62..5ea261fae3f 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -18,6 +18,7 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.aggregation.PolarFixAggregator; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; +import com.sap.sailing.polars.caching.NoCacheEntryException; import com.sap.sailing.polars.caching.PolarFixCache; import com.sap.sailing.polars.caching.PolarFixCacheRaceInterval; import com.sap.sailing.polars.caching.PolarSheetPerBoatClassCache; @@ -84,9 +85,23 @@ public class PolarDataServiceImpl implements PolarDataService { @Override public PolarSheetsData generatePolarSheet(Set trackedRaces, PolarSheetGenerationSettings settings, Executor executor) throws InterruptedException, ExecutionException { - PolarFixAggregator aggregator = new PolarFixAggregator(trackedRaces, settings, executor); - aggregator.startPolarFixAggregation(); - Set fixes = aggregator.get(); + Set fixes; + // If settings are default, look into cache + if (settings.areDefault()) { + try { + fixes = polarFixCache.getFixesForTrackedRaces(trackedRaces); + } catch (NoCacheEntryException e) { + // If there is no cache entry for at least one of the races: Aggregate for non-cached races. + fixes = e.getCachedResultList(); + PolarFixAggregator aggregator = new PolarFixAggregator(e.getNotCached(), settings, executor); + aggregator.startPolarFixAggregation(); + fixes.addAll(aggregator.get()); + } + } else { + PolarFixAggregator aggregator = new PolarFixAggregator(trackedRaces, settings, executor); + aggregator.startPolarFixAggregation(); + fixes = aggregator.get(); + } PolarSheetGenerator generator = new PolarSheetGenerator(fixes, settings); return generator.generate(); } @@ -113,4 +128,9 @@ public class PolarDataServiceImpl implements PolarDataService { return polarSheetPerBoatClassCache.get(boatClass, false); } + @Override + public Set getAllBoatClassesWithPolarSheetsAvailable() { + return polarSheetPerBoatClassCache.keySet(); + } + } diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index a224a6d735d..501bda79a90 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -32,5 +32,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch index 5c389198eee..b0b883f0331 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch @@ -26,6 +26,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Monitoring).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Monitoring).launch index ac9df1a3e54..871e89c982b 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Monitoring).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Monitoring).launch @@ -26,6 +26,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy).launch b/java/com.sap.sailing.server/SailingServer (Proxy).launch index e43062f69e8..0a824143f25 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy).launch @@ -26,6 +26,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch index 19f12d1e112..82011661e6e 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch @@ -26,6 +26,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch index 3bf7010079d..3c0b0aee73e 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch @@ -26,5 +26,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch b/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch index fb3efbaddb1..5409dff6ecb 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch @@ -25,5 +25,5 @@ - + From dcdd74684440f700290bca9bb6a22dd611e7d320 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 8 Jan 2014 17:24:55 +0100 Subject: [PATCH 08/72] Added option in UI to display cached boat class polars also added optimal downwind speed calculation to polar api --- .../sailing/gwt/ui/client/SailingService.java | 4 ++ .../gwt/ui/client/SailingServiceAsync.java | 5 ++ .../sailing/gwt/ui/client/StringMessages.java | 4 ++ .../gwt/ui/client/StringMessages.properties | 2 + .../ui/client/StringMessages_de.properties | 2 + .../gwt/ui/polarsheets/PolarSheetsPanel.java | 54 +++++++++++++++++++ .../gwt/ui/server/SailingServiceImpl.java | 20 +++++++ .../polars/test/PolarSheetAnalyzerTest.java | 22 +++++++- .../polars/analysis/PolarSheetAnalyzer.java | 30 +++++++++++ .../polars/impl/PolarDataServiceImpl.java | 3 +- 10 files changed, 143 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index 5a86a3d7fe4..4138486aa76 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -308,6 +308,10 @@ public interface SailingService extends RemoteService { PolarSheetGenerationResponse generatePolarSheetForRaces(List selectedRaces, PolarSheetGenerationSettings settings, String name) throws Exception; + List getBoatClassNamesWithPolarSheetsAvailable(); + + PolarSheetGenerationResponse showCachedPolarSheetForBoatClass(String boatClassName); + CourseAreaDTO createCourseArea(String eventIdAsString, String courseAreaName); EventDTO getEventByIdAsString(String eventIdAsString); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index bee04eb526a..f106faf64a8 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -485,5 +485,10 @@ public interface SailingServiceAsync { void removeIgtimiAccount(String eMailOfAccountToRemove, AsyncCallback asyncCallback); void importWindFromIgtimi(List selectedRaces, AsyncCallback> asyncCallback); + + void getBoatClassNamesWithPolarSheetsAvailable(AsyncCallback> asyncCallback); + + void showCachedPolarSheetForBoatClass(String boatClassName, + AsyncCallback asyncCallback); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 0421f5a78b8..604f9549472 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -910,4 +910,8 @@ public interface StringMessages extends Messages { String distanceToLine(); String beatAngleTooltip(); String beatAngle(); + + String showBoatClassChartsLabel(); + + String showDiagram(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 03ca5af0e56..e60fc038c47 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -898,3 +898,5 @@ countDownInMillis={0}s to start distanceToLine=Distance to start line beatAngleTooltip=The angle between the competitors direction and the wind beatAngle=Beat Angle +showBoatClassChartsLabel=You can also view the overall diagram for the available boat classes. +showDiagram=Show Diagram \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 195f8f7ee5f..801b934c53c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -894,3 +894,5 @@ countDownInMillis={0}s bis zum Start distanceToLine=Abstand zur Startlinie beatAngleTooltip=Der Winkel zwischen Windrichtung und Fahrtrichtung des Teilnehmers beatAngle=Winkel zum Wind +showBoatClassChartsLabel=Im folgenden können außerdem auch die existierenden Diagramme für die vorhandenen Bootsklassen angezeigt werden. +showDiagram=Diagramm anzeigen \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsPanel.java index 43227986d95..8d2e64a2821 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsPanel.java @@ -12,9 +12,11 @@ import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.Window; import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Button; import com.google.gwt.user.client.ui.DockLayoutPanel; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.ScrollPanel; import com.google.gwt.user.client.ui.SplitLayoutPanel; import com.google.gwt.user.client.ui.TextBox; @@ -93,6 +95,7 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC namingBox = new TextBox(); namingContainer.add(namingBox); leftPanel.add(namingContainer); + addBoatClassDiagramArea(leftPanel); DockLayoutPanel rightPanel = new DockLayoutPanel(Unit.PCT); PolarSheetsChartPanel polarSheetsChartPanel = createPolarSheetsChartPanel(); DockLayoutPanel polarChartAndControlPanel = new DockLayoutPanel(Unit.PCT); @@ -119,6 +122,57 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC chartPanel.setSettings(initialSettings); } + private void addBoatClassDiagramArea(VerticalPanel leftPanel) { + leftPanel.add(new Label(stringMessages.showBoatClassChartsLabel())); + final ListBox boatClassListBox = createBoatClassListBox(); + leftPanel.add(boatClassListBox); + Button showBoatClassDiagramButton = new Button(stringMessages.showDiagram()); + showBoatClassDiagramButton.addClickHandler(new ClickHandler() { + + @Override + public void onClick(ClickEvent event) { + chartPanel.showLoadingInfo(); + sailingService.showCachedPolarSheetForBoatClass( + boatClassListBox.getItemText(boatClassListBox.getSelectedIndex()), + new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError(caught.getLocalizedMessage()); + } + + @Override + public void onSuccess(PolarSheetGenerationResponse result) { + String name = createNameForPolarSheet(result); + chartPanel.getPolarSheetsDataMap().put(result.getId(), result.getData()); + chartPanel.setData(name, result.getData()); + chartPanel.hideLoadingInfo(); + } + }); + } + }); + leftPanel.add(showBoatClassDiagramButton); + } + + private ListBox createBoatClassListBox() { + final ListBox boatClassListBox = new ListBox(); + sailingService.getBoatClassNamesWithPolarSheetsAvailable(new AsyncCallback>() { + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError(caught.getLocalizedMessage()); + } + + @Override + public void onSuccess(List result) { + for (String item : result) { + boatClassListBox.addItem(item); + } + } + }); + return boatClassListBox; + } + private void setEventListenersForPolarSheetChart() { PointSelectEventHandler pointSelectEventHandler = new PointSelectEventHandler() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index f935fe9f561..d1ffac0f976 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -3138,6 +3138,25 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return new PolarSheetGenerationResponseImpl(id, name, result); } + @Override + public List getBoatClassNamesWithPolarSheetsAvailable() { + Set boatClasses = getService().getPolarDataService().getAllBoatClassesWithPolarSheetsAvailable(); + List names = new ArrayList(); + for (BoatClass boatClass : boatClasses) { + names.add(boatClass.getName()); + } + return names; + } + + @Override + public PolarSheetGenerationResponse showCachedPolarSheetForBoatClass(String boatClassName) { + BoatClass boatClass = getService().getBaseDomainFactory().getOrCreateBoatClass(boatClassName); + PolarSheetsData data = getService().getPolarDataService().getPolarSheetForBoatClass(boatClass); + String name = boatClassName + "_OVERALL"; + String id = name; + return new PolarSheetGenerationResponseImpl(id, name, data); + } + private String getCommonBoatClass(Set trackedRaces) { BoatClass boatClass = null; for (TrackedRace race : trackedRaces) { @@ -3684,4 +3703,5 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } return result; } + } \ No newline at end of file diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java index e31aa06cdde..f7064a0c56c 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -25,7 +25,7 @@ public class PolarSheetAnalyzerTest { { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.3, 0.4, 0.5, 0.501, 0.502, 0.503, 0.504, 0.505, 0.506, 0.507, 0.508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, {} }; + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, {} }; PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, settings.getWindStepping(), null); PolarSheetAnalyzer analyzer = new PolarSheetAnalyzer(new MockedPolarDataService(null, data)); @@ -34,6 +34,26 @@ public class PolarSheetAnalyzerTest { Assert.assertEquals(45, result.getBearing().getDegrees(), 0.01); } + @Test + public void testOptimalDownwindSpeedCalculation() { + PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); + Number[][] averagedPolarDataByWindSpeed = { + {}, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4.5, 4, 3, 2, 1, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0 }, {} }; + PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, + settings.getWindStepping(), null); + PolarSheetAnalyzer analyzer = new PolarSheetAnalyzer(new MockedPolarDataService(null, data)); + SpeedWithBearing result = analyzer.getOptimalDownwindSpeedWithBearingFor(null, new KnotSpeedImpl(6)); + Assert.assertEquals(4.5 * Math.sin(Math.toRadians(85)), result.getKnots(), 0.01); + Assert.assertEquals(175, result.getBearing().getDegrees(), 0.01); + } + private class MockedPolarDataService extends PolarDataServiceImpl { private PolarSheetsData mockedData; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java index 1bc4c206a71..70053988acd 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java @@ -27,6 +27,9 @@ public class PolarSheetAnalyzer { Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed(); int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); Number[] dataForProvidedWindSpeed = dataPerWindSpeed[windSpeedIndex]; + if (dataForProvidedWindSpeed.length == 360) { + dataForProvidedWindSpeed = convertToOneSidedPolar(dataForProvidedWindSpeed); + } double optUpwindSpeed = 0; int optUpwindDeg = 0; for (int i = 0; i < 90; i++) { @@ -40,4 +43,31 @@ public class PolarSheetAnalyzer { return new KnotSpeedWithBearingImpl(optUpwindSpeed, new DegreeBearingImpl(optUpwindDeg)); } + public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + PolarSheetsData data = polarDataService.getPolarSheetForBoatClass(boatClass); + Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed(); + int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); + Number[] dataForProvidedWindSpeed = dataPerWindSpeed[windSpeedIndex]; + double optDownwindSpeed = 0; + int optUpwindDeg = 0; + for (int i = 90; i < 180; i++) { + Number speed = dataForProvidedWindSpeed[i]; + double speedUpwind = speed.doubleValue() * Math.sin(Math.toRadians(i - 90)); + if (speedUpwind > optDownwindSpeed) { + optUpwindDeg = i; + optDownwindSpeed = speedUpwind; + } + } + return new KnotSpeedWithBearingImpl(optDownwindSpeed, new DegreeBearingImpl(optUpwindDeg)); + } + + private Number[] convertToOneSidedPolar(Number[] dataForProvidedWindSpeed) { + Number[] oneSided = new Number[180]; + for (int i = 181; i < 360; i++) { + oneSided[360 - i] = (dataForProvidedWindSpeed[360 - i].doubleValue() + dataForProvidedWindSpeed[i] + .doubleValue()) / 2; + } + return oneSided; + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 5ea261fae3f..da35c9cbf78 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -54,8 +54,7 @@ public class PolarDataServiceImpl implements PolarDataService { @Override public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { - // TODO Auto-generated method stub - return null; + return polarSheetAnalyzer.getOptimalDownwindSpeedWithBearingFor(boatClass, windSpeed); } @Override From d690ca8c16a652b42c8ececfaf80a168fcaaa18c Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Thu, 9 Jan 2014 14:28:04 +0100 Subject: [PATCH 09/72] Changed polars test bundle to fragment --- .../META-INF/MANIFEST.MF | 15 ++------------- java/com.sap.sailing.polars/META-INF/MANIFEST.MF | 7 +------ java/com.sap.sailing.server/META-INF/MANIFEST.MF | 3 ++- 3 files changed, 5 insertions(+), 20 deletions(-) diff --git a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF index b661e5a3860..08b24d6da7b 100644 --- a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF @@ -5,17 +5,6 @@ Bundle-SymbolicName: com.sap.sailing.polars.test Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: com.sap.sailing.domain.base, - com.sap.sailing.domain.base.impl, - com.sap.sailing.domain.common, - com.sap.sailing.domain.common.impl, - com.sap.sailing.domain.test.mock, - com.sap.sailing.domain.tracking, - com.sap.sailing.domain.tracking.impl, - com.sap.sailing.polars.aggregation, - com.sap.sailing.polars.analysis, - com.sap.sailing.polars.data, - com.sap.sailing.polars.data.impl, - com.sap.sailing.polars.generation, - com.sap.sailing.polars.impl, +Fragment-Host: com.sap.sailing.polars +Import-Package: com.sap.sailing.domain.test.mock, org.junit;version="4.8.2" diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index ed712a32c95..695b6402514 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -8,13 +8,8 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ClassPath: . Export-Package: com.sap.sailing.polars, - com.sap.sailing.polars.aggregation, - com.sap.sailing.polars.analysis, com.sap.sailing.polars.data, - com.sap.sailing.polars.data.impl, - com.sap.sailing.polars.factory, - com.sap.sailing.polars.generation, - com.sap.sailing.polars.impl + com.sap.sailing.polars.factory Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", diff --git a/java/com.sap.sailing.server/META-INF/MANIFEST.MF b/java/com.sap.sailing.server/META-INF/MANIFEST.MF index 17d204a81ae..978d50804b9 100755 --- a/java/com.sap.sailing.server/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server/META-INF/MANIFEST.MF @@ -26,7 +26,8 @@ Require-Bundle: com.sap.sailing.domain, com.sap.sailing.udpconnector, com.sap.sailing.operationaltransformation, com.sap.sailing.domain.shared.android, - com.sap.sailing.server.gateway.serialization + com.sap.sailing.server.gateway.serialization, + com.sap.sailing.polars Bundle-ClassPath: . Export-Package: com.sap.sailing.server, com.sap.sailing.server.impl; From 926fc013dc8a60046e3bf0a9905cd7b4f569b098 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 15 Jan 2014 13:24:46 +0100 Subject: [PATCH 10/72] Started working on distributed polar data service --- .../META-INF/MANIFEST.MF | 4 +- .../sap/sailing/polars/PolarDataService.java | 72 ++--------------- .../polars/impl/PolarDataServiceImpl.java | 79 ++++++++++++------- ...ceWithIntegerRelationJsonDeserializer.java | 26 ++++++ ...enceWithIntegerRelationJsonSerializer.java | 24 ++++++ .../META-INF/MANIFEST.MF | 3 +- .../gateway/jaxrs/api/PolarResource.java | 39 +++++++++ 7 files changed, 148 insertions(+), 99 deletions(-) create mode 100644 java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/SpeedWithConfidenceWithIntegerRelationJsonDeserializer.java create mode 100644 java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/SpeedWithConfidenceWithIntegerRelationJsonSerializer.java create mode 100644 java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index 695b6402514..9b5a72bd398 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -14,7 +14,5 @@ Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", com.sap.sailing.domain -Import-Package: com.sap.sailing.domain.base, - com.sap.sailing.domain.confidence, - com.sap.sailing.domain.tracking, +Import-Package: com.sap.sailing.util diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 78fcf1f171e..944622ca040 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -5,18 +5,16 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.data.PolarFix; /** - * Public Facade interface allowing access to the polars of {@link TrackedRace}s and per {@link BoatClass}. Methods like - * {@link PolarDataService#getOptimalUpwindSpeedWithBearingFor(BoatClass, Speed)} allow the extraction of typical useful - * information obtainable from polar sheets. + * Public Facade interface allowing access to the polars of {@link TrackedRace}s and per {@link BoatClass}. * * @author Frederik Petersen (D054528) * @@ -24,70 +22,14 @@ import com.sap.sailing.polars.data.PolarFix; public interface PolarDataService { /** - * Queries the {@link PolarDataService} for the optimal upwind speed and angle as calculated from the existing - * database. * * @param boatClass - * @return The optimal upwind speed of the given boat class with the corresponding bearing difference to the wind. - * Bear in mind that the Bearing difference is an absolute. It can be applied to both sides of the wind. + * @param windSpeed + * @param bearingToTheWind + * Boat's direction relative to the wind. either in -180 -> +180 or 0 -> 359 degrees + * @return The speed the boat is moving at for the specified wind and bearing according to the polar diagram. */ - SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed); - - /** - * Queries the {@link PolarDataService} for the optimal downwind speed and angle as calculated from the existing - * database. - * - * @param boatClass - * @return The optimal downwind speed of the given boat class with the corresponding bearing difference to the wind. - * Bear in mind that the Bearing difference is an absolute. It can be applied to both sides of the wind. - */ - SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed); - - /** - * Queries the {@link PolarDataService} for the optimal reaching speed at a given bearing difference to the wind. - * This should be used for gathering information about non-up&down-wind legs. For up- and downwind legs use - * {@link #getOptimalUpwindSpeedWithBearingFor(BoatClass, Speed)} and - * {@link #getOptimalDownwindSpeedWithBearingFor(BoatClass, Speed)}. - * - * @param boatClass - * @param bearingDifferenceToWind - * absolute bearing difference of the boat's course to the wind - * @return The optimal speed of the given boat class at the given bearing difference to the wind. - */ - Speed getOptimalReachingSpeedFor(BoatClass boatClass, Speed windSpeed, Bearing bearingDifferenceToWind); - - /** - * Queries the {@link PolarDataService} for the average upwind speed and angle as calculated from the existing - * database. - * - * @param boatClass - * @return The average upwind speed of the given boat class with the corresponding bearing difference to the wind. - * Bear in mind that the Bearing difference is an absolute. It can be applied to both sides of the wind. - */ - SpeedWithBearing getAverageUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed); - - /** - * Queries the {@link PolarDataService} for the average downwind speed and angle as calculated from the existing - * database. - * - * @param boatClass - * @return The average downwind speed of the given boat class with the corresponding bearing difference to the wind. - * Bear in mind that the Bearing difference is an absolute. It can be applied to both sides of the wind. - */ - SpeedWithBearing getAverageDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed); - - /** - * Queries the {@link PolarDataService} for the average reaching speed at a given bearing difference to the wind. - * This should be used for gathering information about non-up&down-wind legs. For up- and downwind legs use - * {@link #getAverageUpwindSpeedWithBearingFor(BoatClass, Speed)} and - * {@link #getAverageDownwindSpeedWithBearingFor(BoatClass, Speed)}. - * - * @param boatClass - * @param bearingDifferenceToWind - * absolute bearing difference of the boat's course to the wind - * @return The average speed of the given boat class at the given bearing difference to the wind. - */ - Speed getAverageReachingSpeedFor(BoatClass boatClass, Speed windSpeed, Bearing bearingDifferenceToWind); + SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind); /** * Generates a polar sheet for geven races and settings using the provided executor for the worker threads. This diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index da35c9cbf78..eaf3eb0411a 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -8,16 +8,18 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.domain.common.PolarSheetsHistogramData; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.aggregation.PolarFixAggregator; -import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.caching.NoCacheEntryException; import com.sap.sailing.polars.caching.PolarFixCache; import com.sap.sailing.polars.caching.PolarFixCacheRaceInterval; @@ -38,48 +40,65 @@ public class PolarDataServiceImpl implements PolarDataService { private final PolarFixCache polarFixCache; private final PolarSheetPerBoatClassCache polarSheetPerBoatClassCache; - private final PolarSheetAnalyzer polarSheetAnalyzer; public PolarDataServiceImpl(Executor executor) { this.polarFixCache = new PolarFixCache(executor); this.polarSheetPerBoatClassCache = new PolarSheetPerBoatClassCache(this); polarFixCache.addListener(polarSheetPerBoatClassCache); - this.polarSheetAnalyzer = new PolarSheetAnalyzer(this); } @Override - public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { - return polarSheetAnalyzer.getOptimalUpwindSpeedWithBearingFor(boatClass, windSpeed); + public SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) { + PolarSheetsData data = polarSheetPerBoatClassCache.get(boatClass, false); + int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); + double angleToTheWind = bearingToTheWind.getDegrees(); + if (angleToTheWind < 0) { + angleToTheWind = 360 + angleToTheWind; + } + int angleToTheWindFloor = (int) Math.floor(bearingToTheWind.getDegrees()); + SpeedWithConfidence speedFloor = getSpeedForConcreteValues(data, windSpeedIndex, angleToTheWindFloor); + int angleToTheWindCeiling = (int) Math.ceil(bearingToTheWind.getDegrees()); + SpeedWithConfidence speedCeiling = getSpeedForConcreteValues(data, windSpeedIndex, + angleToTheWindCeiling); + SpeedWithConfidence speed = interpolateSpeed(speedFloor, speedCeiling, angleToTheWind + - angleToTheWindFloor); + + return speed; } - @Override - public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { - return polarSheetAnalyzer.getOptimalDownwindSpeedWithBearingFor(boatClass, windSpeed); + private SpeedWithConfidence interpolateSpeed(SpeedWithConfidence speedFloor, + SpeedWithConfidence speedCeiling, double distanceToFloor) { + double distanceOverConfidenceFactor = 0.8; + double floorFactor = distanceOverConfidenceFactor * (1 - distanceToFloor) + (1 - distanceOverConfidenceFactor) + * speedFloor.getConfidence(); + double ceilingFactor = distanceOverConfidenceFactor * distanceToFloor + (1 - distanceOverConfidenceFactor) + * speedCeiling.getConfidence(); + double alpha = 1 / (floorFactor + ceilingFactor); + double scaledFactorFloor = floorFactor * alpha; + double scaledFactorCeiling = ceilingFactor * alpha; + // FIXME remove this assert when testing is done + assert (1 - (scaledFactorCeiling + scaledFactorFloor) < 0.005 && 1 - (scaledFactorCeiling + scaledFactorFloor) > -0.005); + double interpolatedSpeedInKnots = scaledFactorFloor * speedFloor.getObject().getKnots() + scaledFactorCeiling + * speedCeiling.getObject().getKnots(); + double interpolatedConfidence = scaledFactorFloor * speedFloor.getConfidence() + scaledFactorCeiling + * speedCeiling.getConfidence(); + Speed interpolatedSpeed = new KnotSpeedImpl(interpolatedSpeedInKnots); + int numberOfUnderlyingFixes = speedFloor.getRelativeTo() + speedCeiling.getRelativeTo(); + return new SpeedWithConfidenceImpl(interpolatedSpeed, interpolatedConfidence, numberOfUnderlyingFixes); } - @Override - public Speed getOptimalReachingSpeedFor(BoatClass boatClass, Speed windSpeed, Bearing bearingDifferenceToWind) { - // TODO Auto-generated method stub - return null; + private SpeedWithConfidence getSpeedForConcreteValues(PolarSheetsData data, int windSpeedIndex, + int angleToTheWind) { + Number rawSpeed = data.getAveragedPolarDataByWindSpeed()[windSpeedIndex][angleToTheWind]; + Speed speed = new KnotSpeedImpl(rawSpeed.doubleValue()); + PolarSheetsHistogramData histogramData = data.getHistogramDataMap().get(windSpeedIndex).get(angleToTheWind); + double confidence = histogramData.getConfidenceMeasure(); + int numberOfUnderlyingFixes = histogramData.getDataCount(); + SpeedWithConfidence speedWithConfidence = new SpeedWithConfidenceImpl(speed, confidence, + numberOfUnderlyingFixes); + return speedWithConfidence; } - @Override - public SpeedWithBearing getAverageUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { - // TODO Auto-generated method stub - return null; - } - - @Override - public SpeedWithBearing getAverageDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { - // TODO Auto-generated method stub - return null; - } - - @Override - public Speed getAverageReachingSpeedFor(BoatClass boatClass, Speed windSpeed, Bearing bearingDifferenceToWind) { - // TODO Auto-generated method stub - return null; - } @Override public PolarSheetsData generatePolarSheet(Set trackedRaces, PolarSheetGenerationSettings settings, diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/SpeedWithConfidenceWithIntegerRelationJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/SpeedWithConfidenceWithIntegerRelationJsonDeserializer.java new file mode 100644 index 00000000000..cf9cf344254 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/SpeedWithConfidenceWithIntegerRelationJsonDeserializer.java @@ -0,0 +1,26 @@ +package com.sap.sailing.server.gateway.deserialization.impl; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializationException; +import com.sap.sailing.server.gateway.deserialization.JsonDeserializer; +import com.sap.sailing.server.gateway.serialization.impl.SpeedWithConfidenceWithIntegerRelationJsonSerializer; + +public class SpeedWithConfidenceWithIntegerRelationJsonDeserializer implements + JsonDeserializer> { + + @Override + public SpeedWithConfidence deserialize(JSONObject object) throws JsonDeserializationException { + double speedInKnots = (Double) object.get(SpeedWithConfidenceWithIntegerRelationJsonSerializer.FIELD_SPEED); + double confidence = (Double) object.get(SpeedWithConfidenceWithIntegerRelationJsonSerializer.FIELD_CONFIDENCE); + int relativeToValue = (Integer) object + .get(SpeedWithConfidenceWithIntegerRelationJsonSerializer.FIELD_RELATION_INT); + Speed speed = new KnotSpeedImpl(speedInKnots); + return new SpeedWithConfidenceImpl(speed, confidence, relativeToValue); + } + +} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/SpeedWithConfidenceWithIntegerRelationJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/SpeedWithConfidenceWithIntegerRelationJsonSerializer.java new file mode 100644 index 00000000000..887934e3450 --- /dev/null +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/SpeedWithConfidenceWithIntegerRelationJsonSerializer.java @@ -0,0 +1,24 @@ +package com.sap.sailing.server.gateway.serialization.impl; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.server.gateway.serialization.JsonSerializer; + +public class SpeedWithConfidenceWithIntegerRelationJsonSerializer implements + JsonSerializer> { + + public static final String FIELD_SPEED = "speed"; + public static final String FIELD_CONFIDENCE = "confidence"; + public static final String FIELD_RELATION_INT = "relationInt"; + + @Override + public JSONObject serialize(SpeedWithConfidence speed) { + JSONObject object = new JSONObject(); + object.put(FIELD_SPEED, speed.getObject().getKnots()); + object.put(FIELD_CONFIDENCE, speed.getConfidence()); + object.put(FIELD_RELATION_INT, speed.getRelativeTo()); + return object; + } + +} diff --git a/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF index 991c6f43fd7..9635c55c9e4 100644 --- a/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF @@ -5,7 +5,8 @@ Bundle-SymbolicName: com.sap.sailing.server.gateway Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 -Import-Package: com.sap.sailing.server, +Import-Package: com.sap.sailing.polars, + com.sap.sailing.server, com.sap.sailing.server.operationaltransformation, javax.ws.rs;version="1.1.1", javax.ws.rs.core;version="1.1.1", diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java new file mode 100644 index 00000000000..fb3c3c0d123 --- /dev/null +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java @@ -0,0 +1,39 @@ +package com.sap.sailing.server.gateway.jaxrs.api; + +import javax.ws.rs.GET; +import javax.ws.rs.Path; +import javax.ws.rs.PathParam; +import javax.ws.rs.Produces; +import javax.ws.rs.QueryParam; +import javax.ws.rs.core.MediaType; +import javax.ws.rs.core.Response; +import javax.ws.rs.core.Response.ResponseBuilder; + +import org.json.simple.JSONObject; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; +import com.sap.sailing.server.gateway.serialization.impl.SpeedWithConfidenceWithIntegerRelationJsonSerializer; + +@Path("/v1/polars") +public class PolarResource extends AbstractSailingServerResource { + + @GET + @Produces("application/json;charset=UTF-8") + @Path("{boatClassName}") + public Response getSpeed(@PathParam("boatClassName") String boatClassName, @QueryParam("angle") double angle, + @QueryParam("windspeed") double windSpeed) { + BoatClass boatClass = getService().getDomainObjectFactory().getBaseDomainFactory() + .getOrCreateBoatClass(boatClassName); + SpeedWithConfidence speedWithConfidence = getService().getPolarDataService().getSpeed(boatClass, + new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(angle)); + SpeedWithConfidenceWithIntegerRelationJsonSerializer serializer = new SpeedWithConfidenceWithIntegerRelationJsonSerializer(); + JSONObject jsonObj = serializer.serialize(speedWithConfidence); + ResponseBuilder responseBuilder = Response.ok(jsonObj.toJSONString(), MediaType.APPLICATION_JSON); + return responseBuilder.build(); + } + +} From de5378c3a3611d6ec7a7bc53c2e8d2fd7312882e Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 15 Jan 2014 16:27:09 +0100 Subject: [PATCH 11/72] Polar API now interpolates between angles and wind levels --- .../sailing/domain/common/WindStepping.java | 6 ++ .../PolarSheetGenerationSettingsImpl.java | 4 +- .../domain/common/impl/WindSteppingImpl.java | 38 ++++++++++++ .../polars/NoPolarDataAvailableException.java | 28 +++++++++ .../sap/sailing/polars/PolarDataService.java | 4 +- .../polars/impl/PolarDataServiceImpl.java | 62 ++++++++++++++++--- .../gateway/jaxrs/api/PolarResource.java | 18 ++++-- .../gateway/jaxrs/api/RestApiApplication.java | 1 + 8 files changed, 146 insertions(+), 15 deletions(-) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/NoPolarDataAvailableException.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindStepping.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindStepping.java index 4f39dbfdfc9..e6dedbf86e5 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindStepping.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindStepping.java @@ -10,4 +10,10 @@ public interface WindStepping extends Serializable{ Integer[] getRawStepping(); + public abstract int getLevelIndexFloorForValue(double speed); + + public abstract int getLevelIndexCeilingForValue(double speed); + + double getDistanceToLevelFloor(double speed); + } \ No newline at end of file diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java index ca26d292e0f..426eef99c03 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java @@ -5,8 +5,8 @@ import com.sap.sailing.domain.common.PolarSheetGenerationSettings; public class PolarSheetGenerationSettingsImpl implements PolarSheetGenerationSettings { public static PolarSheetGenerationSettings createStandardPolarSettings() { - Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 }; - WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0); + Integer[] levels = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34 }; + WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.5); return new PolarSheetGenerationSettingsImpl(200, 0.1, 10, 20, 0.1, true, true, 2, 0.05, true, windStepping, false); } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingImpl.java index 7dabefdcf67..ea11afd446e 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/WindSteppingImpl.java @@ -68,4 +68,42 @@ public class WindSteppingImpl implements WindStepping { return true; } + @Override + public int getLevelIndexFloorForValue(double speed) { + int result = -1; + for (int i = 0; i < levels.length - 1; i++) { + if (speed >= levels[i]) { + result = i; + } + } + return result; + } + + @Override + public double getDistanceToLevelFloor(double speed) { + double result = 0; + int floor = getLevelIndexFloorForValue(speed); + if (floor == -1) { + result = 0; + } else { + result = speed - levels[floor]; + } + return result; + } + + @Override + public int getLevelIndexCeilingForValue(double speed) { + int floor = getLevelIndexFloorForValue(speed); + int result; + if (floor == -1) { + result = -1; + } + if (levels[floor] == speed) { + result = floor; + } else { + result = floor + 1; + } + return result; + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/NoPolarDataAvailableException.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/NoPolarDataAvailableException.java new file mode 100644 index 00000000000..dcefa34b9af --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/NoPolarDataAvailableException.java @@ -0,0 +1,28 @@ +package com.sap.sailing.polars; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Speed; + +public class NoPolarDataAvailableException extends Exception { + + private static final long serialVersionUID = -6939576877174310618L; + + private final BoatClass boatClass; + private final Speed windSpeed; + private final Bearing bearingToTheWind; + + public NoPolarDataAvailableException(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) { + this.boatClass = boatClass; + this.windSpeed = windSpeed; + this.bearingToTheWind = bearingToTheWind; + } + + @Override + public String getMessage() { + return String + .format("There was no polar data available for boat class '%s' for an angle to the wind of %.1f degrees and the wind speed of %.1f knots.", + boatClass.getName(), bearingToTheWind.getDegrees(), windSpeed.getKnots()); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 944622ca040..e14829cc63f 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -28,8 +28,10 @@ public interface PolarDataService { * @param bearingToTheWind * Boat's direction relative to the wind. either in -180 -> +180 or 0 -> 359 degrees * @return The speed the boat is moving at for the specified wind and bearing according to the polar diagram. + * @throws NoPolarDataAvailableException */ - SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind); + SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) + throws NoPolarDataAvailableException; /** * Generates a polar sheet for geven races and settings using the provided executor for the worker threads. This diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index eaf3eb0411a..6cdb6210aad 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -6,6 +6,7 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; +import java.util.logging.Logger; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.SpeedWithConfidence; @@ -16,8 +17,10 @@ import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.PolarSheetsHistogramData; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.polars.NoPolarDataAvailableException; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.aggregation.PolarFixAggregator; import com.sap.sailing.polars.caching.NoCacheEntryException; @@ -38,6 +41,8 @@ import com.sap.sailing.util.SmartFutureCache; */ public class PolarDataServiceImpl implements PolarDataService { + private static final Logger logger = Logger.getLogger(PolarDataServiceImpl.class.getName()); + private final PolarFixCache polarFixCache; private final PolarSheetPerBoatClassCache polarSheetPerBoatClassCache; @@ -48,21 +53,56 @@ public class PolarDataServiceImpl implements PolarDataService { } @Override - public SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) { + public SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) + throws NoPolarDataAvailableException { PolarSheetsData data = polarSheetPerBoatClassCache.get(boatClass, false); - int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); double angleToTheWind = bearingToTheWind.getDegrees(); if (angleToTheWind < 0) { angleToTheWind = 360 + angleToTheWind; } int angleToTheWindFloor = (int) Math.floor(bearingToTheWind.getDegrees()); - SpeedWithConfidence speedFloor = getSpeedForConcreteValues(data, windSpeedIndex, angleToTheWindFloor); int angleToTheWindCeiling = (int) Math.ceil(bearingToTheWind.getDegrees()); + int windSpeedIndexFloor = data.getStepping().getLevelIndexFloorForValue(windSpeed.getKnots()); + boolean useFloor = true; + boolean useCeiling = true; + int windSpeedIndexCeiling; + if (windSpeedIndexFloor == -1) { + useFloor = false; + windSpeedIndexCeiling = 0; + } else if (windSpeedIndexFloor == data.getStepping().getRawStepping().length - 1) { + windSpeedIndexCeiling = -1; + useCeiling = false; + } else { + windSpeedIndexCeiling = data.getStepping().getLevelIndexCeilingForValue(windSpeed.getKnots()); + } + SpeedWithConfidence speed; + if (useFloor && useCeiling) { + double windSpeedIndexDistanceToFloor = data.getStepping().getDistanceToLevelFloor(windSpeed.getKnots()); + SpeedWithConfidence windSpeedFloor = getInterpolatedSpeedBetweenAngles(boatClass, data, + windSpeedIndexFloor, angleToTheWind, angleToTheWindFloor, angleToTheWindCeiling); + SpeedWithConfidence windSpeedCeiling = getInterpolatedSpeedBetweenAngles(boatClass, data, + windSpeedIndexCeiling, angleToTheWind, angleToTheWindFloor, angleToTheWindCeiling); + speed = interpolateSpeed(windSpeedFloor, windSpeedCeiling, windSpeedIndexDistanceToFloor); + } else if (useFloor) { + speed = getInterpolatedSpeedBetweenAngles(boatClass, data, windSpeedIndexFloor, angleToTheWind, + angleToTheWindFloor, angleToTheWindCeiling); + } else { + speed = getInterpolatedSpeedBetweenAngles(boatClass, data, windSpeedIndexCeiling, angleToTheWind, + angleToTheWindFloor, angleToTheWindCeiling); + } + + return speed; + } + + private SpeedWithConfidence getInterpolatedSpeedBetweenAngles(BoatClass boatClass, PolarSheetsData data, + int windSpeedIndex, double angleToTheWind, int angleToTheWindFloor, int angleToTheWindCeiling) + throws NoPolarDataAvailableException { + SpeedWithConfidence speedFloor = getSpeedForConcreteValues(data, windSpeedIndex, angleToTheWindFloor, + boatClass); SpeedWithConfidence speedCeiling = getSpeedForConcreteValues(data, windSpeedIndex, - angleToTheWindCeiling); + angleToTheWindCeiling, boatClass); SpeedWithConfidence speed = interpolateSpeed(speedFloor, speedCeiling, angleToTheWind - angleToTheWindFloor); - return speed; } @@ -88,14 +128,22 @@ public class PolarDataServiceImpl implements PolarDataService { } private SpeedWithConfidence getSpeedForConcreteValues(PolarSheetsData data, int windSpeedIndex, - int angleToTheWind) { + int angleToTheWind, BoatClass boatClass) throws NoPolarDataAvailableException { Number rawSpeed = data.getAveragedPolarDataByWindSpeed()[windSpeedIndex][angleToTheWind]; Speed speed = new KnotSpeedImpl(rawSpeed.doubleValue()); PolarSheetsHistogramData histogramData = data.getHistogramDataMap().get(windSpeedIndex).get(angleToTheWind); + SpeedWithConfidenceImpl speedWithConfidence; + if (histogramData != null) { double confidence = histogramData.getConfidenceMeasure(); int numberOfUnderlyingFixes = histogramData.getDataCount(); - SpeedWithConfidence speedWithConfidence = new SpeedWithConfidenceImpl(speed, confidence, + speedWithConfidence = new SpeedWithConfidenceImpl(speed, confidence, numberOfUnderlyingFixes); + } else { + NoPolarDataAvailableException e = new NoPolarDataAvailableException(boatClass, new KnotSpeedImpl(data + .getStepping().getRawStepping()[windSpeedIndex]), new DegreeBearingImpl(angleToTheWind)); + logger.warning(e.getLocalizedMessage()); + throw e; + } return speedWithConfidence; } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java index fb3c3c0d123..474767b9762 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java @@ -15,6 +15,7 @@ import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.polars.NoPolarDataAvailableException; import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; import com.sap.sailing.server.gateway.serialization.impl.SpeedWithConfidenceWithIntegerRelationJsonSerializer; @@ -28,11 +29,18 @@ public class PolarResource extends AbstractSailingServerResource { @QueryParam("windspeed") double windSpeed) { BoatClass boatClass = getService().getDomainObjectFactory().getBaseDomainFactory() .getOrCreateBoatClass(boatClassName); - SpeedWithConfidence speedWithConfidence = getService().getPolarDataService().getSpeed(boatClass, - new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(angle)); - SpeedWithConfidenceWithIntegerRelationJsonSerializer serializer = new SpeedWithConfidenceWithIntegerRelationJsonSerializer(); - JSONObject jsonObj = serializer.serialize(speedWithConfidence); - ResponseBuilder responseBuilder = Response.ok(jsonObj.toJSONString(), MediaType.APPLICATION_JSON); + SpeedWithConfidence speedWithConfidence; + ResponseBuilder responseBuilder; + try { + speedWithConfidence = getService().getPolarDataService().getSpeed(boatClass, new KnotSpeedImpl(windSpeed), + new DegreeBearingImpl(angle)); + SpeedWithConfidenceWithIntegerRelationJsonSerializer serializer = new SpeedWithConfidenceWithIntegerRelationJsonSerializer(); + JSONObject jsonObj = serializer.serialize(speedWithConfidence); + responseBuilder = Response.ok(jsonObj.toJSONString(), MediaType.APPLICATION_JSON); + } catch (NoPolarDataAvailableException e) { + responseBuilder = Response.noContent(); + } + return responseBuilder.build(); } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java index 7ef60dead92..caa0bcd5903 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RestApiApplication.java @@ -14,6 +14,7 @@ public class RestApiApplication extends Application { classes.add(RegattasResource.class); classes.add(LeaderboardsResource.class); classes.add(MasterDataResource.class); + classes.add(PolarResource.class); // classes.add(WindResource.class); return classes; From 4f86a174a55525224a786bbd646ba5dae2f23311 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 26 Feb 2014 13:10:44 +0100 Subject: [PATCH 12/72] Added polar sheet analyzer interface --- .../polars/test/PolarSheetAnalyzerTest.java | 7 ++++--- java/com.sap.sailing.polars/META-INF/MANIFEST.MF | 3 ++- .../com/sap/sailing/polars/PolarDataService.java | 7 +++++-- .../analysis/PolarSheetAnalyzerInterface.java | 15 +++++++++++++++ .../PolarSheetAnalyzerImpl.java} | 13 ++++++++----- .../sailing/polars/impl/PolarDataServiceImpl.java | 10 ++++++++++ 6 files changed, 44 insertions(+), 11 deletions(-) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzerInterface.java rename java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/{PolarSheetAnalyzer.java => impl/PolarSheetAnalyzerImpl.java} (84%) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java index f7064a0c56c..d95b65b0900 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -12,7 +12,8 @@ import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; -import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzerInterface; +import com.sap.sailing.polars.analysis.impl.PolarSheetAnalyzerImpl; import com.sap.sailing.polars.impl.PolarDataServiceImpl; public class PolarSheetAnalyzerTest { @@ -28,7 +29,7 @@ public class PolarSheetAnalyzerTest { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, {} }; PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, settings.getWindStepping(), null); - PolarSheetAnalyzer analyzer = new PolarSheetAnalyzer(new MockedPolarDataService(null, data)); + PolarSheetAnalyzerInterface analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); SpeedWithBearing result = analyzer.getOptimalUpwindSpeedWithBearingFor(null, new KnotSpeedImpl(6)); Assert.assertEquals(0.5 * Math.cos(Math.toRadians(45)), result.getKnots(), 0.01); Assert.assertEquals(45, result.getBearing().getDegrees(), 0.01); @@ -48,7 +49,7 @@ public class PolarSheetAnalyzerTest { 0, 0, 0, 0, 0, 0, 0 }, {} }; PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, settings.getWindStepping(), null); - PolarSheetAnalyzer analyzer = new PolarSheetAnalyzer(new MockedPolarDataService(null, data)); + PolarSheetAnalyzerInterface analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); SpeedWithBearing result = analyzer.getOptimalDownwindSpeedWithBearingFor(null, new KnotSpeedImpl(6)); Assert.assertEquals(4.5 * Math.sin(Math.toRadians(85)), result.getKnots(), 0.01); Assert.assertEquals(175, result.getBearing().getDegrees(), 0.01); diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index 9b5a72bd398..f5a36ce37ff 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -9,7 +9,8 @@ Bundle-ClassPath: . Export-Package: com.sap.sailing.polars, com.sap.sailing.polars.data, - com.sap.sailing.polars.factory + com.sap.sailing.polars.factory, + com.sap.sailing.polars.analysis Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index e14829cc63f..fa474495f50 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -11,6 +11,7 @@ import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzerInterface; import com.sap.sailing.polars.data.PolarFix; /** @@ -64,13 +65,15 @@ public interface PolarDataService { * The {@link BoatClass} to obtain the polar sheet for. * @return The polar sheet for all existing races of the {@link BoatClass}. */ - public PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass); + PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass); /** * * @return The {@link BoatClass}es for which there are polar sheets available via * {@link PolarDataService#getPolarSheetForBoatClass(BoatClass)} */ - public Set getAllBoatClassesWithPolarSheetsAvailable(); + Set getAllBoatClassesWithPolarSheetsAvailable(); + + PolarSheetAnalyzerInterface getAnalyzer(); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzerInterface.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzerInterface.java new file mode 100644 index 00000000000..96e8f7d8b28 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzerInterface.java @@ -0,0 +1,15 @@ +package com.sap.sailing.polars.analysis; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; + +public interface PolarSheetAnalyzerInterface { + + SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, + Speed windSpeed); + + SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, + Speed windSpeed); + +} \ No newline at end of file diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java similarity index 84% rename from java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java index 70053988acd..bb4cd7eb18e 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java @@ -1,4 +1,4 @@ -package com.sap.sailing.polars.analysis; +package com.sap.sailing.polars.analysis.impl; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.PolarSheetsData; @@ -7,6 +7,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzerInterface; /** * Extracts typical measures from polar sheets. @@ -14,15 +15,16 @@ import com.sap.sailing.polars.PolarDataService; * @author Frederik Petersen (D054528) * */ -public class PolarSheetAnalyzer { +public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzerInterface { private PolarDataService polarDataService; - public PolarSheetAnalyzer(PolarDataService polarDataService) { + public PolarSheetAnalyzerImpl(PolarDataService polarDataService) { this.polarDataService = polarDataService; } - public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + @Override + public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { PolarSheetsData data = polarDataService.getPolarSheetForBoatClass(boatClass); Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed(); int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); @@ -43,7 +45,8 @@ public class PolarSheetAnalyzer { return new KnotSpeedWithBearingImpl(optUpwindSpeed, new DegreeBearingImpl(optUpwindDeg)); } - public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + @Override + public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { PolarSheetsData data = polarDataService.getPolarSheetForBoatClass(boatClass); Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed(); int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 6cdb6210aad..7d8fb0aa77d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -23,6 +23,8 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.NoPolarDataAvailableException; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.aggregation.PolarFixAggregator; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzerInterface; +import com.sap.sailing.polars.analysis.impl.PolarSheetAnalyzerImpl; import com.sap.sailing.polars.caching.NoCacheEntryException; import com.sap.sailing.polars.caching.PolarFixCache; import com.sap.sailing.polars.caching.PolarFixCacheRaceInterval; @@ -45,11 +47,14 @@ public class PolarDataServiceImpl implements PolarDataService { private final PolarFixCache polarFixCache; private final PolarSheetPerBoatClassCache polarSheetPerBoatClassCache; + + private final PolarSheetAnalyzerInterface polarSheetAnalyzer; public PolarDataServiceImpl(Executor executor) { this.polarFixCache = new PolarFixCache(executor); this.polarSheetPerBoatClassCache = new PolarSheetPerBoatClassCache(this); polarFixCache.addListener(polarSheetPerBoatClassCache); + this.polarSheetAnalyzer = new PolarSheetAnalyzerImpl(this); } @Override @@ -199,4 +204,9 @@ public class PolarDataServiceImpl implements PolarDataService { return polarSheetPerBoatClassCache.keySet(); } + @Override + public PolarSheetAnalyzerInterface getAnalyzer() { + return polarSheetAnalyzer; + } + } From 0d4299e24ea81e101875414f141d989c8e1c48e8 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 26 Feb 2014 13:11:53 +0100 Subject: [PATCH 13/72] Renamed interface --- .../com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java | 6 +++--- .../src/com/sap/sailing/polars/PolarDataService.java | 4 ++-- ...rSheetAnalyzerInterface.java => PolarSheetAnalyzer.java} | 2 +- .../polars/analysis/impl/PolarSheetAnalyzerImpl.java | 4 ++-- .../com/sap/sailing/polars/impl/PolarDataServiceImpl.java | 6 +++--- 5 files changed, 11 insertions(+), 11 deletions(-) rename java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/{PolarSheetAnalyzerInterface.java => PolarSheetAnalyzer.java} (89%) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java index d95b65b0900..c9436b96080 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -12,7 +12,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; -import com.sap.sailing.polars.analysis.PolarSheetAnalyzerInterface; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.analysis.impl.PolarSheetAnalyzerImpl; import com.sap.sailing.polars.impl.PolarDataServiceImpl; @@ -29,7 +29,7 @@ public class PolarSheetAnalyzerTest { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, {} }; PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, settings.getWindStepping(), null); - PolarSheetAnalyzerInterface analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); + PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); SpeedWithBearing result = analyzer.getOptimalUpwindSpeedWithBearingFor(null, new KnotSpeedImpl(6)); Assert.assertEquals(0.5 * Math.cos(Math.toRadians(45)), result.getKnots(), 0.01); Assert.assertEquals(45, result.getBearing().getDegrees(), 0.01); @@ -49,7 +49,7 @@ public class PolarSheetAnalyzerTest { 0, 0, 0, 0, 0, 0, 0 }, {} }; PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, settings.getWindStepping(), null); - PolarSheetAnalyzerInterface analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); + PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); SpeedWithBearing result = analyzer.getOptimalDownwindSpeedWithBearingFor(null, new KnotSpeedImpl(6)); Assert.assertEquals(4.5 * Math.sin(Math.toRadians(85)), result.getKnots(), 0.01); Assert.assertEquals(175, result.getBearing().getDegrees(), 0.01); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index fa474495f50..16f03c99fc4 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -11,7 +11,7 @@ import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sailing.polars.analysis.PolarSheetAnalyzerInterface; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.data.PolarFix; /** @@ -74,6 +74,6 @@ public interface PolarDataService { */ Set getAllBoatClassesWithPolarSheetsAvailable(); - PolarSheetAnalyzerInterface getAnalyzer(); + PolarSheetAnalyzer getAnalyzer(); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzerInterface.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java similarity index 89% rename from java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzerInterface.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java index 96e8f7d8b28..e35327e863d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzerInterface.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java @@ -4,7 +4,7 @@ import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; -public interface PolarSheetAnalyzerInterface { +public interface PolarSheetAnalyzer { SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java index bb4cd7eb18e..6f895ab3b2e 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java @@ -7,7 +7,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.polars.PolarDataService; -import com.sap.sailing.polars.analysis.PolarSheetAnalyzerInterface; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; /** * Extracts typical measures from polar sheets. @@ -15,7 +15,7 @@ import com.sap.sailing.polars.analysis.PolarSheetAnalyzerInterface; * @author Frederik Petersen (D054528) * */ -public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzerInterface { +public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { private PolarDataService polarDataService; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 7d8fb0aa77d..aa0da753623 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -23,7 +23,7 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.NoPolarDataAvailableException; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.aggregation.PolarFixAggregator; -import com.sap.sailing.polars.analysis.PolarSheetAnalyzerInterface; +import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.analysis.impl.PolarSheetAnalyzerImpl; import com.sap.sailing.polars.caching.NoCacheEntryException; import com.sap.sailing.polars.caching.PolarFixCache; @@ -48,7 +48,7 @@ public class PolarDataServiceImpl implements PolarDataService { private final PolarFixCache polarFixCache; private final PolarSheetPerBoatClassCache polarSheetPerBoatClassCache; - private final PolarSheetAnalyzerInterface polarSheetAnalyzer; + private final PolarSheetAnalyzer polarSheetAnalyzer; public PolarDataServiceImpl(Executor executor) { this.polarFixCache = new PolarFixCache(executor); @@ -205,7 +205,7 @@ public class PolarDataServiceImpl implements PolarDataService { } @Override - public PolarSheetAnalyzerInterface getAnalyzer() { + public PolarSheetAnalyzer getAnalyzer() { return polarSheetAnalyzer; } From 78a4459f1ef6ef7f406f537d043993b62637e87d Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 12 Mar 2014 14:40:12 +0100 Subject: [PATCH 14/72] Polar fixes are now aggregates on the fly BUT at this point there are way to many timer threads. --- .../tracking/DynamicTrackedRegatta.java | 7 +- .../polars/test/PolarSheetAnalyzerTest.java | 4 +- .../polars/test/PolarSheetGenerationTest.java | 10 ++- .../sap/sailing/polars/PolarDataService.java | 5 ++ .../PolarFixAggregationWorker.java | 37 ++++++-- .../aggregation/PolarFixAggregator.java | 71 +++++++++++---- .../aggregation/PolarFixRaceInterval.java | 14 +++ .../SimplePolarFixRaceInterval.java | 29 ++++++ .../analysis/impl/PolarSheetAnalyzerImpl.java | 4 +- .../caching/PolarFixCacheRaceInterval.java | 90 ++++++++++++++++--- .../polars/caching/PolarFixCacheUpdater.java | 26 +----- .../polars/impl/PolarDataServiceImpl.java | 50 ++++++++--- .../server/test/MasterDataImportTest.java | 6 +- .../server/impl/RacingEventServiceImpl.java | 34 ++++++- 14 files changed, 307 insertions(+), 80 deletions(-) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixRaceInterval.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/SimplePolarFixRaceInterval.java diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRegatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRegatta.java index 9db1d0850a3..0f7f3ba28bf 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRegatta.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRegatta.java @@ -17,9 +17,10 @@ public interface DynamicTrackedRegatta extends TrackedRegatta { * @param raceDefinitionSetToUpdate * may be null which means that no update will be fired to any * {@link DynamicRaceDefinitionSet}. + * @param polarDataService */ - DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable sidelines, WindStore windStore, - long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed, - DynamicRaceDefinitionSet raceDefinitionSetToUpdate); + DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable sidelines, + WindStore windStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, + long millisecondsOverWhichToAverageSpeed, DynamicRaceDefinitionSet raceDefinitionSetToUpdate); } diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java index c9436b96080..41d2e1d0bf2 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -30,7 +30,7 @@ public class PolarSheetAnalyzerTest { PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, settings.getWindStepping(), null); PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); - SpeedWithBearing result = analyzer.getOptimalUpwindSpeedWithBearingFor(null, new KnotSpeedImpl(6)); + SpeedWithBearing result = analyzer.getOptimalUpwindSpeedWithBearingFor(null, new KnotSpeedImpl(4)); Assert.assertEquals(0.5 * Math.cos(Math.toRadians(45)), result.getKnots(), 0.01); Assert.assertEquals(45, result.getBearing().getDegrees(), 0.01); } @@ -50,7 +50,7 @@ public class PolarSheetAnalyzerTest { PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, settings.getWindStepping(), null); PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); - SpeedWithBearing result = analyzer.getOptimalDownwindSpeedWithBearingFor(null, new KnotSpeedImpl(6)); + SpeedWithBearing result = analyzer.getOptimalDownwindSpeedWithBearingFor(null, new KnotSpeedImpl(4)); Assert.assertEquals(4.5 * Math.sin(Math.toRadians(85)), result.getKnots(), 0.01); Assert.assertEquals(175, result.getBearing().getDegrees(), 0.01); } diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetGenerationTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetGenerationTest.java index 7327ab723e6..b67f8239e0c 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetGenerationTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetGenerationTest.java @@ -62,6 +62,7 @@ import com.sap.sailing.domain.tracking.impl.WindImpl; import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl; import com.sap.sailing.polars.aggregation.PolarFixAggregationWorker; import com.sap.sailing.polars.aggregation.PolarFixAggregator; +import com.sap.sailing.polars.aggregation.SimplePolarFixRaceInterval; import com.sap.sailing.polars.data.DataPointWithOriginInfo; import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.polars.data.impl.DataPointWithOriginInfoImpl; @@ -87,7 +88,8 @@ public class PolarSheetGenerationTest { TimePoint startTime = new MillisecondsTimePoint(9); TimePoint endTime = new MillisecondsTimePoint(80); // Only used for storing and exporting results in this test case: - PolarFixAggregator resultContainer = new PolarFixAggregator(new HashSet(), + PolarFixAggregator resultContainer = new PolarFixAggregator(new SimplePolarFixRaceInterval( + new HashSet()), settings, executor); BoatClass forelle = new BoatClassImpl("Forelle", true); @@ -95,7 +97,8 @@ public class PolarSheetGenerationTest { new BoatImpl("Schnelle Forelle", forelle, "GER000")); PolarFixAggregationWorker task = new PolarFixAggregationWorker(race, - resultContainer, startTime, endTime, competitor, settings); + resultContainer, startTime, endTime, + competitor, settings, null); executor.execute(task); double timeUntilTimeout = 1000; @@ -103,7 +106,8 @@ public class PolarSheetGenerationTest { Thread.sleep(100); timeUntilTimeout = timeUntilTimeout - 0.1; } - Set fixes = resultContainer.get(); + + Set fixes = resultContainer.getAggregationResultAsSingleList(); PolarSheetGenerator generator = new PolarSheetGenerator(fixes, settings); PolarSheetsData data = generator.generate(); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 16f03c99fc4..e6e7d47bf66 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -5,11 +5,13 @@ import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.data.PolarFix; @@ -76,4 +78,7 @@ public interface PolarDataService { PolarSheetAnalyzer getAnalyzer(); + void competitorPositionChanged(TimePoint timePoint, Competitor competitor, TrackedRace createdTrackedRace, + long delay); + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregationWorker.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregationWorker.java index f39460e5461..565d9993413 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregationWorker.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregationWorker.java @@ -50,9 +50,11 @@ public class PolarFixAggregationWorker implements Runnable { private PolarSheetGenerationSettings settings; - public PolarFixAggregationWorker(TrackedRace race, - PolarFixAggregator polarSheetGenerationWorker, TimePoint startTime, TimePoint endTime, - Competitor competitor, PolarSheetGenerationSettings settings) { + private Pair intervalBeginningAndEnd; + + public PolarFixAggregationWorker(TrackedRace race, PolarFixAggregator polarSheetGenerationWorker, + TimePoint startTime, TimePoint endTime, Competitor competitor, PolarSheetGenerationSettings settings, + Pair intervalBeginningAndEnd) { super(); this.race = race; this.polarSheetGenerationWorker = polarSheetGenerationWorker; @@ -60,9 +62,24 @@ public class PolarFixAggregationWorker implements Runnable { this.endTime = endTime; this.competitor = competitor; this.settings = settings; + this.intervalBeginningAndEnd = intervalBeginningAndEnd; optimizeStartTime(); optimizeEndTime(); checkIfRaceAborted(); + checkIfIntervalBeforeOrAfterRace(); + } + + private void checkIfIntervalBeforeOrAfterRace() { + if (intervalBeginningAndEnd != null) { + TimePoint intervalStart = intervalBeginningAndEnd.getA(); + if (intervalStart.after(endTime)) { + noConfidence = true; + } + TimePoint intervalEnd = intervalBeginningAndEnd.getB(); + if (intervalEnd.before(startTime)) { + noConfidence = true; + } + } } private void checkIfRaceAborted() { @@ -108,7 +125,17 @@ public class PolarFixAggregationWorker implements Runnable { GPSFixTrack track = race.getTrack(competitor); track.lockForRead(); try { - Iterator fixesIterator = track.getFixesIterator(startTime, true); + Iterator fixesIterator; + if (intervalBeginningAndEnd != null) { + TimePoint startOfIntervalToCheck = intervalBeginningAndEnd.getA().before(startTime) ? startTime + : intervalBeginningAndEnd.getA(); + TimePoint endOfIntervalToCheck = intervalBeginningAndEnd.getB().after(endTime) ? endTime + : intervalBeginningAndEnd.getB(); + fixesIterator = track.getFixes(startOfIntervalToCheck, true, endOfIntervalToCheck, true).iterator(); + } else { + // No timepoint interval given + fixesIterator = track.getFixesIterator(startTime, true); + } TimePoint lastConsideredTimePoint = null; if (finishedEarlyAtWaypoint != -1) { NavigableSet markPassings = race.getMarkPassings(competitor); @@ -159,7 +186,7 @@ public class PolarFixAggregationWorker implements Runnable { && windWithConfidence.getConfidence() >= settings.getMinimumWindConfidence()) { PolarFix polarFix = new PolarFixImpl(fix, race, track, windWithConfidence.getObject(), settings, windWithSourceIdStringPair.getA()); - polarSheetGenerationWorker.addPolarFix(polarFix); + polarSheetGenerationWorker.addPolarFix(race.getRaceIdentifier(), polarFix); } } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java index 01b60650700..63eafdf6788 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixAggregator.java @@ -1,6 +1,11 @@ package com.sap.sailing.polars.aggregation; +import java.util.ArrayList; +import java.util.HashMap; import java.util.HashSet; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; @@ -11,7 +16,9 @@ import java.util.concurrent.TimeoutException; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.data.PolarFix; @@ -23,40 +30,54 @@ import com.sap.sailing.polars.data.PolarFix; * @author D054528 Frederik Petersen * */ -public class PolarFixAggregator implements Future> { +public class PolarFixAggregator implements Future>> { private final Set workers; private final Executor executor; - private final Set fixes = new HashSet(); + private final Map> fixes = new HashMap>(); /** - * Will prepare the {@link PolarFixAggregationWorker}s per race & per competitor. This includes - * determining start and end time. + * Will prepare the {@link PolarFixAggregationWorker}s per race & per competitor. This includes determining start + * and end time. * - * @param trackedRaces + * @param updateInterval * from which the data is to be collected - * @param settings + * @param settings * @param executor * executes the tasks upon {@link #startPolarFixAggregation()} */ - public PolarFixAggregator(Set trackedRaces, PolarSheetGenerationSettings settings, Executor executor) { + public PolarFixAggregator(PolarFixRaceInterval updateInterval, PolarSheetGenerationSettings settings, + Executor executor) { this.executor = executor; workers = new HashSet(); - for (TrackedRace race : trackedRaces) { + for (Entry>> entry : updateInterval + .getCompetitorAndTimepointsForRace().entrySet()) { + TrackedRace race = entry.getKey(); TimePoint startTime = race.getStartOfRace(); TimePoint endTime = race.getEndOfRace(); if (endTime == null) { endTime = race.getTimePointOfNewestEvent(); } RaceDefinition raceDefinition = race.getRace(); - Iterable competitors = raceDefinition.getCompetitors(); - for (Competitor competitor : competitors) { - PolarFixAggregationWorker task = new PolarFixAggregationWorker( - race, this, startTime, endTime, competitor, settings); - workers.add(task); + Map> detailedInterval = entry.getValue(); + if (detailedInterval == null) { + Iterable competitors = raceDefinition.getCompetitors(); + for (Competitor competitor : competitors) { + PolarFixAggregationWorker task = new PolarFixAggregationWorker(race, this, startTime, endTime, + competitor, settings, null); + workers.add(task); + } + } else { + for (Entry> competitorEntry : detailedInterval.entrySet()) { + Competitor competitor = competitorEntry.getKey(); + Pair intervalBeginningAndEnd = competitorEntry.getValue(); + PolarFixAggregationWorker task = new PolarFixAggregationWorker(race, this, startTime, endTime, + competitor, settings, intervalBeginningAndEnd); + workers.add(task); + } } } @@ -76,9 +97,14 @@ public class PolarFixAggregator implements Future> { * results * wind's speed in knots */ - protected void addPolarFix(PolarFix polarFix) { + protected void addPolarFix(RegattaAndRaceIdentifier raceId, PolarFix polarFix) { synchronized (fixes) { - fixes.add(polarFix); + List existingList = fixes.get(raceId); + if (existingList == null) { + existingList = new ArrayList(); + fixes.put(raceId, existingList); + } + existingList.add(polarFix); } } @@ -109,7 +135,7 @@ public class PolarFixAggregator implements Future> { } @Override - public Set get() throws InterruptedException, ExecutionException { + public Map> get() throws InterruptedException, ExecutionException { while (!isDone()) { Thread.sleep(100); } @@ -117,7 +143,8 @@ public class PolarFixAggregator implements Future> { } @Override - public Set get(long timeout, TimeUnit unit) throws InterruptedException, ExecutionException, + public Map> get(long timeout, TimeUnit unit) throws InterruptedException, + ExecutionException, TimeoutException { long timeRun = 0; long timeoutInMillis = unit.toMillis(timeout); @@ -131,4 +158,14 @@ public class PolarFixAggregator implements Future> { return fixes; } + public Set getAggregationResultAsSingleList() throws InterruptedException, + ExecutionException { + Set fixes = new HashSet(); + Map> aggregationResult = get(); + for (List list : aggregationResult.values()) { + fixes.addAll(list); + } + return fixes; + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixRaceInterval.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixRaceInterval.java new file mode 100644 index 00000000000..6ce0d9f92ae --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/PolarFixRaceInterval.java @@ -0,0 +1,14 @@ +package com.sap.sailing.polars.aggregation; + +import java.util.Map; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.Util.Pair; +import com.sap.sailing.domain.tracking.TrackedRace; + +public interface PolarFixRaceInterval { + + Map>> getCompetitorAndTimepointsForRace(); + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/SimplePolarFixRaceInterval.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/SimplePolarFixRaceInterval.java new file mode 100644 index 00000000000..06ec6abb6f4 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/aggregation/SimplePolarFixRaceInterval.java @@ -0,0 +1,29 @@ +package com.sap.sailing.polars.aggregation; + +import java.util.HashMap; +import java.util.Map; +import java.util.Set; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.Util.Pair; +import com.sap.sailing.domain.tracking.TrackedRace; + +public class SimplePolarFixRaceInterval implements PolarFixRaceInterval { + + + private final Map>> intervalMap; + + public SimplePolarFixRaceInterval(Set races) { + intervalMap = new HashMap>>(); + for (TrackedRace race : races) { + intervalMap.put(race, null); + } + } + + @Override + public Map>> getCompetitorAndTimepointsForRace() { + return intervalMap; + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java index 6f895ab3b2e..f3980c083ad 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java @@ -24,7 +24,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } @Override - public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { PolarSheetsData data = polarDataService.getPolarSheetForBoatClass(boatClass); Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed(); int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); @@ -46,7 +46,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } @Override - public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { + public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { PolarSheetsData data = polarDataService.getPolarSheetForBoatClass(boatClass); Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed(); int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java index f26067ecda7..409191fc7b6 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java @@ -1,29 +1,97 @@ package com.sap.sailing.polars.caching; +import java.util.HashMap; import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.polars.aggregation.PolarFixRaceInterval; import com.sap.sailing.util.SmartFutureCache.UpdateInterval; -public class PolarFixCacheRaceInterval implements UpdateInterval { +public class PolarFixCacheRaceInterval implements UpdateInterval, PolarFixRaceInterval { - private final Set races; + private final Map>> competitorAndTimepointsForRace; - public PolarFixCacheRaceInterval(Set races) { - this.races = races; + public PolarFixCacheRaceInterval( + Map>> competitorAndTimepointsForRace) { + this.competitorAndTimepointsForRace = competitorAndTimepointsForRace; } - public Set getRaces() { - return races; + @Override + public Map>> getCompetitorAndTimepointsForRace() { + return competitorAndTimepointsForRace; } @Override public PolarFixCacheRaceInterval join(PolarFixCacheRaceInterval otherUpdateInterval) { - Set joined = new HashSet(); - joined.addAll(races); - joined.addAll(otherUpdateInterval.getRaces()); - return new PolarFixCacheRaceInterval(joined); + Map>> joinedInterval = new HashMap>>(); + Set racesContainedInEitherMap = new HashSet(); + for (TrackedRace race : competitorAndTimepointsForRace.keySet()) { + racesContainedInEitherMap.add(race); + } + Map>> otherUpdateIntervalInExplicitForm = otherUpdateInterval + .getCompetitorAndTimepointsForRace(); + for (TrackedRace race : otherUpdateIntervalInExplicitForm.keySet()) { + racesContainedInEitherMap.add(race); + } + + for (TrackedRace race : racesContainedInEitherMap) { + HashMap> joinedMap = new HashMap>(); + Map> mapOfTimpointCompetitorPairs = competitorAndTimepointsForRace + .get(race); + if (mapOfTimpointCompetitorPairs != null) { + joinedMap.putAll(mapOfTimpointCompetitorPairs); + } + Map> mapOfOtherTimpointCompetitorPairs = competitorAndTimepointsForRace + .get(race); + if (mapOfOtherTimpointCompetitorPairs != null) { + mergeMap(joinedMap, mapOfOtherTimpointCompetitorPairs); + } + joinedInterval.put(race, joinedMap); + } + return new PolarFixCacheRaceInterval(joinedInterval); } -} + private void mergeMap(HashMap> joinedMap, + Map> mapOfOtherTimpointCompetitorPairs) { + for (Entry> otherEntry : mapOfOtherTimpointCompetitorPairs.entrySet()) { + + Competitor competitor = otherEntry.getKey(); + Pair otherValue = otherEntry.getValue(); + Pair existingValue = joinedMap.get(competitor); + if (existingValue != null) { + Pair joinedTimepointInterval = joinTimepointIntervals(existingValue, otherValue); + joinedMap.put(competitor, joinedTimepointInterval); + } else { + joinedMap.put(otherEntry.getKey(), otherEntry.getValue()); + } + + } + } + + /** + * This method ignores a possible gap between the two intervals. + * + * @param existingValue + * @param otherValue + * @return + */ + private Pair joinTimepointIntervals(Pair existingValue, + Pair otherValue) { + TimePoint startExisting = existingValue.getA(); + TimePoint endExisting = existingValue.getB(); + TimePoint startOther = existingValue.getA(); + TimePoint endOther = existingValue.getB(); + + TimePoint startNew = startExisting.before(startOther) ? startExisting : startOther; + TimePoint endNew = endExisting.after(endOther) ? endExisting : endOther; + + return new Pair(startNew, endNew); + } + +} \ No newline at end of file diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java index ab1e286c993..9ebdad88aef 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java @@ -1,18 +1,14 @@ package com.sap.sailing.polars.caching; -import java.util.ArrayList; import java.util.HashMap; -import java.util.HashSet; import java.util.List; import java.util.Map; import java.util.Map.Entry; -import java.util.Set; import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; -import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.aggregation.PolarFixAggregator; import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.util.SmartFutureCache.CacheUpdater; @@ -29,25 +25,11 @@ public class PolarFixCacheUpdater implements @Override public Map> computeCacheUpdate(BoatClass key, PolarFixCacheRaceInterval updateInterval) throws Exception { - Set races = updateInterval.getRaces(); - Map aggregators = new HashMap(); - for (TrackedRace trackedRace : races) { - Set trackedRaces = new HashSet(); - trackedRaces.add(trackedRace); - PolarFixAggregator aggregator = new PolarFixAggregator(trackedRaces, - PolarSheetGenerationSettingsImpl.createStandardPolarSettings(), executor); - aggregator.startPolarFixAggregation(); - aggregators.put(trackedRace.getRaceIdentifier(), aggregator); - } - Map> resultMap = new HashMap>(); - for (Entry entry : aggregators.entrySet()) { - PolarFixAggregator aggregator = entry.getValue(); - Set result = aggregator.get(); - List resultList = new ArrayList(result); - resultMap.put(entry.getKey(), resultList); - } - return resultMap; + PolarFixAggregator aggregator = new PolarFixAggregator(updateInterval, + PolarSheetGenerationSettingsImpl.createStandardPolarSettings(), executor); + aggregator.startPolarFixAggregation(); + return aggregator.get(); } @Override diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index aa0da753623..702b7686e78 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -1,5 +1,6 @@ package com.sap.sailing.polars.impl; +import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -9,6 +10,7 @@ import java.util.concurrent.Executor; import java.util.logging.Logger; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; @@ -17,12 +19,15 @@ import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.PolarSheetsHistogramData; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.NoPolarDataAvailableException; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.aggregation.PolarFixAggregator; +import com.sap.sailing.polars.aggregation.SimplePolarFixRaceInterval; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.analysis.impl.PolarSheetAnalyzerImpl; import com.sap.sailing.polars.caching.NoCacheEntryException; @@ -164,14 +169,17 @@ public class PolarDataServiceImpl implements PolarDataService { } catch (NoCacheEntryException e) { // If there is no cache entry for at least one of the races: Aggregate for non-cached races. fixes = e.getCachedResultList(); - PolarFixAggregator aggregator = new PolarFixAggregator(e.getNotCached(), settings, executor); + PolarFixAggregator aggregator = new PolarFixAggregator( + new SimplePolarFixRaceInterval(e.getNotCached()), + settings, executor); aggregator.startPolarFixAggregation(); - fixes.addAll(aggregator.get()); + fixes = aggregator.getAggregationResultAsSingleList(); } } else { - PolarFixAggregator aggregator = new PolarFixAggregator(trackedRaces, settings, executor); + PolarFixAggregator aggregator = new PolarFixAggregator(new SimplePolarFixRaceInterval(trackedRaces), + settings, executor); aggregator.startPolarFixAggregation(); - fixes = aggregator.get(); + fixes = aggregator.getAggregationResultAsSingleList(); } PolarSheetGenerator generator = new PolarSheetGenerator(fixes, settings); return generator.generate(); @@ -179,9 +187,10 @@ public class PolarDataServiceImpl implements PolarDataService { @Override public void newRaceFinishedTracking(TrackedRace trackedRace) { - HashSet set = new HashSet(); - set.add(trackedRace); - polarFixCache.triggerUpdate(trackedRace.getRace().getBoatClass(), new PolarFixCacheRaceInterval(set)); + // TODO add a similar listening method, that deletes all cached data on removal of race + // HashSet set = new HashSet(); + // set.add(trackedRace); + // polarFixCache.triggerUpdate(trackedRace.getRace().getBoatClass(), new PolarFixCacheRaceInterval(set)); } @Override @@ -204,9 +213,28 @@ public class PolarDataServiceImpl implements PolarDataService { return polarSheetPerBoatClassCache.keySet(); } - @Override - public PolarSheetAnalyzer getAnalyzer() { - return polarSheetAnalyzer; - } + @Override + public PolarSheetAnalyzer getAnalyzer() { + return polarSheetAnalyzer; + } + + @Override + public void competitorPositionChanged(final TimePoint timePoint, final Competitor competitor, + final TrackedRace createdTrackedRace, long delay) { + + PolarFixCacheRaceInterval interval = new PolarFixCacheRaceInterval(createIntervalSpecification( + createdTrackedRace, competitor, timePoint)); + polarFixCache.triggerUpdate(createdTrackedRace.getRace().getBoatClass(), interval); + + } + + private Map>> createIntervalSpecification( + TrackedRace createdTrackedRace, Competitor competitor, TimePoint timePoint) { + HashMap>> result = new HashMap>>(); + HashMap> competitorMap = new HashMap>(); + competitorMap.put(competitor, new Pair(timePoint, timePoint)); + result.put(createdTrackedRace, competitorMap); + return result; + } } diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java index 81c28a6967a..9259a9c13bf 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java @@ -124,12 +124,12 @@ public class MasterDataImportTest { private Set storedLogUUIDs = new HashSet(); @After - public void tearDown() throws MalformedURLException, IOException, InterruptedException { + public void tearDown() { deleteAllDataFromDatabase(); } @Before - public void setUp() throws MalformedURLException, IOException, InterruptedException { + public void setUp() { deleteAllDataFromDatabase(); } @@ -140,7 +140,7 @@ public class MasterDataImportTest { return spyResource; } - private void deleteAllDataFromDatabase() throws MalformedURLException, IOException, InterruptedException { + private void deleteAllDataFromDatabase() { MongoDBService service = MongoDBConfiguration.getDefaultTestConfiguration().getService(); service.getDB().getWriteConcern().fsync(); service.getDB().dropDatabase(); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 2b03e263f49..8fd98d5b4db 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -1052,9 +1052,11 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed) { DynamicTrackedRegatta trackedRegatta = getOrCreateTrackedRegatta(getRegatta(raceIdentifier)); RaceDefinition race = getRace(raceIdentifier); - return trackedRegatta.createTrackedRace(race, Collections. emptyList(), windStore, delayToLiveInMillis, + final DynamicTrackedRace createdTrackedRace = trackedRegatta.createTrackedRace(race, + Collections. emptyList(), windStore, delayToLiveInMillis, millisecondsOverWhichToAverageWind, millisecondsOverWhichToAverageSpeed, /* raceDefinitionSetToUpdate */null); + return createdTrackedRace; } private void ensureRegattaIsObservedForDefaultLeaderboardAndAutoLeaderboardLinking( @@ -1081,6 +1083,9 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport * race. When a tracked race is removed, the {@link TrackedRaceReplicator} that was added as listener to that * tracked race is removed again. * + * A {@link PolarFixCacheUpdater} is added to every race so that polar fixes are aggregated when new GPS fixes + * arrive. + * * @author Axel Uhl (d043530) * */ @@ -1089,8 +1094,11 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport private final Map trackedRaceReplicators; + private final Map polarFixCacheUpdaters; + public RaceAdditionListener() { this.trackedRaceReplicators = new HashMap(); + this.polarFixCacheUpdaters = new HashMap(); } @Override @@ -1099,6 +1107,10 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport if (trackedRaceReplicator != null) { trackedRace.removeListener(trackedRaceReplicator); } + PolarFixCacheUpdater polarFixCacheUpdater = polarFixCacheUpdaters.remove(trackedRace); + if (polarFixCacheUpdater != null) { + trackedRace.removeListener(polarFixCacheUpdater); + } } @Override @@ -1119,9 +1131,29 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport TrackedRaceReplicator trackedRaceReplicator = new TrackedRaceReplicator(trackedRace); trackedRaceReplicators.put(trackedRace, trackedRaceReplicator); trackedRace.addListener(trackedRaceReplicator, /* fire wind already loaded */ true); + + PolarFixCacheUpdater polarFixCacheUpdater = new PolarFixCacheUpdater(trackedRace); + polarFixCacheUpdaters.put(trackedRace, polarFixCacheUpdater); + trackedRace.addListener(polarFixCacheUpdater); } } + private class PolarFixCacheUpdater extends AbstractRaceChangeListener { + + private final TrackedRace race; + + public PolarFixCacheUpdater(TrackedRace race) { + this.race = race; + } + + @Override + public void competitorPositionChanged(GPSFixMoving fix, Competitor item) { + polarDataService.competitorPositionChanged(fix.getTimePoint(), item, race, + race.getMillisecondsOverWhichToAverageSpeed()); + } + + } + private class TrackedRaceReplicator implements RaceChangeListener { private final TrackedRace trackedRace; From c35c4a487a0146155136af710e4c18c6b3f45308 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Mon, 17 Mar 2014 14:15:47 +0100 Subject: [PATCH 15/72] Added test for interval and fixed it --- .../META-INF/MANIFEST.MF | 2 + .../test/PolarFixCacheRaceIntervalTest.java | 89 +++++++++++++++++++ .../caching/PolarFixCacheRaceInterval.java | 8 +- .../polars/caching/PolarFixCacheUpdater.java | 1 + 4 files changed, 96 insertions(+), 4 deletions(-) create mode 100644 java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/caching/test/PolarFixCacheRaceIntervalTest.java diff --git a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF index 08b24d6da7b..d402c7e44f0 100644 --- a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF @@ -8,3 +8,5 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Fragment-Host: com.sap.sailing.polars Import-Package: com.sap.sailing.domain.test.mock, org.junit;version="4.8.2" +Require-Bundle: org.mockito.mockito-core;bundle-version="1.9.5", + org.hamcrest;bundle-version="1.1.0" diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/caching/test/PolarFixCacheRaceIntervalTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/caching/test/PolarFixCacheRaceIntervalTest.java new file mode 100644 index 00000000000..ebe8c33b491 --- /dev/null +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/caching/test/PolarFixCacheRaceIntervalTest.java @@ -0,0 +1,89 @@ +package com.sap.sailing.polars.caching.test; + +import static org.junit.Assert.assertThat; +import static org.mockito.Mockito.mock; +import static org.hamcrest.CoreMatchers.*; + +import java.util.Calendar; +import java.util.HashMap; +import java.util.Map; + +import org.junit.Test; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.impl.Util.Pair; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.polars.caching.PolarFixCacheRaceInterval; + +public class PolarFixCacheRaceIntervalTest { + + @Test + public void testIntervalJoin() { + TrackedRace mockedTrackedRace1 = mock(TrackedRace.class); + TrackedRace mockedTrackedRace2 = mock(TrackedRace.class); + + Competitor mockedCompetitor1 = mock(Competitor.class); + Competitor mockedCompetitor2 = mock(Competitor.class); + + Calendar calender = Calendar.getInstance(); + calender.set(2014, 3, 17, 16, 0, 0); + TimePoint timePoint1 = new MillisecondsTimePoint(calender.getTime()); + calender.set(2014, 3, 17, 16, 10, 0); + TimePoint timePoint2 = new MillisecondsTimePoint(calender.getTime()); + + PolarFixCacheRaceInterval interval1 = createInterval1( + mockedTrackedRace1, mockedTrackedRace2, mockedCompetitor1, + mockedCompetitor2, timePoint1, timePoint2); + + PolarFixCacheRaceInterval interval2 = createInterval2( + mockedTrackedRace1, mockedTrackedRace2, mockedCompetitor1, + timePoint2); + PolarFixCacheRaceInterval joined = interval1.join(interval2); + + Map>> joinedMap = joined + .getCompetitorAndTimepointsForRace(); + + Map> explicitIntervalForRace1 = joinedMap.get(mockedTrackedRace1); + assertThat(explicitIntervalForRace1, notNullValue(Map.class)); + Pair explicitIntervalForCompetitor1 = explicitIntervalForRace1.get(mockedCompetitor1); + assertThat(explicitIntervalForCompetitor1, notNullValue(Pair.class)); + + TimePoint start1 = explicitIntervalForCompetitor1.getA(); + TimePoint end1 = explicitIntervalForCompetitor1.getB(); + + assertThat(start1, is(timePoint1)); + assertThat(end1, is(timePoint2)); + + } + + private PolarFixCacheRaceInterval createInterval1( + TrackedRace mockedTrackedRace1, TrackedRace mockedTrackedRace2, + Competitor mockedCompetitor1, Competitor mockedCompetitor2, + TimePoint timePoint1, TimePoint timePoint2) { + Map>> competitorAndTimepointsForRace = new HashMap>>(); + HashMap> mapForRace1 = new HashMap>(); + mapForRace1.put(mockedCompetitor1, new Pair(timePoint1, timePoint1)); + competitorAndTimepointsForRace.put(mockedTrackedRace1, mapForRace1); + HashMap> mapForRace2 = new HashMap>(); + mapForRace2.put(mockedCompetitor2, new Pair(timePoint1, timePoint2)); + competitorAndTimepointsForRace.put(mockedTrackedRace2, mapForRace2); + PolarFixCacheRaceInterval interval1 = new PolarFixCacheRaceInterval( + competitorAndTimepointsForRace); + return interval1; + } + + private PolarFixCacheRaceInterval createInterval2( + TrackedRace mockedTrackedRace1, TrackedRace mockedTrackedRace2, + Competitor mockedCompetitor1, TimePoint timePoint2) { + Map>> competitorAndTimepointsForRace = new HashMap>>(); + HashMap> mapForRace1 = new HashMap>(); + mapForRace1.put(mockedCompetitor1, new Pair( + timePoint2, timePoint2)); + competitorAndTimepointsForRace.put(mockedTrackedRace1, mapForRace1); + PolarFixCacheRaceInterval interval2 = new PolarFixCacheRaceInterval( + competitorAndTimepointsForRace); + return interval2; + } +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java index 409191fc7b6..d5fa4fbf37e 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheRaceInterval.java @@ -47,7 +47,7 @@ public class PolarFixCacheRaceInterval implements UpdateInterval> mapOfOtherTimpointCompetitorPairs = competitorAndTimepointsForRace + Map> mapOfOtherTimpointCompetitorPairs = otherUpdateIntervalInExplicitForm .get(race); if (mapOfOtherTimpointCompetitorPairs != null) { mergeMap(joinedMap, mapOfOtherTimpointCompetitorPairs); @@ -68,7 +68,7 @@ public class PolarFixCacheRaceInterval implements UpdateInterval joinedTimepointInterval = joinTimepointIntervals(existingValue, otherValue); joinedMap.put(competitor, joinedTimepointInterval); } else { - joinedMap.put(otherEntry.getKey(), otherEntry.getValue()); + joinedMap.put(competitor, otherValue); } } @@ -85,8 +85,8 @@ public class PolarFixCacheRaceInterval implements UpdateInterval otherValue) { TimePoint startExisting = existingValue.getA(); TimePoint endExisting = existingValue.getB(); - TimePoint startOther = existingValue.getA(); - TimePoint endOther = existingValue.getB(); + TimePoint startOther = otherValue.getA(); + TimePoint endOther = otherValue.getB(); TimePoint startNew = startExisting.before(startOther) ? startExisting : startOther; TimePoint endNew = endExisting.after(endOther) ? endExisting : endOther; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java index 9ebdad88aef..71b3d27a267 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java @@ -27,6 +27,7 @@ public class PolarFixCacheUpdater implements PolarFixCacheRaceInterval updateInterval) throws Exception { PolarFixAggregator aggregator = new PolarFixAggregator(updateInterval, PolarSheetGenerationSettingsImpl.createStandardPolarSettings(), executor); + Thread.sleep(updateInterval.getCompetitorAndTimepointsForRace().keySet().iterator().next().getMillisecondsOverWhichToAverageSpeed()); aggregator.startPolarFixAggregation(); return aggregator.get(); From 336673b62e889701332c83daab9c0056eec3e2a6 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Mon, 17 Mar 2014 14:48:02 +0100 Subject: [PATCH 16/72] Fixed manual polar sheet generation --- .../src/com/sap/sailing/polars/caching/PolarFixCache.java | 5 +++-- .../com/sap/sailing/polars/impl/PolarDataServiceImpl.java | 2 +- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java index 61234ebef1f..d7aa74c2b01 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java @@ -54,8 +54,9 @@ public class PolarFixCache extends List resultForRace = result.get(trackedRace.getRaceIdentifier()); if (resultForRace == null) { notCached.add(trackedRace); - } - resultList.addAll(resultForRace); + } else { + resultList.addAll(resultForRace); + } } if (notCached.size() > 0) { throw new NoCacheEntryException(notCached, resultList); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 702b7686e78..d81af34a8fa 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -173,7 +173,7 @@ public class PolarDataServiceImpl implements PolarDataService { new SimplePolarFixRaceInterval(e.getNotCached()), settings, executor); aggregator.startPolarFixAggregation(); - fixes = aggregator.getAggregationResultAsSingleList(); + fixes.addAll(aggregator.getAggregationResultAsSingleList()); } } else { PolarFixAggregator aggregator = new PolarFixAggregator(new SimplePolarFixRaceInterval(trackedRaces), From e4c1a911eb23b47eeeecbd42d16d53756467843a Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Mon, 17 Mar 2014 15:27:25 +0100 Subject: [PATCH 17/72] Fixed polar fix cache update merge --- .../sap/sailing/polars/caching/PolarFixCacheUpdater.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java index 71b3d27a267..8733deaad29 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java @@ -42,7 +42,12 @@ public class PolarFixCacheUpdater implements newCacheValue.putAll(oldCacheValue); } for (Entry> newEntry : computedCacheUpdate.entrySet()) { - newCacheValue.put(newEntry.getKey(), newEntry.getValue()); + List oldList = newCacheValue.get(newEntry.getKey()); + if (oldList != null) { + oldList.addAll(newEntry.getValue()); + } else { + newCacheValue.put(newEntry.getKey(), newEntry.getValue()); + } } return newCacheValue; } From 78e48459e5a7bbc2de1c2b43e95b30fe017e9d92 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Tue, 18 Mar 2014 08:43:55 +0100 Subject: [PATCH 18/72] Fixed formatting --- .../src/com/sap/sailing/polars/caching/PolarFixCache.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java index d7aa74c2b01..c9306b315f2 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCache.java @@ -54,9 +54,9 @@ public class PolarFixCache extends List resultForRace = result.get(trackedRace.getRaceIdentifier()); if (resultForRace == null) { notCached.add(trackedRace); - } else { - resultList.addAll(resultForRace); - } + } else { + resultList.addAll(resultForRace); + } } if (notCached.size() > 0) { throw new NoCacheEntryException(notCached, resultList); From e4189be918053b905ac2e87ed386129385ddce6b Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Tue, 18 Mar 2014 08:49:24 +0100 Subject: [PATCH 19/72] Fixed formatting --- .../sailing/polars/caching/PolarFixCacheUpdater.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java index 8733deaad29..c119ed8bc69 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java @@ -42,12 +42,12 @@ public class PolarFixCacheUpdater implements newCacheValue.putAll(oldCacheValue); } for (Entry> newEntry : computedCacheUpdate.entrySet()) { - List oldList = newCacheValue.get(newEntry.getKey()); - if (oldList != null) { - oldList.addAll(newEntry.getValue()); - } else { - newCacheValue.put(newEntry.getKey(), newEntry.getValue()); - } + List oldList = newCacheValue.get(newEntry.getKey()); + if (oldList != null) { + oldList.addAll(newEntry.getValue()); + } else { + newCacheValue.put(newEntry.getKey(), newEntry.getValue()); + } } return newCacheValue; } From 99576e2ce405cd878a9f3730318c0af8e2773785 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Tue, 18 Mar 2014 08:50:05 +0100 Subject: [PATCH 20/72] Fixed polar fix cache update delay --- .../com/sap/sailing/polars/caching/PolarFixCacheUpdater.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java index c119ed8bc69..db782760f85 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/caching/PolarFixCacheUpdater.java @@ -27,7 +27,8 @@ public class PolarFixCacheUpdater implements PolarFixCacheRaceInterval updateInterval) throws Exception { PolarFixAggregator aggregator = new PolarFixAggregator(updateInterval, PolarSheetGenerationSettingsImpl.createStandardPolarSettings(), executor); - Thread.sleep(updateInterval.getCompetitorAndTimepointsForRace().keySet().iterator().next().getMillisecondsOverWhichToAverageSpeed()); + Thread.sleep(updateInterval.getCompetitorAndTimepointsForRace().keySet().iterator().next() + .getMillisecondsOverWhichToAverageSpeed() / 2); aggregator.startPolarFixAggregation(); return aggregator.get(); From 0451176ffae87b02ccff9569abd67c7faa5452de Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Tue, 18 Mar 2014 14:07:15 +0100 Subject: [PATCH 21/72] Added Linear Regression processor --- .../test/IncrementalLinearRegressionTest.java | 140 ++++++++++++++++++ .../polars/impl/PolarDataServiceImpl.java | 8 +- .../IncrementalLinearRegressionProcessor.java | 9 ++ .../NoDataHasBeenAddedException.java | 11 ++ .../IncrementalLeastSquaresProcessor.java | 100 +++++++++++++ 5 files changed, 264 insertions(+), 4 deletions(-) create mode 100644 java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NoDataHasBeenAddedException.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java new file mode 100644 index 00000000000..7c1437632cf --- /dev/null +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java @@ -0,0 +1,140 @@ +package com.sap.sailing.polars.regression.test; + +import static org.junit.Assert.assertThat; + +import org.hamcrest.number.IsCloseTo; +import org.junit.Test; + +import com.sap.sailing.polars.regression.IncrementalLinearRegressionProcessor; +import com.sap.sailing.polars.regression.NoDataHasBeenAddedException; +import com.sap.sailing.polars.regression.impl.IncrementalLeastSquaresProcessor; + +public class IncrementalLinearRegressionTest { + + private static double EPSILON = 1E-12; + + @Test + public void testRegressionWithOneDataPoint() throws NoDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + + double x1 = 3; + double y1 = 5; + processor.addMeasuredPoint(x1, y1); + + double y = processor.getY(x1); + assertThat(y, new IsCloseTo(y1, EPSILON)); + } + + @Test + public void testRegressionWithTwoDataPoints() throws NoDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + + double x1 = 3; + double y1 = 7; + processor.addMeasuredPoint(x1, y1); + + double x2 = 6; + double y2 = 14; + processor.addMeasuredPoint(x2, y2); + + double y = processor.getY(x1); + assertThat(y, new IsCloseTo(y1, EPSILON)); + + y = processor.getY(x2); + assertThat(y, new IsCloseTo(y2, EPSILON)); + + y = processor.getY(4.5); + assertThat(y, new IsCloseTo(10.5, EPSILON)); + } + + @Test + public void testRegressionWithEightDataPoints() throws NoDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + + // 6 Points on the line + for (int i = -500000; i < 500000; i++) { + processor.addMeasuredPoint(i, i); + } + + double y = processor.getY(-2); + assertThat(y, new IsCloseTo(-2, EPSILON)); + + // 2 Points off the line + processor.addMeasuredPoint(-30, 30); + processor.addMeasuredPoint(30, -30); + + y = processor.getY(-2); + assertThat(y, new IsCloseTo(-2, EPSILON)); + y = processor.getY(20); + assertThat(y, new IsCloseTo(20, EPSILON)); + } + + @Test + public void testRegressionWithZeroValueData() throws NoDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + + double x1 = 0; + double y1 = 0; + processor.addMeasuredPoint(x1, y1); + + double x2 = 5; + double y2 = 0; + processor.addMeasuredPoint(x2, y2); + + double y = processor.getY(x1); + assertThat(y, new IsCloseTo(y1, EPSILON)); + + y = processor.getY(x2); + assertThat(y, new IsCloseTo(y2, EPSILON)); + } + + @Test + public void testRegressionWithNegativeData() throws NoDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + + double x1 = -5; + double y1 = -5; + processor.addMeasuredPoint(x1, y1); + + double x2 = 5; + double y2 = 5; + processor.addMeasuredPoint(x2, y2); + + double y = processor.getY(x1); + assertThat(y, new IsCloseTo(y1, EPSILON)); + + y = processor.getY(x2); + assertThat(y, new IsCloseTo(y2, EPSILON)); + + y = processor.getY(0); + assertThat(y, new IsCloseTo(0, EPSILON)); + } + + @Test + public void testRegressionWithSmallValuedData() throws NoDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + + double x1 = 5E-9; + double y1 = 5E-9; + for (int i = 0; i < 1000; i++) { + double yTimesI = y1 * i; + double xTimesI = x1 * i; + processor.addMeasuredPoint(xTimesI, yTimesI); + double y = processor.getY(xTimesI); + assertThat(y, new IsCloseTo(yTimesI, EPSILON)); + } + // Add data that is off the line but offsetting each other + processor.addMeasuredPoint(-x1, y1); + processor.addMeasuredPoint(x1, -y1); + + double y = processor.getY(0); + assertThat(y, new IsCloseTo(0, EPSILON)); + } + + @Test(expected = NoDataHasBeenAddedException.class) + public void testNoDataExceptionThrowing() throws NoDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + processor.getY(15); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index d81af34a8fa..ea20ddc2e98 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -32,7 +32,6 @@ import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.analysis.impl.PolarSheetAnalyzerImpl; import com.sap.sailing.polars.caching.NoCacheEntryException; import com.sap.sailing.polars.caching.PolarFixCache; -import com.sap.sailing.polars.caching.PolarFixCacheRaceInterval; import com.sap.sailing.polars.caching.PolarSheetPerBoatClassCache; import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.polars.generation.PolarSheetGenerator; @@ -221,10 +220,11 @@ public class PolarDataServiceImpl implements PolarDataService { @Override public void competitorPositionChanged(final TimePoint timePoint, final Competitor competitor, final TrackedRace createdTrackedRace, long delay) { + // TODO build datamining pipeline for the cache - PolarFixCacheRaceInterval interval = new PolarFixCacheRaceInterval(createIntervalSpecification( - createdTrackedRace, competitor, timePoint)); - polarFixCache.triggerUpdate(createdTrackedRace.getRace().getBoatClass(), interval); + // PolarFixCacheRaceInterval interval = new PolarFixCacheRaceInterval(createIntervalSpecification( + // createdTrackedRace, competitor, timePoint)); + // polarFixCache.triggerUpdate(createdTrackedRace.getRace().getBoatClass(), interval); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java new file mode 100644 index 00000000000..608dd3519d5 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java @@ -0,0 +1,9 @@ +package com.sap.sailing.polars.regression; + +public interface IncrementalLinearRegressionProcessor { + + double getY(double x) throws NoDataHasBeenAddedException; + + void addMeasuredPoint(double x, double y); + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NoDataHasBeenAddedException.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NoDataHasBeenAddedException.java new file mode 100644 index 00000000000..4c200d04177 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NoDataHasBeenAddedException.java @@ -0,0 +1,11 @@ +package com.sap.sailing.polars.regression; + +public class NoDataHasBeenAddedException extends Exception { + + private static final long serialVersionUID = 4134116458606258315L; + + public NoDataHasBeenAddedException() { + super("No data has been added to the Processor yet."); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java new file mode 100644 index 00000000000..1fe7a1322e1 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java @@ -0,0 +1,100 @@ +package com.sap.sailing.polars.regression.impl; + +import com.sap.sailing.polars.regression.IncrementalLinearRegressionProcessor; +import com.sap.sailing.polars.regression.NoDataHasBeenAddedException; + +/** + * Allows a linear regression of a 2 dimensional and growing dataset. It has a complexity of O(1) + * + * The algorithm and variable naming is similar to the german wikipedia + * article + * + * The difference is that some parts of the equation are memorized as fields and updated incrementally. + * + * + * @author Frederik Petersen (D054528) + * + */ +public class IncrementalLeastSquaresProcessor implements IncrementalLinearRegressionProcessor { + + private double alpha1 = 0; + + private double alpha0 = 0; + + private double partA = 0; + + private double partB = 0; + + private double meanOfX = 0; + + private double meanOfY = 0; + + private int numberOfPointsAdded = 0; + + @Override + public double getY(double x) throws NoDataHasBeenAddedException { + if (numberOfPointsAdded < 1) { + throw new NoDataHasBeenAddedException(); + } + + return alpha0 + alpha1 * x; + } + + @Override + public void addMeasuredPoint(double x, double y) { + numberOfPointsAdded++; + + calculatePartA(x, y); + calculatePartB(x, y); + + addToArithmeticMeanOfX(x); + addToArithmeticMeanOfY(y); + + calculateFactor1(); + calculateFactor2(); + } + + private void calculateFactor2() { + alpha0 = meanOfY - alpha1 * meanOfX; + } + + private void calculateFactor1() { + double numerator = calculateNumeratorOfFactor1(); + double denominator = calculateDenominatorOfFactor1(); + if (denominator == 0) { + alpha1 = 0; + } else { + alpha1 = numerator / denominator; + } + } + + private double calculateNumeratorOfFactor1() { + return partA - numberOfPointsAdded * meanOfX * meanOfY; + } + + private double calculateDenominatorOfFactor1() { + return partB - numberOfPointsAdded * meanOfX * meanOfX; + } + + private void addToArithmeticMeanOfY(double y) { + meanOfY = addToMean(y, meanOfY); + } + + private void addToArithmeticMeanOfX(double x) { + meanOfX = addToMean(x, meanOfX); + } + + private double addToMean(double z, double oldMean) { + return oldMean + (z - oldMean) / numberOfPointsAdded; + } + + private void calculatePartB(double x, double y) { + partB = partB + x * x; + } + + private void calculatePartA(double x, double y) { + partA = partA + x * y; + } + +} From 7f3ead381c7bff2d7c0ca9e857c838eb0a99172b Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 19 Mar 2014 13:30:27 +0100 Subject: [PATCH 22/72] Enhanced test for linear regression and added apache regression class --- .../test/IncrementalLinearRegressionTest.java | 150 ++++++++++++++---- .../META-INF/MANIFEST.MF | 3 +- .../IncrementalLinearRegressionProcessor.java | 6 +- .../NoDataHasBeenAddedException.java | 11 -- .../NotEnoughDataHasBeenAddedException.java | 11 ++ .../impl/ApacheSimpleRegressionWrapper.java | 40 +++++ .../IncrementalLeastSquaresProcessor.java | 18 ++- .../race-analysis-p2-ide-local.target | 23 +-- .../race-analysis-p2-ide-p2admin.target | 1 + .../race-analysis-p2-maven-central.target | 1 + .../race-analysis-p2-maven-local.target | 1 + 11 files changed, 206 insertions(+), 59 deletions(-) delete mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NoDataHasBeenAddedException.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NotEnoughDataHasBeenAddedException.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/ApacheSimpleRegressionWrapper.java diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java index 7c1437632cf..1fc597e91fd 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java @@ -1,33 +1,31 @@ package com.sap.sailing.polars.regression.test; import static org.junit.Assert.assertThat; +import static org.junit.Assert.assertTrue; import org.hamcrest.number.IsCloseTo; import org.junit.Test; import com.sap.sailing.polars.regression.IncrementalLinearRegressionProcessor; -import com.sap.sailing.polars.regression.NoDataHasBeenAddedException; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; +import com.sap.sailing.polars.regression.impl.ApacheSimpleRegressionWrapper; import com.sap.sailing.polars.regression.impl.IncrementalLeastSquaresProcessor; public class IncrementalLinearRegressionTest { private static double EPSILON = 1E-12; - @Test - public void testRegressionWithOneDataPoint() throws NoDataHasBeenAddedException { + private static double VAGUE_EPSILON = 0.1; + + private IncrementalLinearRegressionProcessor createRegressionProcessor() { IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); - - double x1 = 3; - double y1 = 5; - processor.addMeasuredPoint(x1, y1); - - double y = processor.getY(x1); - assertThat(y, new IsCloseTo(y1, EPSILON)); + ApacheSimpleRegressionWrapper apacheProcessor = new ApacheSimpleRegressionWrapper(); + return processor; } @Test - public void testRegressionWithTwoDataPoints() throws NoDataHasBeenAddedException { - IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + public void testRegressionWithTwoDataPoints() throws NotEnoughDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); double x1 = 3; double y1 = 7; @@ -48,11 +46,10 @@ public class IncrementalLinearRegressionTest { } @Test - public void testRegressionWithEightDataPoints() throws NoDataHasBeenAddedException { - IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + public void testRegressionWithMultipleDataPoints() throws NotEnoughDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); - // 6 Points on the line - for (int i = -500000; i < 500000; i++) { + for (int i = -50; i < 51; i++) { processor.addMeasuredPoint(i, i); } @@ -60,18 +57,35 @@ public class IncrementalLinearRegressionTest { assertThat(y, new IsCloseTo(-2, EPSILON)); // 2 Points off the line - processor.addMeasuredPoint(-30, 30); - processor.addMeasuredPoint(30, -30); + processor.addMeasuredPoint(-8, -3); + processor.addMeasuredPoint(8, -3); + y = processor.getY(-2); - assertThat(y, new IsCloseTo(-2, EPSILON)); + assertThat(y, new IsCloseTo(-2, VAGUE_EPSILON)); y = processor.getY(20); - assertThat(y, new IsCloseTo(20, EPSILON)); + assertThat(y, new IsCloseTo(20, VAGUE_EPSILON)); } @Test - public void testRegressionWithZeroValueData() throws NoDataHasBeenAddedException { - IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + public void testRegressionWithABigAmountOfDataPoints() throws NotEnoughDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); + + for (int i = -1000; i < 1000; i++) { + // For every even number + 1, else - 1 + double y = i % 2 == 0 ? i + 1 : i - 1; + processor.addMeasuredPoint(i, y); + } + + double y = processor.getY(-2); + assertThat(y, new IsCloseTo(-2, VAGUE_EPSILON)); + y = processor.getY(20); + assertThat(y, new IsCloseTo(20, VAGUE_EPSILON)); + } + + @Test + public void testRegressionWithZeroValueData() throws NotEnoughDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); double x1 = 0; double y1 = 0; @@ -89,8 +103,8 @@ public class IncrementalLinearRegressionTest { } @Test - public void testRegressionWithNegativeData() throws NoDataHasBeenAddedException { - IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + public void testRegressionWithNegativeData() throws NotEnoughDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); double x1 = -5; double y1 = -5; @@ -111,8 +125,8 @@ public class IncrementalLinearRegressionTest { } @Test - public void testRegressionWithSmallValuedData() throws NoDataHasBeenAddedException { - IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + public void testRegressionWithSmallValuedData() throws NotEnoughDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); double x1 = 5E-9; double y1 = 5E-9; @@ -120,8 +134,10 @@ public class IncrementalLinearRegressionTest { double yTimesI = y1 * i; double xTimesI = x1 * i; processor.addMeasuredPoint(xTimesI, yTimesI); - double y = processor.getY(xTimesI); - assertThat(y, new IsCloseTo(yTimesI, EPSILON)); + if (i > 0) { + double y = processor.getY(xTimesI); + assertThat(y, new IsCloseTo(yTimesI, EPSILON)); + } } // Add data that is off the line but offsetting each other processor.addMeasuredPoint(-x1, y1); @@ -131,10 +147,82 @@ public class IncrementalLinearRegressionTest { assertThat(y, new IsCloseTo(0, EPSILON)); } - @Test(expected = NoDataHasBeenAddedException.class) - public void testNoDataExceptionThrowing() throws NoDataHasBeenAddedException { - IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); + @Test(expected = NotEnoughDataHasBeenAddedException.class) + public void testNoDataExceptionThrowing() throws NotEnoughDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); processor.getY(15); } + @Test(expected = NotEnoughDataHasBeenAddedException.class) + public void testRegressionWithOneDataPoint() throws NotEnoughDataHasBeenAddedException { + IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); + + double x1 = 3; + double y1 = 5; + processor.addMeasuredPoint(x1, y1); + + processor.getY(x1); + } + + /* + * The following tests and the data are taken from: org.apache.commons.math3.stat.regression.SimpleRegressionTest + * + * http://svn.apache.org/viewvc/commons/proper/math/trunk/src/test/java/org/apache/commons/math3/stat/regression/ + * SimpleRegressionTest.java?view=markup + * + * They are adapted to use the IncrementalLinearRegressionProcessor, but the data and the asserts were kept as is + */ + + /* + * NIST "Norris" refernce data set from + * http://www.itl.nist.gov/div898/strd/lls/data/LINKS/DATA/Norris.dat + * Strangely, order is {y,x} + */ + private double[][] data = { { 0.1, 0.2 }, {338.8, 337.4 }, {118.1, 118.2 }, + {888.0, 884.6 }, {9.2, 10.1 }, {228.1, 226.5 }, {668.5, 666.3 }, {998.5, 996.3 }, + {449.1, 448.6 }, {778.9, 777.0 }, {559.2, 558.2 }, {0.3, 0.4 }, {0.1, 0.6 }, {778.1, 775.5 }, + {668.8, 666.9 }, {339.3, 338.0 }, {448.9, 447.5 }, {10.8, 11.6 }, {557.7, 556.0 }, + {228.3, 228.1 }, {998.0, 995.8 }, {888.8, 887.6 }, {119.6, 120.2 }, {0.3, 0.3 }, + {0.6, 0.3 }, {557.6, 556.8 }, {339.3, 339.1 }, {888.0, 887.2 }, {998.5, 999.0 }, + {778.9, 779.0 }, {10.2, 11.1 }, {117.6, 118.3 }, {228.9, 229.2 }, {668.4, 669.1 }, + {449.2, 448.9 }, {0.2, 0.5 } + }; + + + @Test + public void testNorris() throws NotEnoughDataHasBeenAddedException { + IncrementalLinearRegressionProcessor regression = createRegressionProcessor(); + for (int i = 0; i < data.length; i++) { + regression.addMeasuredPoint(data[i][1], data[i][0]); + } + // Tests against certified values from + // http://www.itl.nist.gov/div898/strd/lls/data/LINKS/DATA/Norris.dat + assertThat(regression.getSlope(), new IsCloseTo(1.00211681802045, EPSILON)); + assertThat(regression.getIntercept(), new IsCloseTo(-0.262323073774029, EPSILON)); + // ------------ End certified data tests + assertThat(regression.getY(0), new IsCloseTo(-0.262323073774029, EPSILON)); + assertThat(regression.getY(1), new IsCloseTo(1.00211681802045 - 0.26232307377402, EPSILON)); + } + + @Test + public void testPerfect() { + IncrementalLinearRegressionProcessor regression = createRegressionProcessor(); + int n = 100; + for (int i = 0; i < n; i++) { + regression.addMeasuredPoint(((double) i) / (n - 1), i); + } + assertTrue(regression.getSlope() > 0); + } + + @Test + public void testPerfectNegative() { + IncrementalLinearRegressionProcessor regression = createRegressionProcessor(); + int n = 100; + for (int i = 0; i < n; i++) { + regression.addMeasuredPoint(-((double) i) / (n - 1), i); + } + assertTrue(regression.getSlope() < 0); + } + + } diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index f5a36ce37ff..97008f9faa2 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -14,6 +14,7 @@ Export-Package: Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", - com.sap.sailing.domain + com.sap.sailing.domain, + org.apache.commons.math;bundle-version="2.1.0" Import-Package: com.sap.sailing.util diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java index 608dd3519d5..abda8ff6699 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java @@ -2,8 +2,12 @@ package com.sap.sailing.polars.regression; public interface IncrementalLinearRegressionProcessor { - double getY(double x) throws NoDataHasBeenAddedException; + double getY(double x) throws NotEnoughDataHasBeenAddedException; void addMeasuredPoint(double x, double y); + double getSlope(); + + double getIntercept(); + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NoDataHasBeenAddedException.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NoDataHasBeenAddedException.java deleted file mode 100644 index 4c200d04177..00000000000 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NoDataHasBeenAddedException.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.sap.sailing.polars.regression; - -public class NoDataHasBeenAddedException extends Exception { - - private static final long serialVersionUID = 4134116458606258315L; - - public NoDataHasBeenAddedException() { - super("No data has been added to the Processor yet."); - } - -} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NotEnoughDataHasBeenAddedException.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NotEnoughDataHasBeenAddedException.java new file mode 100644 index 00000000000..f6a60d87135 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NotEnoughDataHasBeenAddedException.java @@ -0,0 +1,11 @@ +package com.sap.sailing.polars.regression; + +public class NotEnoughDataHasBeenAddedException extends Exception { + + private static final long serialVersionUID = 4134116458606258315L; + + public NotEnoughDataHasBeenAddedException() { + super("No data has been added to the Processor yet. At least to value-pairs have to be added."); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/ApacheSimpleRegressionWrapper.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/ApacheSimpleRegressionWrapper.java new file mode 100644 index 00000000000..a81a1add95d --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/ApacheSimpleRegressionWrapper.java @@ -0,0 +1,40 @@ +package com.sap.sailing.polars.regression.impl; + +import org.apache.commons.math.stat.regression.SimpleRegression; + +import com.sap.sailing.polars.regression.IncrementalLinearRegressionProcessor; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; + +public class ApacheSimpleRegressionWrapper implements IncrementalLinearRegressionProcessor { + + private final SimpleRegression regression; + + public ApacheSimpleRegressionWrapper() { + regression = new SimpleRegression(); + } + + @Override + public double getY(double x) throws NotEnoughDataHasBeenAddedException { + double prediction = regression.predict(x); + if (Double.isNaN(prediction)) { + throw new NotEnoughDataHasBeenAddedException(); + } + return prediction; + } + + @Override + public void addMeasuredPoint(double x, double y) { + regression.addData(x, y); + } + + @Override + public double getSlope() { + return regression.getSlope(); + } + + @Override + public double getIntercept() { + return regression.getIntercept(); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java index 1fe7a1322e1..a73d8b23c03 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java @@ -1,7 +1,7 @@ package com.sap.sailing.polars.regression.impl; import com.sap.sailing.polars.regression.IncrementalLinearRegressionProcessor; -import com.sap.sailing.polars.regression.NoDataHasBeenAddedException; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; /** * Allows a linear regression of a 2 dimensional and growing dataset. It has a complexity of O(1) @@ -33,9 +33,9 @@ public class IncrementalLeastSquaresProcessor implements IncrementalLinearRegres private int numberOfPointsAdded = 0; @Override - public double getY(double x) throws NoDataHasBeenAddedException { - if (numberOfPointsAdded < 1) { - throw new NoDataHasBeenAddedException(); + public double getY(double x) throws NotEnoughDataHasBeenAddedException { + if (numberOfPointsAdded < 2) { + throw new NotEnoughDataHasBeenAddedException(); } return alpha0 + alpha1 * x; @@ -97,4 +97,14 @@ public class IncrementalLeastSquaresProcessor implements IncrementalLinearRegres partA = partA + x * y; } + @Override + public double getSlope() { + return alpha1; + } + + @Override + public double getIntercept() { + return alpha0; + } + } diff --git a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-local.target b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-local.target index 11feaed4447..9c92db46d25 100755 --- a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-local.target +++ b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-local.target @@ -1,5 +1,5 @@ - + @@ -14,6 +14,7 @@ + @@ -41,16 +42,16 @@ - - - - - - - - - - + + + + + + + + + + diff --git a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-p2admin.target b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-p2admin.target index 227aeade65f..912f2428870 100644 --- a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-p2admin.target +++ b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-p2admin.target @@ -27,6 +27,7 @@ + diff --git a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-central.target b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-central.target index ccf84d4dd34..209aff37290 100644 --- a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-central.target +++ b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-central.target @@ -33,6 +33,7 @@ + diff --git a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-local.target b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-local.target index c003cfd6b9e..25da4c84e5a 100644 --- a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-local.target +++ b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-local.target @@ -33,6 +33,7 @@ + From f6d1c32a350625e2f2fd1f5b7482972c0729bfb8 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 19 Mar 2014 13:36:25 +0100 Subject: [PATCH 23/72] Removed apache math library --- .../test/IncrementalLinearRegressionTest.java | 2 - .../META-INF/MANIFEST.MF | 3 +- .../polars/impl/PolarDataServiceImpl.java | 20 +++++----- .../impl/ApacheSimpleRegressionWrapper.java | 40 ------------------- .../race-analysis-p2-ide-local.target | 1 - .../race-analysis-p2-ide-p2admin.target | 1 - .../race-analysis-p2-maven-central.target | 1 - .../race-analysis-p2-maven-local.target | 1 - 8 files changed, 11 insertions(+), 58 deletions(-) delete mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/ApacheSimpleRegressionWrapper.java diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java index 1fc597e91fd..10a1ff17280 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java @@ -8,7 +8,6 @@ import org.junit.Test; import com.sap.sailing.polars.regression.IncrementalLinearRegressionProcessor; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; -import com.sap.sailing.polars.regression.impl.ApacheSimpleRegressionWrapper; import com.sap.sailing.polars.regression.impl.IncrementalLeastSquaresProcessor; public class IncrementalLinearRegressionTest { @@ -19,7 +18,6 @@ public class IncrementalLinearRegressionTest { private IncrementalLinearRegressionProcessor createRegressionProcessor() { IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor(); - ApacheSimpleRegressionWrapper apacheProcessor = new ApacheSimpleRegressionWrapper(); return processor; } diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index 97008f9faa2..f5a36ce37ff 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -14,7 +14,6 @@ Export-Package: Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", - com.sap.sailing.domain, - org.apache.commons.math;bundle-version="2.1.0" + com.sap.sailing.domain Import-Package: com.sap.sailing.util diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index ea20ddc2e98..c195714c028 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -1,6 +1,5 @@ package com.sap.sailing.polars.impl; -import java.util.HashMap; import java.util.HashSet; import java.util.List; import java.util.Map; @@ -22,7 +21,6 @@ import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; -import com.sap.sailing.domain.common.impl.Util.Pair; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.NoPolarDataAvailableException; import com.sap.sailing.polars.PolarDataService; @@ -228,13 +226,15 @@ public class PolarDataServiceImpl implements PolarDataService { } - private Map>> createIntervalSpecification( - TrackedRace createdTrackedRace, Competitor competitor, TimePoint timePoint) { - HashMap>> result = new HashMap>>(); - HashMap> competitorMap = new HashMap>(); - competitorMap.put(competitor, new Pair(timePoint, timePoint)); - result.put(createdTrackedRace, competitorMap); - return result; - } + // private Map>> createIntervalSpecification( + // TrackedRace createdTrackedRace, Competitor competitor, TimePoint timePoint) { + // HashMap>> result = new HashMap>>(); + // HashMap> competitorMap = new HashMap>(); + // competitorMap.put(competitor, new Pair(timePoint, timePoint)); + // result.put(createdTrackedRace, competitorMap); + // return result; + // } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/ApacheSimpleRegressionWrapper.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/ApacheSimpleRegressionWrapper.java deleted file mode 100644 index a81a1add95d..00000000000 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/ApacheSimpleRegressionWrapper.java +++ /dev/null @@ -1,40 +0,0 @@ -package com.sap.sailing.polars.regression.impl; - -import org.apache.commons.math.stat.regression.SimpleRegression; - -import com.sap.sailing.polars.regression.IncrementalLinearRegressionProcessor; -import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; - -public class ApacheSimpleRegressionWrapper implements IncrementalLinearRegressionProcessor { - - private final SimpleRegression regression; - - public ApacheSimpleRegressionWrapper() { - regression = new SimpleRegression(); - } - - @Override - public double getY(double x) throws NotEnoughDataHasBeenAddedException { - double prediction = regression.predict(x); - if (Double.isNaN(prediction)) { - throw new NotEnoughDataHasBeenAddedException(); - } - return prediction; - } - - @Override - public void addMeasuredPoint(double x, double y) { - regression.addData(x, y); - } - - @Override - public double getSlope() { - return regression.getSlope(); - } - - @Override - public double getIntercept() { - return regression.getIntercept(); - } - -} diff --git a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-local.target b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-local.target index 9c92db46d25..52d2a4a9bb1 100755 --- a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-local.target +++ b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-local.target @@ -14,7 +14,6 @@ - diff --git a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-p2admin.target b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-p2admin.target index 912f2428870..227aeade65f 100644 --- a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-p2admin.target +++ b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-ide-p2admin.target @@ -27,7 +27,6 @@ - diff --git a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-central.target b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-central.target index 209aff37290..ccf84d4dd34 100644 --- a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-central.target +++ b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-central.target @@ -33,7 +33,6 @@ - diff --git a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-local.target b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-local.target index 25da4c84e5a..c003cfd6b9e 100644 --- a/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-local.target +++ b/java/com.sap.sailing.targetplatform/definitions/race-analysis-p2-maven-local.target @@ -33,7 +33,6 @@ - From 5065f7437b8da30d82513d0bd8b58519e366069c Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Thu, 20 Mar 2014 13:12:44 +0100 Subject: [PATCH 24/72] Started working on porting... ...the polar data processing to use the dataming framework --- .../META-INF/MANIFEST.MF | 4 +- .../sap/sailing/polars/PolarDataService.java | 5 +- .../polars/impl/PolarDataServiceImpl.java | 11 ++- .../mining/AbstractEnrichingProcessor.java | 35 ++++++++ .../mining/GPSFixMovingWithPolarContext.java | 44 +++++++++++ .../sailing/polars/mining/PolarDataMiner.java | 73 +++++++++++++++++ .../polars/mining/PolarFixFilterCriteria.java | 79 +++++++++++++++++++ .../server/impl/RacingEventServiceImpl.java | 3 +- 8 files changed, 245 insertions(+), 9 deletions(-) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index f5a36ce37ff..a50a27adb4a 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -14,6 +14,8 @@ Export-Package: Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", - com.sap.sailing.domain + com.sap.sailing.domain, + com.sap.sse.datamining, + com.sap.sse.datamining.shared;bundle-version="1.0.0" Import-Package: com.sap.sailing.util diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index e6e7d47bf66..1709081aa03 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -11,7 +11,7 @@ import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.data.PolarFix; @@ -78,7 +78,6 @@ public interface PolarDataService { PolarSheetAnalyzer getAnalyzer(); - void competitorPositionChanged(TimePoint timePoint, Competitor competitor, TrackedRace createdTrackedRace, - long delay); + void competitorPositionChanged(GPSFixMoving fix, Competitor competitor, TrackedRace createdTrackedRace); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index c195714c028..e21e1293f0d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -18,9 +18,9 @@ import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.PolarSheetsHistogramData; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.NoPolarDataAvailableException; import com.sap.sailing.polars.PolarDataService; @@ -33,6 +33,7 @@ import com.sap.sailing.polars.caching.PolarFixCache; import com.sap.sailing.polars.caching.PolarSheetPerBoatClassCache; import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.polars.generation.PolarSheetGenerator; +import com.sap.sailing.polars.mining.PolarDataMiner; import com.sap.sailing.util.SmartFutureCache; /** @@ -52,11 +53,14 @@ public class PolarDataServiceImpl implements PolarDataService { private final PolarSheetAnalyzer polarSheetAnalyzer; + private final PolarDataMiner polarDataMiner; + public PolarDataServiceImpl(Executor executor) { this.polarFixCache = new PolarFixCache(executor); this.polarSheetPerBoatClassCache = new PolarSheetPerBoatClassCache(this); polarFixCache.addListener(polarSheetPerBoatClassCache); this.polarSheetAnalyzer = new PolarSheetAnalyzerImpl(this); + this.polarDataMiner = new PolarDataMiner(); } @Override @@ -216,8 +220,9 @@ public class PolarDataServiceImpl implements PolarDataService { } @Override - public void competitorPositionChanged(final TimePoint timePoint, final Competitor competitor, - final TrackedRace createdTrackedRace, long delay) { + public void competitorPositionChanged(final GPSFixMoving fix, final Competitor competitor, + final TrackedRace createdTrackedRace) { + polarDataMiner.addFix(fix, competitor, createdTrackedRace); // TODO build datamining pipeline for the cache // PolarFixCacheRaceInterval interval = new PolarFixCacheRaceInterval(createIntervalSpecification( diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java new file mode 100644 index 00000000000..855b2442613 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java @@ -0,0 +1,35 @@ +package com.sap.sailing.polars.mining; + +import java.util.Collection; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutorService; + +import com.sap.sse.datamining.AdditionalResultDataBuilder; +import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.impl.components.AbstractSimpleParallelProcessor; + +public abstract class AbstractEnrichingProcessor extends + AbstractSimpleParallelProcessor { + + public AbstractEnrichingProcessor(ExecutorService executor, Collection> resultReceivers) { + super(executor, resultReceivers); + } + + @Override + protected Callable createInstruction(final InputType element) { + return new Callable() { + + @Override + public ResultType call() throws Exception { + return enrich(element); + } + }; + } + + protected abstract ResultType enrich(InputType element); + + @Override + protected void setAdditionalData(AdditionalResultDataBuilder additionalDataBuilder) { + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java new file mode 100644 index 00000000000..546fed87508 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -0,0 +1,44 @@ +package com.sap.sailing.polars.mining; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.tracking.GPSFixMoving; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.Wind; +import com.sap.sse.datamining.shared.annotations.Dimension; + +public class GPSFixMovingWithPolarContext { + + private final GPSFixMoving fix; + private final TrackedRace race; + private final Competitor competitor; + + public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor) { + this.fix = fix; + this.race = race; + this.competitor = competitor; + } + + public GPSFixMoving getFix() { + return fix; + } + + public TrackedRace getRace() { + return race; + } + + public Competitor getCompetitor() { + return competitor; + } + + @Dimension(messageKey = "roundedAngle") + public int roundedAngleToTheWind() { + SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); + Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint()); + Bearing bearing = boatSpeed.getBearing(); + int roundedAngle = (int) Math.round(bearing.getDifferenceTo(wind.getBearing()).getDegrees()); + return roundedAngle; + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java new file mode 100644 index 00000000000..6fb0fbd36bb --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -0,0 +1,73 @@ +package com.sap.sailing.polars.mining; + +import java.util.ArrayList; +import java.util.Arrays; +import java.util.Collection; +import java.util.concurrent.LinkedBlockingQueue; +import java.util.concurrent.ThreadPoolExecutor; +import java.util.concurrent.TimeUnit; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.impl.Util.Triple; +import com.sap.sailing.domain.tracking.GPSFixMoving; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.factories.FunctionFactory; +import com.sap.sse.datamining.functions.Function; +import com.sap.sse.datamining.impl.components.GroupedDataEntry; +import com.sap.sse.datamining.impl.components.ParallelFilteringProcessor; +import com.sap.sse.datamining.impl.components.ParallelMultiDimensionalGroupingProcessor; + +public class PolarDataMiner { + + private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3); + private static final ThreadPoolExecutor executor = createExecutor(); + + private static ThreadPoolExecutor createExecutor() { + return new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS, + new LinkedBlockingQueue()); + } + + + private AbstractEnrichingProcessor, GPSFixMovingWithPolarContext> enrichingProcessor; + + public PolarDataMiner() { + try { + setUpWorkflow(); + } catch (ClassCastException | NoSuchMethodException | SecurityException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + } + + + + private void setUpWorkflow() throws ClassCastException, NoSuchMethodException, + SecurityException { + Collection>> grouperResultReceivers = null; + Collection> dimensions = new ArrayList<>(); + Function function = FunctionFactory.createMethodWrappingFunction(GPSFixMovingWithPolarContext.class + .getMethod("roundedAngleToTheWind", new Class[0])); + dimensions.add(function); + Processor groupingProcessor = new ParallelMultiDimensionalGroupingProcessor( + executor, grouperResultReceivers, dimensions); + Collection> filteringResultReceivers = Arrays.asList(groupingProcessor); + Processor filteringProcessor = new ParallelFilteringProcessor( + executor, filteringResultReceivers, new PolarFixFilterCriteria()); + Collection> enrichingResultReceivers = Arrays + .asList(filteringProcessor); + enrichingProcessor = new AbstractEnrichingProcessor, GPSFixMovingWithPolarContext>(executor, enrichingResultReceivers) { + + @Override + protected GPSFixMovingWithPolarContext enrich(Triple element) { + return new GPSFixMovingWithPolarContext(element.getA(), element.getB(), element.getC()); + } + }; + } + + + public void addFix(GPSFixMoving fix, Competitor competitor, TrackedRace trackedRace) { + enrichingProcessor.onElement(new Triple(fix, trackedRace, competitor)); + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java new file mode 100644 index 00000000000..7e412f34fc0 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java @@ -0,0 +1,79 @@ +package com.sap.sailing.polars.mining; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.tracking.GPSFixMoving; +import com.sap.sailing.domain.tracking.GPSFixTrack; +import com.sap.sailing.domain.tracking.MarkPassing; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sse.datamining.components.FilterCriteria; + +public class PolarFixFilterCriteria implements FilterCriteria { + + @Override + public boolean matches(GPSFixMovingWithPolarContext element) { + boolean afterStartTime = isAfterStartTime(element); + boolean beforeFinishTime = isBeforeFinishTime(element); + boolean noDirectionChange = !hasDirectionChange(element); + return (afterStartTime && beforeFinishTime && noDirectionChange); + } + + private boolean hasDirectionChange(GPSFixMovingWithPolarContext element) { + GPSFixTrack track = element.getRace().getTrack(element.getCompetitor()); + return track.hasDirectionChange(element.getFix().getTimePoint(), element.getRace().getRace().getBoatClass() + .getManeuverDegreeAngleThreshold()); + } + + private boolean isBeforeFinishTime(GPSFixMovingWithPolarContext element) { + TimePoint timepointOfFix = element.getFix().getTimePoint(); + boolean isBeforeFinish; + TimePoint finishTime = calculateFinishTime(element.getRace(), element.getCompetitor()); + isBeforeFinish = timepointOfFix.before(finishTime); + return isBeforeFinish; + } + + private TimePoint calculateFinishTime(TrackedRace race, Competitor competitor) { + TimePoint finishTime = race.getEndOfRace(); + MarkPassing finishPassing = race.getMarkPassing(competitor, race.getRace().getCourse().getLastWaypoint()); + if (finishPassing != null) { + TimePoint passedFinishTimePoint = finishPassing.getTimePoint(); + if (passedFinishTimePoint.before(finishTime)) { + finishTime = passedFinishTimePoint; + } + } + return finishTime; + } + + private boolean isAfterStartTime(GPSFixMovingWithPolarContext element) { + TimePoint timepointOfFix = element.getFix().getTimePoint(); + boolean isAfterStart; + try { + TimePoint startTime = calculateStartTime(element.getRace(), element.getCompetitor()); + isAfterStart = timepointOfFix.after(startTime); + } catch (CompetitorDidNotStartYetException exception) { + isAfterStart = false; + } + return isAfterStart; + } + + private TimePoint calculateStartTime(TrackedRace race, Competitor competitor) throws CompetitorDidNotStartYetException { + MarkPassing startPassing = race.getMarkPassing(competitor, race.getRace().getCourse().getFirstWaypoint()); + if (startPassing == null) { + throw new CompetitorDidNotStartYetException(); + } + TimePoint raceStartTime = race.getStartOfRace(); + TimePoint startTime; + TimePoint passedStartTimePoint = startPassing.getTimePoint(); + if (passedStartTimePoint.after(raceStartTime)) { + startTime = passedStartTimePoint; + } else { + startTime = raceStartTime; + } + return startTime; + } + + private class CompetitorDidNotStartYetException extends Exception { + private static final long serialVersionUID = 7906688735433666009L; + } + +} diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index d9337c41897..3e062a976d8 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -1176,8 +1176,7 @@ public class RacingEventServiceImpl implements RacingEventServiceWithTestSupport @Override public void competitorPositionChanged(GPSFixMoving fix, Competitor item) { - polarDataService.competitorPositionChanged(fix.getTimePoint(), item, race, - race.getMillisecondsOverWhichToAverageSpeed()); + polarDataService.competitorPositionChanged(fix, item, race); } } From af5139377367f0bf3fac375047d7d53c82a9efca Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Mon, 31 Mar 2014 13:42:54 +0200 Subject: [PATCH 25/72] Added missing import --- .../java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 7ce4705327c..404ffa9cce5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -262,11 +262,12 @@ import com.sap.sailing.gwt.ui.shared.WaypointDTO; import com.sap.sailing.gwt.ui.shared.WindDTO; import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO; import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO; -import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.manage2sail.EventResultDescriptor; import com.sap.sailing.manage2sail.Manage2SailEventResultsParserImpl; import com.sap.sailing.manage2sail.RaceResultDescriptor; import com.sap.sailing.manage2sail.RegattaResultDescriptor; import com.sap.sailing.manage2sail.TestManage2SailEventResultsParser; +import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.resultimport.ResultUrlProvider; import com.sap.sailing.resultimport.ResultUrlRegistry; import com.sap.sailing.server.RacingEventService; From c861ab5b3d8f916b36aa8fc6f4b5a1f754b07511 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Thu, 3 Apr 2014 10:12:34 +0200 Subject: [PATCH 26/72] Started creating a test for polar fix grouping --- .../sailing/domain/common/WindStepping.java | 4 + .../mining/test/PolarDataMinerTest.java | 120 ++++++++++++++++++ .../mining/GPSFixMovingWithPolarContext.java | 26 +++- .../IncrementalRegressionProcessor.java | 43 +++++++ .../polars/mining/PolarClusterKey.java | 21 +++ .../sailing/polars/mining/PolarDataMiner.java | 35 ++++- .../polars/mining/RoundedAngleToTheWind.java | 33 +++++ .../sailing/polars/mining/WindSpeedLevel.java | 44 +++++++ .../shared/impl/CompoundGroupKey.java | 1 + .../datamining/factories/GroupKeyFactory.java | 26 ++++ ...llelMultiDimensionalGroupingProcessor.java | 22 +--- 11 files changed, 343 insertions(+), 32 deletions(-) create mode 100644 java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarClusterKey.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedAngleToTheWind.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java create mode 100644 java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/GroupKeyFactory.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindStepping.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindStepping.java index e6dedbf86e5..0ed1304fd4d 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindStepping.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/WindStepping.java @@ -16,4 +16,8 @@ public interface WindStepping extends Serializable{ double getDistanceToLevelFloor(double speed); + int hashCode(); + + boolean equals(Object obj); + } \ No newline at end of file diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java new file mode 100644 index 00000000000..61ec5c5ff6e --- /dev/null +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java @@ -0,0 +1,120 @@ +package com.sap.sailing.polars.mining.test; + +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.eq; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.Calendar; +import java.util.concurrent.TimeoutException; + +import org.junit.Test; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Course; +import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; +import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; +import com.sap.sailing.domain.tracking.GPSFixMoving; +import com.sap.sailing.domain.tracking.MarkPassing; +import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; +import com.sap.sailing.polars.mining.PolarDataMiner; + +public class PolarDataMinerTest { + + private static final int MILLISECONDS_OVER_WHICH_TO_AVERAGE_SPEED = 30; + + @Test + public void testGrouping() throws InterruptedException, TimeoutException { + PolarDataMiner miner = new PolarDataMiner(); + + GPSFixMoving fix = createMockedFix(); + Competitor competitor = mock(Competitor.class); + TrackedRace trackedRace = createMockedTrackedRace(competitor, fix); + miner.addFix(fix, competitor, trackedRace); + int millisLeft = 500000; + while (miner.isCurrentlyActiveAndOrHasQueue() && millisLeft > 0) { + System.out.println("Executor active"); + Thread.sleep(100); + millisLeft = millisLeft - 100; + if (miner.isCurrentlyActiveAndOrHasQueue() && millisLeft <= 0) { + throw new TimeoutException(); + } + } + } + + private GPSFixMoving createMockedFix() { + GPSFixMoving fix = mock(GPSFixMoving.class); + when(fix.getPosition()).thenReturn(new DegreePosition(54.431952, 10.186767)); + Calendar cal = Calendar.getInstance(); + cal.set(2014, 4, 3, 13, 00); + TimePoint fixTimePoint = new MillisecondsTimePoint(cal.getTime()); + when(fix.getTimePoint()).thenReturn(fixTimePoint); + Bearing bearing = new DegreeBearingImpl(45); + SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(10.5, bearing); + when(fix.getSpeed()).thenReturn(speedWithBearing); + return fix; + } + + private TrackedRace createMockedTrackedRace(Competitor competitor, GPSFixMoving fix) { + TrackedRace trackedRace = mock(TrackedRace.class); + + DynamicGPSFixTrack track = new DynamicGPSFixMovingTrackImpl(competitor, + MILLISECONDS_OVER_WHICH_TO_AVERAGE_SPEED); + track.add(fix); + when(trackedRace.getTrack(competitor)).thenReturn(track); + + RaceDefinition mockedRaceDefinition = createMockedRaceDefinition(); + when(trackedRace.getRace()).thenReturn(mockedRaceDefinition); + + MarkPassing markpassing = createMockedStartMarkPassing(); + when(trackedRace.getMarkPassing(eq(competitor), any(Waypoint.class))).thenReturn(markpassing); + + Calendar cal = Calendar.getInstance(); + cal.set(2014, 4, 3, 12, 00); + TimePoint startOfRace = new MillisecondsTimePoint(cal.getTime()); + + cal.set(2014, 4, 3, 15, 00); + TimePoint endOfRace = new MillisecondsTimePoint(cal.getTime()); + + when(trackedRace.getStartOfRace()).thenReturn(startOfRace); + when(trackedRace.getEndOfRace()).thenReturn(endOfRace); + + return trackedRace; + } + + private MarkPassing createMockedStartMarkPassing() { + Calendar cal = Calendar.getInstance(); + cal.set(2014, 4, 3, 12, 15); + TimePoint startOfRaceForCompetitor = new MillisecondsTimePoint(cal.getTime()); + + MarkPassing passing = mock(MarkPassing.class); + when(passing.getTimePoint()).thenReturn(startOfRaceForCompetitor); + return passing; + } + + private RaceDefinition createMockedRaceDefinition() { + RaceDefinition raceDefinition = mock(RaceDefinition.class); + Course mockedCourse = createMockedCourse(); + when(raceDefinition.getCourse()).thenReturn(mockedCourse); + BoatClass mockedBoatClass = mock(BoatClass.class); + when(mockedBoatClass.getManeuverDegreeAngleThreshold()).thenReturn(20.0); + when(raceDefinition.getBoatClass()).thenReturn(mockedBoatClass); + return raceDefinition; + } + + private Course createMockedCourse() { + Course course = mock(Course.class); + return course; + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index 546fed87508..fc1a366edae 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -2,22 +2,26 @@ package com.sap.sailing.polars.mining; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.Wind; -import com.sap.sse.datamining.shared.annotations.Dimension; -public class GPSFixMovingWithPolarContext { +public class GPSFixMovingWithPolarContext implements PolarClusterKey { private final GPSFixMoving fix; private final TrackedRace race; private final Competitor competitor; + private final PolarSheetGenerationSettings defaultPolarSheetGenerationSettings; - public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor) { + public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor, + PolarSheetGenerationSettings defaultPolarSheetGenerationSettings) { this.fix = fix; this.race = race; this.competitor = competitor; + this.defaultPolarSheetGenerationSettings = defaultPolarSheetGenerationSettings; } public GPSFixMoving getFix() { @@ -32,13 +36,21 @@ public class GPSFixMovingWithPolarContext { return competitor; } - @Dimension(messageKey = "roundedAngle") - public int roundedAngleToTheWind() { + @Override + public RoundedAngleToTheWind getRoundedAngleToTheWind() { SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint()); Bearing bearing = boatSpeed.getBearing(); - int roundedAngle = (int) Math.round(bearing.getDifferenceTo(wind.getBearing()).getDegrees()); - return roundedAngle; + int angle = (int) Math.round(bearing.getDifferenceTo(wind.getBearing()).getDegrees()); + return new RoundedAngleToTheWind(angle); + } + + @Override + public WindSpeedLevel getWindSpeedLevel() { + Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint()); + WindSteppingWithMaxDistance stepping = defaultPolarSheetGenerationSettings.getWindStepping(); + int level = stepping.getLevelIndexForValue(wind.getKnots()); + return new WindSpeedLevel(level, stepping); } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java new file mode 100644 index 00000000000..e4fecda2c90 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -0,0 +1,43 @@ +package com.sap.sailing.polars.mining; + +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Set; + +import com.sap.sse.datamining.AdditionalResultDataBuilder; +import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.impl.components.GroupedDataEntry; +import com.sap.sse.datamining.shared.GroupKey; + +public class IncrementalRegressionProcessor implements Processor> { + + private final Map> container = new HashMap>(); + + @Override + public void onElement(GroupedDataEntry element) { + GroupKey key = element.getKey(); + if (!container.containsKey(key)) { + container.put(key, new HashSet()); + } + Set set = container.get(key); + set.add(element.getDataEntry()); + } + + @Override + public void finish() throws InterruptedException { + // Nothing to do here + } + + @Override + public void abort() { + // TODO Auto-generated method stub + } + + @Override + public AdditionalResultDataBuilder getAdditionalResultData(AdditionalResultDataBuilder additionalDataBuilder) { + // TODO Auto-generated method stub + return null; + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarClusterKey.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarClusterKey.java new file mode 100644 index 00000000000..65f0d163ad3 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarClusterKey.java @@ -0,0 +1,21 @@ +package com.sap.sailing.polars.mining; + +import com.sap.sse.datamining.shared.annotations.Dimension; + +public interface PolarClusterKey { + + /** + * + * @return degrees boat to windDirection in DEG + */ + @Dimension(messageKey = "angleToWind") + public abstract RoundedAngleToTheWind getRoundedAngleToTheWind(); + + /** + * + * @return wind speed level + */ + @Dimension(messageKey = "windSpeed") + public abstract WindSpeedLevel getWindSpeedLevel(); + +} \ No newline at end of file diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index 6fb0fbd36bb..94a1d2e4ed5 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -8,6 +8,8 @@ import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.common.impl.Util.Triple; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; @@ -22,6 +24,7 @@ public class PolarDataMiner { private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3); private static final ThreadPoolExecutor executor = createExecutor(); + private final PolarSheetGenerationSettings defaultPolarSheetGenerationSettings; private static ThreadPoolExecutor createExecutor() { return new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS, @@ -32,6 +35,7 @@ public class PolarDataMiner { private AbstractEnrichingProcessor, GPSFixMovingWithPolarContext> enrichingProcessor; public PolarDataMiner() { + defaultPolarSheetGenerationSettings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); try { setUpWorkflow(); } catch (ClassCastException | NoSuchMethodException | SecurityException e) { @@ -44,23 +48,36 @@ public class PolarDataMiner { private void setUpWorkflow() throws ClassCastException, NoSuchMethodException, SecurityException { - Collection>> grouperResultReceivers = null; + Processor> incrementalRegressionProcessor = new IncrementalRegressionProcessor(); + Collection>> grouperResultReceivers = Arrays + .asList(incrementalRegressionProcessor); + Collection> dimensions = new ArrayList<>(); - Function function = FunctionFactory.createMethodWrappingFunction(GPSFixMovingWithPolarContext.class - .getMethod("roundedAngleToTheWind", new Class[0])); - dimensions.add(function); + Function angleFunction = FunctionFactory + .createMethodWrappingFunction(PolarClusterKey.class.getMethod("getRoundedAngleToTheWind", + new Class[0])); + Function windSpeedFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class + .getMethod("getWindSpeedLevel", new Class[0])); + + dimensions.add(angleFunction); + dimensions.add(windSpeedFunction); + Processor groupingProcessor = new ParallelMultiDimensionalGroupingProcessor( executor, grouperResultReceivers, dimensions); + Collection> filteringResultReceivers = Arrays.asList(groupingProcessor); Processor filteringProcessor = new ParallelFilteringProcessor( executor, filteringResultReceivers, new PolarFixFilterCriteria()); + Collection> enrichingResultReceivers = Arrays .asList(filteringProcessor); - enrichingProcessor = new AbstractEnrichingProcessor, GPSFixMovingWithPolarContext>(executor, enrichingResultReceivers) { + enrichingProcessor = new AbstractEnrichingProcessor, GPSFixMovingWithPolarContext>( + executor, enrichingResultReceivers) { @Override protected GPSFixMovingWithPolarContext enrich(Triple element) { - return new GPSFixMovingWithPolarContext(element.getA(), element.getB(), element.getC()); + return new GPSFixMovingWithPolarContext(element.getA(), element.getB(), element.getC(), + defaultPolarSheetGenerationSettings); } }; } @@ -70,4 +87,10 @@ public class PolarDataMiner { enrichingProcessor.onElement(new Triple(fix, trackedRace, competitor)); } + public boolean isCurrentlyActiveAndOrHasQueue() { + boolean isActive = executor.getActiveCount() > 0; + boolean hasQueue = executor.getQueue().size() > 0; + return isActive || hasQueue; + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedAngleToTheWind.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedAngleToTheWind.java new file mode 100644 index 00000000000..1b74c811eda --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedAngleToTheWind.java @@ -0,0 +1,33 @@ +package com.sap.sailing.polars.mining; + +public class RoundedAngleToTheWind { + + private final int angle; + + public RoundedAngleToTheWind(int angle) { + this.angle = angle; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + angle; + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + RoundedAngleToTheWind other = (RoundedAngleToTheWind) obj; + if (angle != other.angle) + return false; + return true; + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java new file mode 100644 index 00000000000..aa01b1e74de --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java @@ -0,0 +1,44 @@ +package com.sap.sailing.polars.mining; + +import com.sap.sailing.domain.common.WindStepping; + +public class WindSpeedLevel { + + private final int level; + private final WindStepping stepping; + + public WindSpeedLevel(int level, WindStepping stepping) { + this.level = level; + this.stepping = stepping; + } + + @Override + public int hashCode() { + final int prime = 31; + int result = 1; + result = prime * result + level; + result = prime * result + ((stepping == null) ? 0 : stepping.hashCode()); + return result; + } + + @Override + public boolean equals(Object obj) { + if (this == obj) + return true; + if (obj == null) + return false; + if (getClass() != obj.getClass()) + return false; + WindSpeedLevel other = (WindSpeedLevel) obj; + if (level != other.level) + return false; + if (stepping == null) { + if (other.stepping != null) + return false; + } else if (!stepping.equals(other.stepping)) + return false; + return true; + } + + +} diff --git a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/CompoundGroupKey.java b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/CompoundGroupKey.java index 6166caea99e..9198a29c107 100644 --- a/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/CompoundGroupKey.java +++ b/java/com.sap.sse.datamining.shared/src/com/sap/sse/datamining/shared/impl/CompoundGroupKey.java @@ -4,6 +4,7 @@ import com.sap.sse.datamining.shared.GroupKey; public class CompoundGroupKey extends AbstractGroupKey { + private static final long serialVersionUID = -7902450253393172550L; private GroupKey mainKey; diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/GroupKeyFactory.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/GroupKeyFactory.java new file mode 100644 index 00000000000..5d487d0dcaa --- /dev/null +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/factories/GroupKeyFactory.java @@ -0,0 +1,26 @@ +package com.sap.sse.datamining.factories; + +import java.util.Iterator; + +import com.sap.sse.datamining.functions.Function; +import com.sap.sse.datamining.shared.GroupKey; +import com.sap.sse.datamining.shared.impl.CompoundGroupKey; +import com.sap.sse.datamining.shared.impl.GenericGroupKey; + +public class GroupKeyFactory { + + public static GroupKey createCompoundKeyFor(DataType input, Iterator> dimensionsIterator) { + Function mainDimension = dimensionsIterator.next(); + GroupKey key = createGroupKeyFor(input, mainDimension); + if (dimensionsIterator.hasNext()) { + key = new CompoundGroupKey(key, createCompoundKeyFor(input, dimensionsIterator)); + } + return key; + } + + public static GroupKey createGroupKeyFor(DataType input, Function mainDimension) { + Object keyValue = mainDimension.tryToInvoke(input); + return new GenericGroupKey(keyValue); + } + +} diff --git a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/ParallelMultiDimensionalGroupingProcessor.java b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/ParallelMultiDimensionalGroupingProcessor.java index 6585541ebf5..8f264e24f6b 100644 --- a/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/ParallelMultiDimensionalGroupingProcessor.java +++ b/java/com.sap.sse.datamining/src/com/sap/sse/datamining/impl/components/ParallelMultiDimensionalGroupingProcessor.java @@ -1,16 +1,13 @@ package com.sap.sse.datamining.impl.components; import java.util.Collection; -import java.util.Iterator; import java.util.concurrent.Callable; import java.util.concurrent.ExecutorService; import com.sap.sse.datamining.AdditionalResultDataBuilder; import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.factories.GroupKeyFactory; import com.sap.sse.datamining.functions.Function; -import com.sap.sse.datamining.shared.GroupKey; -import com.sap.sse.datamining.shared.impl.CompoundGroupKey; -import com.sap.sse.datamining.shared.impl.GenericGroupKey; public class ParallelMultiDimensionalGroupingProcessor extends AbstractSimpleParallelProcessor> { @@ -47,25 +44,12 @@ public class ParallelMultiDimensionalGroupingProcessor return new Callable>() { @Override public GroupedDataEntry call() throws Exception { - return new GroupedDataEntry(createCompoundKeyFor(element, dimensions.iterator()), element); + return new GroupedDataEntry(GroupKeyFactory.createCompoundKeyFor(element, + dimensions.iterator()), element); } }; } - private GroupKey createCompoundKeyFor(DataType input, Iterator> dimensionsIterator) { - Function mainDimension = dimensionsIterator.next(); - GroupKey key = createGroupKeyFor(input, mainDimension); - if (dimensionsIterator.hasNext()) { - key = new CompoundGroupKey(key, createCompoundKeyFor(input, dimensionsIterator)); - } - return key; - } - - private GroupKey createGroupKeyFor(DataType input, Function mainDimension) { - Object keyValue = mainDimension.tryToInvoke(input); - return new GenericGroupKey(keyValue); - } - @Override protected void setAdditionalData(AdditionalResultDataBuilder additionalDataBuilder) { } From 0593adc2e0e5eb6b19fcc3f33de02c84b887f50c Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Thu, 3 Apr 2014 11:23:28 +0200 Subject: [PATCH 27/72] Extended test case for polar data miner --- .../mining/test/PolarDataMinerTest.java | 36 ++++++++++++++++- .../IncrementalRegressionProcessor.java | 4 ++ .../sailing/polars/mining/PolarDataMiner.java | 39 +++++++++++++------ .../polars/mining/PolarFixFilterCriteria.java | 14 ++++--- 4 files changed, 74 insertions(+), 19 deletions(-) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java index 61ec5c5ff6e..4375c2e657c 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java @@ -6,8 +6,10 @@ import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Calendar; +import java.util.Set; import java.util.concurrent.TimeoutException; +import org.junit.Assert; import org.junit.Test; import com.sap.sailing.domain.base.BoatClass; @@ -22,19 +24,28 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; +import com.sap.sailing.domain.tracking.impl.WindImpl; +import com.sap.sailing.polars.mining.GPSFixMovingWithPolarContext; +import com.sap.sailing.polars.mining.PolarClusterKey; import com.sap.sailing.polars.mining.PolarDataMiner; +import com.sap.sailing.polars.mining.RoundedAngleToTheWind; +import com.sap.sailing.polars.mining.WindSpeedLevel; +import com.sap.sse.datamining.factories.GroupKeyFactory; +import com.sap.sse.datamining.shared.GroupKey; public class PolarDataMinerTest { private static final int MILLISECONDS_OVER_WHICH_TO_AVERAGE_SPEED = 30; @Test - public void testGrouping() throws InterruptedException, TimeoutException { + public void testGrouping() throws InterruptedException, TimeoutException, NoSuchMethodException { PolarDataMiner miner = new PolarDataMiner(); GPSFixMoving fix = createMockedFix(); @@ -43,13 +54,28 @@ public class PolarDataMinerTest { miner.addFix(fix, competitor, trackedRace); int millisLeft = 500000; while (miner.isCurrentlyActiveAndOrHasQueue() && millisLeft > 0) { - System.out.println("Executor active"); Thread.sleep(100); millisLeft = millisLeft - 100; if (miner.isCurrentlyActiveAndOrHasQueue() && millisLeft <= 0) { throw new TimeoutException(); } } + PolarClusterKey key = new PolarClusterKey() { + + @Override + public WindSpeedLevel getWindSpeedLevel() { + return new WindSpeedLevel(7, PolarSheetGenerationSettingsImpl.createStandardPolarSettings() + .getWindStepping()); + } + + @Override + public RoundedAngleToTheWind getRoundedAngleToTheWind() { + return new RoundedAngleToTheWind(-45); + } + }; + GroupKey compoundKey = GroupKeyFactory.createCompoundKeyFor(key, miner.getClusterKeyDimensions().iterator()); + Set fixWithPolarContext = miner.getContainer(compoundKey); + Assert.assertNotNull(fixWithPolarContext); } private GPSFixMoving createMockedFix() { @@ -88,6 +114,12 @@ public class PolarDataMinerTest { when(trackedRace.getStartOfRace()).thenReturn(startOfRace); when(trackedRace.getEndOfRace()).thenReturn(endOfRace); + + Bearing windBearing = new DegreeBearingImpl(0); + SpeedWithBearing windSpeed = new KnotSpeedWithBearingImpl(15, windBearing); + + Wind wind = new WindImpl(fix.getPosition(), fix.getTimePoint(), windSpeed); + when(trackedRace.getWind(fix.getPosition(), fix.getTimePoint())).thenReturn(wind); return trackedRace; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index e4fecda2c90..644d953e7a4 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -40,4 +40,8 @@ public class IncrementalRegressionProcessor implements Processor getContainer(GroupKey compoundKey) { + return container.get(compoundKey); + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index 94a1d2e4ed5..ec3e1638dce 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -3,6 +3,8 @@ package com.sap.sailing.polars.mining; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.List; +import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -19,6 +21,7 @@ import com.sap.sse.datamining.functions.Function; import com.sap.sse.datamining.impl.components.GroupedDataEntry; import com.sap.sse.datamining.impl.components.ParallelFilteringProcessor; import com.sap.sse.datamining.impl.components.ParallelMultiDimensionalGroupingProcessor; +import com.sap.sse.datamining.shared.GroupKey; public class PolarDataMiner { @@ -33,6 +36,7 @@ public class PolarDataMiner { private AbstractEnrichingProcessor, GPSFixMovingWithPolarContext> enrichingProcessor; + private IncrementalRegressionProcessor incrementalRegressionProcessor; public PolarDataMiner() { defaultPolarSheetGenerationSettings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); @@ -48,19 +52,11 @@ public class PolarDataMiner { private void setUpWorkflow() throws ClassCastException, NoSuchMethodException, SecurityException { - Processor> incrementalRegressionProcessor = new IncrementalRegressionProcessor(); - Collection>> grouperResultReceivers = Arrays - .asList(incrementalRegressionProcessor); + incrementalRegressionProcessor = new IncrementalRegressionProcessor(); + List>> grouperResultReceivers = new ArrayList>>(); + grouperResultReceivers.add(incrementalRegressionProcessor); - Collection> dimensions = new ArrayList<>(); - Function angleFunction = FunctionFactory - .createMethodWrappingFunction(PolarClusterKey.class.getMethod("getRoundedAngleToTheWind", - new Class[0])); - Function windSpeedFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class - .getMethod("getWindSpeedLevel", new Class[0])); - - dimensions.add(angleFunction); - dimensions.add(windSpeedFunction); + Collection> dimensions = getClusterKeyDimensions(); Processor groupingProcessor = new ParallelMultiDimensionalGroupingProcessor( executor, grouperResultReceivers, dimensions); @@ -83,6 +79,21 @@ public class PolarDataMiner { } + + public Collection> getClusterKeyDimensions() throws NoSuchMethodException { + Collection> dimensions = new ArrayList<>(); + Function angleFunction = FunctionFactory + .createMethodWrappingFunction(PolarClusterKey.class.getMethod("getRoundedAngleToTheWind", + new Class[0])); + Function windSpeedFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class + .getMethod("getWindSpeedLevel", new Class[0])); + + dimensions.add(angleFunction); + dimensions.add(windSpeedFunction); + return dimensions; + } + + public void addFix(GPSFixMoving fix, Competitor competitor, TrackedRace trackedRace) { enrichingProcessor.onElement(new Triple(fix, trackedRace, competitor)); } @@ -93,4 +104,8 @@ public class PolarDataMiner { return isActive || hasQueue; } + public Set getContainer(GroupKey compoundKey) { + return incrementalRegressionProcessor.getContainer(compoundKey); + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java index 7e412f34fc0..23c112f80f5 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java @@ -1,6 +1,7 @@ package com.sap.sailing.polars.mining; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.GPSFixTrack; @@ -34,11 +35,14 @@ public class PolarFixFilterCriteria implements FilterCriteria Date: Thu, 3 Apr 2014 16:51:44 +0200 Subject: [PATCH 28/72] Introduces dual linear regression for polar data estimation --- .../mining/test/PolarDataMinerTest.java | 115 +++++++++++------- .../test/IncrementalLinearRegressionTest.java | 38 +++--- .../mining/GPSFixMovingWithPolarContext.java | 22 +++- .../IncrementalRegressionProcessor.java | 56 +++++++-- .../PolarDataDimensionCollectionFactory.java | 24 ++++ .../sailing/polars/mining/PolarDataMiner.java | 27 +--- .../polars/mining/RoundedAngleToTheWind.java | 11 +- .../sailing/polars/mining/WindSpeedLevel.java | 12 +- .../polars/regression/BoatSpeedEstimator.java | 50 ++++++++ .../IncrementalLinearRegressionProcessor.java | 2 +- .../IncrementalLeastSquaresProcessor.java | 3 +- 11 files changed, 258 insertions(+), 102 deletions(-) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java index 4375c2e657c..ab19a436f32 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java @@ -1,15 +1,22 @@ package com.sap.sailing.polars.mining.test; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.closeTo; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.notNullValue; import static org.mockito.Matchers.any; import static org.mockito.Matchers.eq; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; import java.util.Calendar; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.TimeoutException; -import org.junit.Assert; import org.junit.Test; import com.sap.sailing.domain.base.BoatClass; @@ -18,13 +25,15 @@ import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.Bearing; +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.TimePoint; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; -import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.MarkPassing; @@ -32,26 +41,50 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; import com.sap.sailing.domain.tracking.impl.WindImpl; -import com.sap.sailing.polars.mining.GPSFixMovingWithPolarContext; -import com.sap.sailing.polars.mining.PolarClusterKey; import com.sap.sailing.polars.mining.PolarDataMiner; -import com.sap.sailing.polars.mining.RoundedAngleToTheWind; -import com.sap.sailing.polars.mining.WindSpeedLevel; -import com.sap.sse.datamining.factories.GroupKeyFactory; -import com.sap.sse.datamining.shared.GroupKey; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; public class PolarDataMinerTest { private static final int MILLISECONDS_OVER_WHICH_TO_AVERAGE_SPEED = 30; + private static final double EPSILON = 1E-4; @Test - public void testGrouping() throws InterruptedException, TimeoutException, NoSuchMethodException { + public void testGrouping() throws InterruptedException, TimeoutException, NoSuchMethodException, + NotEnoughDataHasBeenAddedException { PolarDataMiner miner = new PolarDataMiner(); - GPSFixMoving fix = createMockedFix(); - Competitor competitor = mock(Competitor.class); - TrackedRace trackedRace = createMockedTrackedRace(competitor, fix); - miner.addFix(fix, competitor, trackedRace); + GPSFixMoving fix1_1 = createMockedFix(13, 00, 54.431952, 10.186767, 45, 10.5); + GPSFixMoving fix1_2 = createMockedFix(13, 30, 54.485034, 10.538303, 44.8, 10.6); + Competitor competitor1 = mock(Competitor.class); + + GPSFixMoving fix2_1 = createMockedFix(13, 00, 54.443942, 10.172739, 42.2, 10); + GPSFixMoving fix2_2 = createMockedFix(13, 30, 54.425394, 10.177404, 41.8, 10.1); + Competitor competitor2 = mock(Competitor.class); + + Map> fixesPerCompetitor = new HashMap>(); + + Set setForCompetitor1 = new HashSet(); + setForCompetitor1.add(fix1_1); + setForCompetitor1.add(fix1_2); + fixesPerCompetitor.put(competitor1, setForCompetitor1); + + Set setForCompetitor2 = new HashSet(); + setForCompetitor2.add(fix2_1); + setForCompetitor2.add(fix2_2); + fixesPerCompetitor.put(competitor2, setForCompetitor2); + + + TrackedRace trackedRace = createMockedTrackedRace(fixesPerCompetitor); + + for (Entry> entry : fixesPerCompetitor.entrySet()) { + Competitor competitor = entry.getKey(); + Set fixSet = entry.getValue(); + for (GPSFixMoving fix : fixSet) { + miner.addFix(fix, competitor, trackedRace); + } + } + int millisLeft = 500000; while (miner.isCurrentlyActiveAndOrHasQueue() && millisLeft > 0) { Thread.sleep(100); @@ -60,50 +93,49 @@ public class PolarDataMinerTest { throw new TimeoutException(); } } - PolarClusterKey key = new PolarClusterKey() { - @Override - public WindSpeedLevel getWindSpeedLevel() { - return new WindSpeedLevel(7, PolarSheetGenerationSettingsImpl.createStandardPolarSettings() - .getWindStepping()); - } + Speed estimatedSpeed1 = miner.estimateBoatSpeed(new KnotSpeedImpl(15), new DegreeBearingImpl(-44.9)); + assertThat(estimatedSpeed1, is(notNullValue())); + assertThat(estimatedSpeed1.getKnots(), is(closeTo(10.55, EPSILON))); - @Override - public RoundedAngleToTheWind getRoundedAngleToTheWind() { - return new RoundedAngleToTheWind(-45); - } - }; - GroupKey compoundKey = GroupKeyFactory.createCompoundKeyFor(key, miner.getClusterKeyDimensions().iterator()); - Set fixWithPolarContext = miner.getContainer(compoundKey); - Assert.assertNotNull(fixWithPolarContext); + Speed estimatedSpeed2 = miner.estimateBoatSpeed(new KnotSpeedImpl(15), new DegreeBearingImpl(-42)); + assertThat(estimatedSpeed2, is(notNullValue())); + assertThat(estimatedSpeed2.getKnots(), is(closeTo(10.05, EPSILON))); } - private GPSFixMoving createMockedFix() { + private GPSFixMoving createMockedFix(int hour, int minute, double lat, double lng, double bearingRaw, double speed) { GPSFixMoving fix = mock(GPSFixMoving.class); - when(fix.getPosition()).thenReturn(new DegreePosition(54.431952, 10.186767)); + when(fix.getPosition()).thenReturn(new DegreePosition(lat, lng)); Calendar cal = Calendar.getInstance(); - cal.set(2014, 4, 3, 13, 00); + cal.set(2014, 4, 3, hour, minute); TimePoint fixTimePoint = new MillisecondsTimePoint(cal.getTime()); when(fix.getTimePoint()).thenReturn(fixTimePoint); - Bearing bearing = new DegreeBearingImpl(45); - SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(10.5, bearing); + Bearing bearing = new DegreeBearingImpl(bearingRaw); + SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(speed, bearing); when(fix.getSpeed()).thenReturn(speedWithBearing); return fix; } - private TrackedRace createMockedTrackedRace(Competitor competitor, GPSFixMoving fix) { + private TrackedRace createMockedTrackedRace(Map> fixesPerCompetitor) { TrackedRace trackedRace = mock(TrackedRace.class); - DynamicGPSFixTrack track = new DynamicGPSFixMovingTrackImpl(competitor, - MILLISECONDS_OVER_WHICH_TO_AVERAGE_SPEED); - track.add(fix); - when(trackedRace.getTrack(competitor)).thenReturn(track); + for (Entry> competitorEntry : fixesPerCompetitor.entrySet()) { + Competitor competitor = competitorEntry.getKey(); + DynamicGPSFixTrack track = new DynamicGPSFixMovingTrackImpl( + competitor, MILLISECONDS_OVER_WHICH_TO_AVERAGE_SPEED); + when(trackedRace.getTrack(competitor)).thenReturn(track); + for (GPSFixMoving fix : competitorEntry.getValue()) { + track.add(fix); + } + } RaceDefinition mockedRaceDefinition = createMockedRaceDefinition(); when(trackedRace.getRace()).thenReturn(mockedRaceDefinition); - MarkPassing markpassing = createMockedStartMarkPassing(); - when(trackedRace.getMarkPassing(eq(competitor), any(Waypoint.class))).thenReturn(markpassing); + for (Competitor competitor : fixesPerCompetitor.keySet()) { + MarkPassing markpassing = createMockedStartMarkPassing(); + when(trackedRace.getMarkPassing(eq(competitor), any(Waypoint.class))).thenReturn(markpassing); + } Calendar cal = Calendar.getInstance(); cal.set(2014, 4, 3, 12, 00); @@ -118,8 +150,9 @@ public class PolarDataMinerTest { Bearing windBearing = new DegreeBearingImpl(0); SpeedWithBearing windSpeed = new KnotSpeedWithBearingImpl(15, windBearing); - Wind wind = new WindImpl(fix.getPosition(), fix.getTimePoint(), windSpeed); - when(trackedRace.getWind(fix.getPosition(), fix.getTimePoint())).thenReturn(wind); + Wind wind = new WindImpl(new DegreePosition(54.431952, 10.186767), startOfRace, windSpeed); + // Always return same wind + when(trackedRace.getWind(any(Position.class), any(TimePoint.class))).thenReturn(wind); return trackedRace; } diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java index 10a1ff17280..232e7116494 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java @@ -33,13 +33,13 @@ public class IncrementalLinearRegressionTest { double y2 = 14; processor.addMeasuredPoint(x2, y2); - double y = processor.getY(x1); + double y = processor.getEstimatedY(x1); assertThat(y, new IsCloseTo(y1, EPSILON)); - y = processor.getY(x2); + y = processor.getEstimatedY(x2); assertThat(y, new IsCloseTo(y2, EPSILON)); - y = processor.getY(4.5); + y = processor.getEstimatedY(4.5); assertThat(y, new IsCloseTo(10.5, EPSILON)); } @@ -51,7 +51,7 @@ public class IncrementalLinearRegressionTest { processor.addMeasuredPoint(i, i); } - double y = processor.getY(-2); + double y = processor.getEstimatedY(-2); assertThat(y, new IsCloseTo(-2, EPSILON)); // 2 Points off the line @@ -59,9 +59,9 @@ public class IncrementalLinearRegressionTest { processor.addMeasuredPoint(8, -3); - y = processor.getY(-2); + y = processor.getEstimatedY(-2); assertThat(y, new IsCloseTo(-2, VAGUE_EPSILON)); - y = processor.getY(20); + y = processor.getEstimatedY(20); assertThat(y, new IsCloseTo(20, VAGUE_EPSILON)); } @@ -75,9 +75,9 @@ public class IncrementalLinearRegressionTest { processor.addMeasuredPoint(i, y); } - double y = processor.getY(-2); + double y = processor.getEstimatedY(-2); assertThat(y, new IsCloseTo(-2, VAGUE_EPSILON)); - y = processor.getY(20); + y = processor.getEstimatedY(20); assertThat(y, new IsCloseTo(20, VAGUE_EPSILON)); } @@ -93,10 +93,10 @@ public class IncrementalLinearRegressionTest { double y2 = 0; processor.addMeasuredPoint(x2, y2); - double y = processor.getY(x1); + double y = processor.getEstimatedY(x1); assertThat(y, new IsCloseTo(y1, EPSILON)); - y = processor.getY(x2); + y = processor.getEstimatedY(x2); assertThat(y, new IsCloseTo(y2, EPSILON)); } @@ -112,13 +112,13 @@ public class IncrementalLinearRegressionTest { double y2 = 5; processor.addMeasuredPoint(x2, y2); - double y = processor.getY(x1); + double y = processor.getEstimatedY(x1); assertThat(y, new IsCloseTo(y1, EPSILON)); - y = processor.getY(x2); + y = processor.getEstimatedY(x2); assertThat(y, new IsCloseTo(y2, EPSILON)); - y = processor.getY(0); + y = processor.getEstimatedY(0); assertThat(y, new IsCloseTo(0, EPSILON)); } @@ -133,7 +133,7 @@ public class IncrementalLinearRegressionTest { double xTimesI = x1 * i; processor.addMeasuredPoint(xTimesI, yTimesI); if (i > 0) { - double y = processor.getY(xTimesI); + double y = processor.getEstimatedY(xTimesI); assertThat(y, new IsCloseTo(yTimesI, EPSILON)); } } @@ -141,14 +141,14 @@ public class IncrementalLinearRegressionTest { processor.addMeasuredPoint(-x1, y1); processor.addMeasuredPoint(x1, -y1); - double y = processor.getY(0); + double y = processor.getEstimatedY(0); assertThat(y, new IsCloseTo(0, EPSILON)); } @Test(expected = NotEnoughDataHasBeenAddedException.class) public void testNoDataExceptionThrowing() throws NotEnoughDataHasBeenAddedException { IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); - processor.getY(15); + processor.getEstimatedY(15); } @Test(expected = NotEnoughDataHasBeenAddedException.class) @@ -159,7 +159,7 @@ public class IncrementalLinearRegressionTest { double y1 = 5; processor.addMeasuredPoint(x1, y1); - processor.getY(x1); + processor.getEstimatedY(x1); } /* @@ -198,8 +198,8 @@ public class IncrementalLinearRegressionTest { assertThat(regression.getSlope(), new IsCloseTo(1.00211681802045, EPSILON)); assertThat(regression.getIntercept(), new IsCloseTo(-0.262323073774029, EPSILON)); // ------------ End certified data tests - assertThat(regression.getY(0), new IsCloseTo(-0.262323073774029, EPSILON)); - assertThat(regression.getY(1), new IsCloseTo(1.00211681802045 - 0.26232307377402, EPSILON)); + assertThat(regression.getEstimatedY(0), new IsCloseTo(-0.262323073774029, EPSILON)); + assertThat(regression.getEstimatedY(1), new IsCloseTo(1.00211681802045 - 0.26232307377402, EPSILON)); } @Test diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index fc1a366edae..463f2b53d71 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -3,6 +3,7 @@ package com.sap.sailing.polars.mining; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; import com.sap.sailing.domain.tracking.GPSFixMoving; @@ -38,19 +39,30 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { @Override public RoundedAngleToTheWind getRoundedAngleToTheWind() { + return new RoundedAngleToTheWind(getAngleToTheWind()); + } + + public Bearing getAngleToTheWind() { SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint()); Bearing bearing = boatSpeed.getBearing(); - int angle = (int) Math.round(bearing.getDifferenceTo(wind.getBearing()).getDegrees()); - return new RoundedAngleToTheWind(angle); + return bearing.getDifferenceTo(wind.getBearing()); } @Override public WindSpeedLevel getWindSpeedLevel() { - Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint()); + Speed windSpeed = getWindSpeed(); WindSteppingWithMaxDistance stepping = defaultPolarSheetGenerationSettings.getWindStepping(); - int level = stepping.getLevelIndexForValue(wind.getKnots()); - return new WindSpeedLevel(level, stepping); + return new WindSpeedLevel(windSpeed, stepping); + } + + public Speed getWindSpeed() { + Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint()); + return wind; + } + + public Speed getBoatSpeed() { + return race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index 644d953e7a4..91aa5c4920d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -1,27 +1,39 @@ package com.sap.sailing.polars.mining; import java.util.HashMap; -import java.util.HashSet; import java.util.Map; -import java.util.Set; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; +import com.sap.sailing.polars.regression.BoatSpeedEstimator; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sse.datamining.AdditionalResultDataBuilder; import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.factories.GroupKeyFactory; import com.sap.sse.datamining.impl.components.GroupedDataEntry; import com.sap.sse.datamining.shared.GroupKey; public class IncrementalRegressionProcessor implements Processor> { - private final Map> container = new HashMap>(); + private final Map boatSpeedEstimators = new HashMap(); @Override public void onElement(GroupedDataEntry element) { GroupKey key = element.getKey(); - if (!container.containsKey(key)) { - container.put(key, new HashSet()); + BoatSpeedEstimator boatSpeedEstimator; + synchronized (boatSpeedEstimators) { + if (!boatSpeedEstimators.containsKey(key)) { + boatSpeedEstimators.put(key, new BoatSpeedEstimator()); + } + + boatSpeedEstimator = boatSpeedEstimators.get(key); } - Set set = container.get(key); - set.add(element.getDataEntry()); + GPSFixMovingWithPolarContext fix = element.getDataEntry(); + + boatSpeedEstimator.addData(fix.getWindSpeed().getKnots(), fix.getAngleToTheWind().getDegrees(), fix + .getBoatSpeed().getKnots()); } @Override @@ -40,8 +52,34 @@ public class IncrementalRegressionProcessor implements Processor getContainer(GroupKey compoundKey) { - return container.get(compoundKey); + public Speed estimateBoatSpeed(final Speed windSpeed, final Bearing angleToTheWind) + throws NotEnoughDataHasBeenAddedException { + PolarClusterKey key = new PolarClusterKey() { + + @Override + public WindSpeedLevel getWindSpeedLevel() { + return new WindSpeedLevel(windSpeed, PolarSheetGenerationSettingsImpl.createStandardPolarSettings() + .getWindStepping()); + } + + @Override + public RoundedAngleToTheWind getRoundedAngleToTheWind() { + return new RoundedAngleToTheWind(angleToTheWind); + } + }; + GroupKey compoundKey; + try { + compoundKey = GroupKeyFactory.createCompoundKeyFor(key, PolarDataDimensionCollectionFactory + .getClusterKeyDimensions().iterator()); + } catch (NoSuchMethodException e) { + e.printStackTrace(); + throw new RuntimeException(e); + } + BoatSpeedEstimator boatSpeedEstimator = boatSpeedEstimators.get(compoundKey); + if (boatSpeedEstimator == null) { + throw new NotEnoughDataHasBeenAddedException(); + } + return new KnotSpeedImpl(boatSpeedEstimator.estimateSpeed(windSpeed.getKnots(), angleToTheWind.getDegrees())); } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java new file mode 100644 index 00000000000..9eeb086fc5e --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java @@ -0,0 +1,24 @@ +package com.sap.sailing.polars.mining; + +import java.util.ArrayList; +import java.util.Collection; + +import com.sap.sse.datamining.factories.FunctionFactory; +import com.sap.sse.datamining.functions.Function; + +public class PolarDataDimensionCollectionFactory { + + public static Collection> getClusterKeyDimensions() throws NoSuchMethodException { + Collection> dimensions = new ArrayList<>(); + Function angleFunction = FunctionFactory + .createMethodWrappingFunction(PolarClusterKey.class.getMethod("getRoundedAngleToTheWind", + new Class[0])); + Function windSpeedFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class + .getMethod("getWindSpeedLevel", new Class[0])); + + dimensions.add(angleFunction); + dimensions.add(windSpeedFunction); + return dimensions; + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index ec3e1638dce..e4837311760 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -4,24 +4,24 @@ import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.List; -import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.common.impl.Util.Triple; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sse.datamining.components.Processor; -import com.sap.sse.datamining.factories.FunctionFactory; import com.sap.sse.datamining.functions.Function; import com.sap.sse.datamining.impl.components.GroupedDataEntry; import com.sap.sse.datamining.impl.components.ParallelFilteringProcessor; import com.sap.sse.datamining.impl.components.ParallelMultiDimensionalGroupingProcessor; -import com.sap.sse.datamining.shared.GroupKey; public class PolarDataMiner { @@ -56,7 +56,7 @@ public class PolarDataMiner { List>> grouperResultReceivers = new ArrayList>>(); grouperResultReceivers.add(incrementalRegressionProcessor); - Collection> dimensions = getClusterKeyDimensions(); + Collection> dimensions = PolarDataDimensionCollectionFactory.getClusterKeyDimensions(); Processor groupingProcessor = new ParallelMultiDimensionalGroupingProcessor( executor, grouperResultReceivers, dimensions); @@ -79,21 +79,6 @@ public class PolarDataMiner { } - - public Collection> getClusterKeyDimensions() throws NoSuchMethodException { - Collection> dimensions = new ArrayList<>(); - Function angleFunction = FunctionFactory - .createMethodWrappingFunction(PolarClusterKey.class.getMethod("getRoundedAngleToTheWind", - new Class[0])); - Function windSpeedFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class - .getMethod("getWindSpeedLevel", new Class[0])); - - dimensions.add(angleFunction); - dimensions.add(windSpeedFunction); - return dimensions; - } - - public void addFix(GPSFixMoving fix, Competitor competitor, TrackedRace trackedRace) { enrichingProcessor.onElement(new Triple(fix, trackedRace, competitor)); } @@ -104,8 +89,8 @@ public class PolarDataMiner { return isActive || hasQueue; } - public Set getContainer(GroupKey compoundKey) { - return incrementalRegressionProcessor.getContainer(compoundKey); + public Speed estimateBoatSpeed(Speed windSpeed, Bearing angleToTheWind) throws NotEnoughDataHasBeenAddedException { + return incrementalRegressionProcessor.estimateBoatSpeed(windSpeed, angleToTheWind); } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedAngleToTheWind.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedAngleToTheWind.java index 1b74c811eda..b0d48bdf912 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedAngleToTheWind.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/RoundedAngleToTheWind.java @@ -1,11 +1,18 @@ package com.sap.sailing.polars.mining; +import com.sap.sailing.domain.common.Bearing; + public class RoundedAngleToTheWind { private final int angle; - public RoundedAngleToTheWind(int angle) { - this.angle = angle; + public RoundedAngleToTheWind(Bearing angleToTheWind) { + this.angle = (int) Math.round(angleToTheWind.getDegrees()); + } + + @Override + public String toString() { + return "" + angle; } @Override diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java index aa01b1e74de..08857cf7d12 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/WindSpeedLevel.java @@ -1,5 +1,6 @@ package com.sap.sailing.polars.mining; +import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.WindStepping; public class WindSpeedLevel { @@ -7,11 +8,18 @@ public class WindSpeedLevel { private final int level; private final WindStepping stepping; - public WindSpeedLevel(int level, WindStepping stepping) { - this.level = level; + public WindSpeedLevel(Speed windSpeed, WindStepping stepping) { + this.level = stepping.getLevelIndexForValue(windSpeed.getKnots()); this.stepping = stepping; } + @Override + public String toString() { + // FIXME right now the toString equals for different steppings. This will resolve in an equal groupkey in the + // datamining framework. The current approach is to use only the default WindStepping. + return "" + level; + } + @Override public int hashCode() { final int prime = 31; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java new file mode 100644 index 00000000000..6ffd8719cfc --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java @@ -0,0 +1,50 @@ +package com.sap.sailing.polars.regression; + +import java.util.concurrent.locks.ReentrantReadWriteLock; + +import com.sap.sailing.polars.mining.IncrementalRegressionProcessor; +import com.sap.sailing.polars.regression.impl.IncrementalLeastSquaresProcessor; + +/** + * Combines two linear regression processors. One with the angle to the wind as the x axis and the other with the wind + * speed on the x axis. Both y axis represent the boat speed. + * + * {@link #estimateSpeed(double, double)} returns the average of the estimation of both regressions. + * + * Note that this class should only be used for a small interval on a polar sheet. The + * {@link IncrementalRegressionProcessor} is one example for that. It has one instance of the {@link BoatSpeedEstimator} + * for each wind speed level and rounded angle combination. + * + * @author Frederik Petersen (D054528) + * + */ +public class BoatSpeedEstimator { + + private IncrementalLinearRegressionProcessor angleRegression = new IncrementalLeastSquaresProcessor(); + + private IncrementalLinearRegressionProcessor windSpeedRegression = new IncrementalLeastSquaresProcessor(); + + private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + + public double estimateSpeed(double windSpeed, double angleToTheWind) throws NotEnoughDataHasBeenAddedException { + lock.readLock().lock(); + try { + double angleEstimated = angleRegression.getEstimatedY(angleToTheWind); + double windSpeedEstimated = windSpeedRegression.getEstimatedY(windSpeed); + return (angleEstimated + windSpeedEstimated) / 2.0; + } finally { + lock.readLock().unlock(); + } + } + + public void addData(double windSpeed, double angleToTheWind, double boatSpeed) { + lock.writeLock().lock(); + try { + angleRegression.addMeasuredPoint(angleToTheWind, boatSpeed); + windSpeedRegression.addMeasuredPoint(windSpeed, boatSpeed); + } finally { + lock.writeLock().unlock(); + } + } + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java index abda8ff6699..a22275a7ab3 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java @@ -2,7 +2,7 @@ package com.sap.sailing.polars.regression; public interface IncrementalLinearRegressionProcessor { - double getY(double x) throws NotEnoughDataHasBeenAddedException; + double getEstimatedY(double x) throws NotEnoughDataHasBeenAddedException; void addMeasuredPoint(double x, double y); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java index a73d8b23c03..97650e1fa26 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java @@ -12,7 +12,6 @@ import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; * * The difference is that some parts of the equation are memorized as fields and updated incrementally. * - * * @author Frederik Petersen (D054528) * */ @@ -33,7 +32,7 @@ public class IncrementalLeastSquaresProcessor implements IncrementalLinearRegres private int numberOfPointsAdded = 0; @Override - public double getY(double x) throws NotEnoughDataHasBeenAddedException { + public double getEstimatedY(double x) throws NotEnoughDataHasBeenAddedException { if (numberOfPointsAdded < 2) { throw new NotEnoughDataHasBeenAddedException(); } From 8c39229c1e532cf183f9d6937ceefe1fa999d36d Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Thu, 3 Apr 2014 17:12:43 +0200 Subject: [PATCH 29/72] Made sure that linear regression returns constant y for only one added data point --- .../polars/mining/test/PolarDataMinerTest.java | 11 +++++++++++ .../test/IncrementalLinearRegressionTest.java | 11 +++++++++-- .../impl/IncrementalLeastSquaresProcessor.java | 12 ++++++++++-- 3 files changed, 30 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java index ab19a436f32..3fc323ddf08 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java @@ -62,6 +62,9 @@ public class PolarDataMinerTest { GPSFixMoving fix2_2 = createMockedFix(13, 30, 54.425394, 10.177404, 41.8, 10.1); Competitor competitor2 = mock(Competitor.class); + GPSFixMoving fix3_1 = createMockedFix(13, 45, 54.873740, 10.193648, 43.2, 10); + Competitor competitor3 = mock(Competitor.class); + Map> fixesPerCompetitor = new HashMap>(); Set setForCompetitor1 = new HashSet(); @@ -74,7 +77,11 @@ public class PolarDataMinerTest { setForCompetitor2.add(fix2_2); fixesPerCompetitor.put(competitor2, setForCompetitor2); + Set setForCompetitor3 = new HashSet(); + setForCompetitor2.add(fix3_1); + fixesPerCompetitor.put(competitor3, setForCompetitor3); + TrackedRace trackedRace = createMockedTrackedRace(fixesPerCompetitor); for (Entry> entry : fixesPerCompetitor.entrySet()) { @@ -101,6 +108,10 @@ public class PolarDataMinerTest { Speed estimatedSpeed2 = miner.estimateBoatSpeed(new KnotSpeedImpl(15), new DegreeBearingImpl(-42)); assertThat(estimatedSpeed2, is(notNullValue())); assertThat(estimatedSpeed2.getKnots(), is(closeTo(10.05, EPSILON))); + + Speed estimatedSpeed3 = miner.estimateBoatSpeed(new KnotSpeedImpl(15), new DegreeBearingImpl(-42.8)); + assertThat(estimatedSpeed3, is(notNullValue())); + assertThat(estimatedSpeed3.getKnots(), is(closeTo(10, EPSILON))); } private GPSFixMoving createMockedFix(int hour, int minute, double lat, double lng, double bearingRaw, double speed) { diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java index 232e7116494..05823d6064f 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/regression/test/IncrementalLinearRegressionTest.java @@ -1,5 +1,6 @@ package com.sap.sailing.polars.regression.test; +import static org.hamcrest.Matchers.is; import static org.junit.Assert.assertThat; import static org.junit.Assert.assertTrue; @@ -151,7 +152,12 @@ public class IncrementalLinearRegressionTest { processor.getEstimatedY(15); } - @Test(expected = NotEnoughDataHasBeenAddedException.class) + /** + * Assert that y is constant for only one added data point + * + * @throws NotEnoughDataHasBeenAddedException + */ + @Test public void testRegressionWithOneDataPoint() throws NotEnoughDataHasBeenAddedException { IncrementalLinearRegressionProcessor processor = createRegressionProcessor(); @@ -159,7 +165,8 @@ public class IncrementalLinearRegressionTest { double y1 = 5; processor.addMeasuredPoint(x1, y1); - processor.getEstimatedY(x1); + double y = processor.getEstimatedY(20); + assertThat(y, is(y1)); } /* diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java index 97650e1fa26..d84061448a9 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java @@ -33,11 +33,19 @@ public class IncrementalLeastSquaresProcessor implements IncrementalLinearRegres @Override public double getEstimatedY(double x) throws NotEnoughDataHasBeenAddedException { - if (numberOfPointsAdded < 2) { + if (numberOfPointsAdded < 1) { throw new NotEnoughDataHasBeenAddedException(); } - return alpha0 + alpha1 * x; + double result; + if (numberOfPointsAdded < 2) { + // Return the y value of the only added data point. + result = meanOfY; + } else { + result = alpha0 + alpha1 * x; + } + + return result; } @Override From e3e0a62761c85c80bfd56f0e0303a56c39c08a2b Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 4 Apr 2014 09:52:50 +0200 Subject: [PATCH 30/72] Fixed launch configs and string messages --- .../com/sap/sailing/gwt/ui/client/StringMessages.properties | 2 +- .../com/sap/sailing/gwt/ui/client/StringMessages_de.properties | 1 - java/com.sap.sailing.server/SailingServer (No Proxy).launch | 2 +- .../SailingServer (No Proxy, Jetty on 8889).launch | 2 +- .../SailingServer (No Proxy, Jetty on 8889, Monitoring).launch | 2 +- .../SailingServer (No Proxy, No Tests).launch | 2 +- .../SailingServer (No Proxy, Remote Debug SAP VM).launch | 2 +- .../SailingServer (No Proxy, winddbTest).launch | 2 +- java/com.sap.sailing.server/SailingServer (Proxy).launch | 2 +- .../SailingServer (Proxy, Jetty on 8889).launch | 2 +- .../SailingServer (Proxy, Remote Debug SAP VM).launch | 2 +- .../SailingServer (Proxy, SwissTiming Live Simulation).launch | 2 +- .../SailingServer (Proxy, winddbTest).launch | 2 +- 13 files changed, 12 insertions(+), 13 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index ff8d76b5a8e..972f8375291 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -955,7 +955,7 @@ eventOverview=Event Overview start=Start showAddedScores=Show added scores for each entry in leaderboard ratio=ratio - +importWind=Import Wind importWindTooltip=Unchecking this option will drastically lower the amount of data transfered. authorName=Author name authorPriority=Author priority \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 502ee6d77c9..6dae002db2e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -951,7 +951,6 @@ eventOverview=Veranstaltungsübersicht start=Start showAddedScores=Addierte Punkte für jeden Eintrag im Leaderboard anzeigen ratio=Verhältnis - importWind=Wind importieren importWindTooltip=Diese Option zu deselektieren führt zu einer wesentlich kleineren übertragenen Datenmenge. authorName=Benutzername diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index 962237d1ed9..e34daf2b80a 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -32,5 +32,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch index 50ff96bbaba..c588acae890 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch @@ -26,6 +26,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Monitoring).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Monitoring).launch index 6dde69e94cf..4da3ff99eb6 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Monitoring).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Monitoring).launch @@ -26,6 +26,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch index ac78517119b..1b159821af3 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch @@ -25,5 +25,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch index da82e48bd39..f944c844c3c 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch @@ -25,5 +25,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch index d93891e7f2f..8ed5cbb5fdf 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch @@ -32,5 +32,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy).launch b/java/com.sap.sailing.server/SailingServer (Proxy).launch index 6ea896b8522..8e8a95aedd6 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy).launch @@ -33,6 +33,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch index f5fa069e375..4403b793a57 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch @@ -26,6 +26,6 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch index c3b527f9e4d..8aefe6998d1 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch @@ -26,5 +26,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch b/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch index cf142958cee..ea5e2564849 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch @@ -25,5 +25,5 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch index 21ebdc07640..a1ee575779d 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch @@ -26,6 +26,6 @@ - + From 1706f40d6c23b4a3a9b387106b9665c22796d4ac Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Mon, 7 Apr 2014 11:02:08 +0200 Subject: [PATCH 31/72] Added Boat class as a polar fix groupkey --- .../polars/mining/test/PolarDataMinerTest.java | 17 ++++++++++++----- .../mining/GPSFixMovingWithPolarContext.java | 6 ++++++ .../mining/IncrementalRegressionProcessor.java | 8 +++++++- .../sailing/polars/mining/PolarClusterKey.java | 8 ++++++-- .../PolarDataDimensionCollectionFactory.java | 4 ++++ .../sailing/polars/mining/PolarDataMiner.java | 6 ++++-- 6 files changed, 39 insertions(+), 10 deletions(-) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java index 3fc323ddf08..a306c26c289 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java @@ -54,6 +54,8 @@ public class PolarDataMinerTest { NotEnoughDataHasBeenAddedException { PolarDataMiner miner = new PolarDataMiner(); + BoatClass mockedBoatClass = mock(BoatClass.class); + GPSFixMoving fix1_1 = createMockedFix(13, 00, 54.431952, 10.186767, 45, 10.5); GPSFixMoving fix1_2 = createMockedFix(13, 30, 54.485034, 10.538303, 44.8, 10.6); Competitor competitor1 = mock(Competitor.class); @@ -82,7 +84,7 @@ public class PolarDataMinerTest { fixesPerCompetitor.put(competitor3, setForCompetitor3); - TrackedRace trackedRace = createMockedTrackedRace(fixesPerCompetitor); + TrackedRace trackedRace = createMockedTrackedRace(fixesPerCompetitor, mockedBoatClass); for (Entry> entry : fixesPerCompetitor.entrySet()) { Competitor competitor = entry.getKey(); @@ -101,15 +103,18 @@ public class PolarDataMinerTest { } } - Speed estimatedSpeed1 = miner.estimateBoatSpeed(new KnotSpeedImpl(15), new DegreeBearingImpl(-44.9)); + Speed estimatedSpeed1 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), new DegreeBearingImpl( + -44.9)); assertThat(estimatedSpeed1, is(notNullValue())); assertThat(estimatedSpeed1.getKnots(), is(closeTo(10.55, EPSILON))); - Speed estimatedSpeed2 = miner.estimateBoatSpeed(new KnotSpeedImpl(15), new DegreeBearingImpl(-42)); + Speed estimatedSpeed2 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), new DegreeBearingImpl( + -42)); assertThat(estimatedSpeed2, is(notNullValue())); assertThat(estimatedSpeed2.getKnots(), is(closeTo(10.05, EPSILON))); - Speed estimatedSpeed3 = miner.estimateBoatSpeed(new KnotSpeedImpl(15), new DegreeBearingImpl(-42.8)); + Speed estimatedSpeed3 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), new DegreeBearingImpl( + -42.8)); assertThat(estimatedSpeed3, is(notNullValue())); assertThat(estimatedSpeed3.getKnots(), is(closeTo(10, EPSILON))); } @@ -127,7 +132,8 @@ public class PolarDataMinerTest { return fix; } - private TrackedRace createMockedTrackedRace(Map> fixesPerCompetitor) { + private TrackedRace createMockedTrackedRace(Map> fixesPerCompetitor, + BoatClass mockedBoatClass) { TrackedRace trackedRace = mock(TrackedRace.class); for (Entry> competitorEntry : fixesPerCompetitor.entrySet()) { @@ -141,6 +147,7 @@ public class PolarDataMinerTest { } RaceDefinition mockedRaceDefinition = createMockedRaceDefinition(); + when(mockedRaceDefinition.getBoatClass()).thenReturn(mockedBoatClass); when(trackedRace.getRace()).thenReturn(mockedRaceDefinition); for (Competitor competitor : fixesPerCompetitor.keySet()) { diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index 463f2b53d71..da5515cf869 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -1,5 +1,6 @@ package com.sap.sailing.polars.mining; +import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; @@ -65,4 +66,9 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { return race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); } + @Override + public BoatClass getBoatClass() { + return race.getRace().getBoatClass(); + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index 91aa5c4920d..0e42bcc1bbe 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -3,6 +3,7 @@ package com.sap.sailing.polars.mining; import java.util.HashMap; import java.util.Map; +import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; @@ -52,7 +53,7 @@ public class IncrementalRegressionProcessor implements Processor[0])); Function windSpeedFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class .getMethod("getWindSpeedLevel", new Class[0])); + Function boatClassFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class + .getMethod("getBoatClass", new Class[0])); + dimensions.add(boatClassFunction); dimensions.add(angleFunction); dimensions.add(windSpeedFunction); return dimensions; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index e4837311760..ac7f728b2bc 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -8,6 +8,7 @@ import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; +import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; @@ -89,8 +90,9 @@ public class PolarDataMiner { return isActive || hasQueue; } - public Speed estimateBoatSpeed(Speed windSpeed, Bearing angleToTheWind) throws NotEnoughDataHasBeenAddedException { - return incrementalRegressionProcessor.estimateBoatSpeed(windSpeed, angleToTheWind); + public Speed estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind) + throws NotEnoughDataHasBeenAddedException { + return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind); } } From 445f7ceb1177fbcaf94ec8fe490415ec817c4f56 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 9 Apr 2014 10:56:17 +0200 Subject: [PATCH 32/72] Added boat class to polar cluster key +created speed cluster for data mining framework --- .../mining/test/PolarDataMinerTest.java | 22 +-- .../META-INF/MANIFEST.MF | 3 +- .../sap/sailing/polars/PolarDataService.java | 5 +- .../polars/impl/PolarDataServiceImpl.java | 139 +++--------------- .../mining/GPSFixMovingWithPolarContext.java | 2 +- .../IncrementalRegressionProcessor.java | 9 +- .../sailing/polars/mining/PolarDataMiner.java | 3 +- .../polars/windcluster/SpeedCluster.java | 77 ++++++++++ .../META-INF/MANIFEST.MF | 1 + .../gateway/jaxrs/api/PolarResource.java | 4 +- .../META-INF/MANIFEST.MF | 2 + 11 files changed, 129 insertions(+), 138 deletions(-) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/windcluster/SpeedCluster.java diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java index a306c26c289..a8e605d5780 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java @@ -23,10 +23,10 @@ import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.RaceDefinition; +import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.Bearing; 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.TimePoint; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; @@ -103,20 +103,20 @@ public class PolarDataMinerTest { } } - Speed estimatedSpeed1 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), new DegreeBearingImpl( - -44.9)); + SpeedWithConfidence estimatedSpeed1 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), + new DegreeBearingImpl(44.9)); assertThat(estimatedSpeed1, is(notNullValue())); - assertThat(estimatedSpeed1.getKnots(), is(closeTo(10.55, EPSILON))); + assertThat(estimatedSpeed1.getObject().getKnots(), is(closeTo(10.55, EPSILON))); - Speed estimatedSpeed2 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), new DegreeBearingImpl( - -42)); + SpeedWithConfidence estimatedSpeed2 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), + new DegreeBearingImpl(42)); assertThat(estimatedSpeed2, is(notNullValue())); - assertThat(estimatedSpeed2.getKnots(), is(closeTo(10.05, EPSILON))); + assertThat(estimatedSpeed2.getObject().getKnots(), is(closeTo(10.05, EPSILON))); - Speed estimatedSpeed3 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), new DegreeBearingImpl( - -42.8)); + SpeedWithConfidence estimatedSpeed3 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), + new DegreeBearingImpl(42.8)); assertThat(estimatedSpeed3, is(notNullValue())); - assertThat(estimatedSpeed3.getKnots(), is(closeTo(10, EPSILON))); + assertThat(estimatedSpeed3.getObject().getKnots(), is(closeTo(10, EPSILON))); } private GPSFixMoving createMockedFix(int hour, int minute, double lat, double lng, double bearingRaw, double speed) { @@ -165,7 +165,7 @@ public class PolarDataMinerTest { when(trackedRace.getStartOfRace()).thenReturn(startOfRace); when(trackedRace.getEndOfRace()).thenReturn(endOfRace); - Bearing windBearing = new DegreeBearingImpl(0); + Bearing windBearing = new DegreeBearingImpl(180); SpeedWithBearing windSpeed = new KnotSpeedWithBearingImpl(15, windBearing); Wind wind = new WindImpl(new DegreePosition(54.431952, 10.186767), startOfRace, windSpeed); diff --git a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF index a50a27adb4a..ae31203ffc7 100644 --- a/java/com.sap.sailing.polars/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars/META-INF/MANIFEST.MF @@ -8,9 +8,10 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Bundle-ClassPath: . Export-Package: com.sap.sailing.polars, + com.sap.sailing.polars.analysis, com.sap.sailing.polars.data, com.sap.sailing.polars.factory, - com.sap.sailing.polars.analysis + com.sap.sailing.polars.regression Bundle-ActivationPolicy: lazy Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 1709081aa03..46dd07f322e 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -15,6 +15,7 @@ import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.data.PolarFix; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; /** * Public Facade interface allowing access to the polars of {@link TrackedRace}s and per {@link BoatClass}. @@ -31,10 +32,10 @@ public interface PolarDataService { * @param bearingToTheWind * Boat's direction relative to the wind. either in -180 -> +180 or 0 -> 359 degrees * @return The speed the boat is moving at for the specified wind and bearing according to the polar diagram. - * @throws NoPolarDataAvailableException + * @throws NotEnoughDataHasBeenAddedException */ SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) - throws NoPolarDataAvailableException; + throws NotEnoughDataHasBeenAddedException; /** * Generates a polar sheet for geven races and settings using the provided executor for the worker threads. This diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index e21e1293f0d..2fefbbd8c49 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -6,34 +6,28 @@ import java.util.Map; import java.util.Set; import java.util.concurrent.ExecutionException; import java.util.concurrent.Executor; -import java.util.logging.Logger; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.SpeedWithConfidence; -import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; -import com.sap.sailing.domain.common.PolarSheetsHistogramData; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.impl.DegreeBearingImpl; -import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sailing.polars.NoPolarDataAvailableException; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.aggregation.PolarFixAggregator; import com.sap.sailing.polars.aggregation.SimplePolarFixRaceInterval; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.analysis.impl.PolarSheetAnalyzerImpl; -import com.sap.sailing.polars.caching.NoCacheEntryException; import com.sap.sailing.polars.caching.PolarFixCache; import com.sap.sailing.polars.caching.PolarSheetPerBoatClassCache; import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.polars.generation.PolarSheetGenerator; import com.sap.sailing.polars.mining.PolarDataMiner; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sailing.util.SmartFutureCache; /** @@ -46,8 +40,6 @@ import com.sap.sailing.util.SmartFutureCache; */ public class PolarDataServiceImpl implements PolarDataService { - private static final Logger logger = Logger.getLogger(PolarDataServiceImpl.class.getName()); - private final PolarFixCache polarFixCache; private final PolarSheetPerBoatClassCache polarSheetPerBoatClassCache; @@ -65,97 +57,8 @@ public class PolarDataServiceImpl implements PolarDataService { @Override public SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) - throws NoPolarDataAvailableException { - PolarSheetsData data = polarSheetPerBoatClassCache.get(boatClass, false); - double angleToTheWind = bearingToTheWind.getDegrees(); - if (angleToTheWind < 0) { - angleToTheWind = 360 + angleToTheWind; - } - int angleToTheWindFloor = (int) Math.floor(bearingToTheWind.getDegrees()); - int angleToTheWindCeiling = (int) Math.ceil(bearingToTheWind.getDegrees()); - int windSpeedIndexFloor = data.getStepping().getLevelIndexFloorForValue(windSpeed.getKnots()); - boolean useFloor = true; - boolean useCeiling = true; - int windSpeedIndexCeiling; - if (windSpeedIndexFloor == -1) { - useFloor = false; - windSpeedIndexCeiling = 0; - } else if (windSpeedIndexFloor == data.getStepping().getRawStepping().length - 1) { - windSpeedIndexCeiling = -1; - useCeiling = false; - } else { - windSpeedIndexCeiling = data.getStepping().getLevelIndexCeilingForValue(windSpeed.getKnots()); - } - SpeedWithConfidence speed; - if (useFloor && useCeiling) { - double windSpeedIndexDistanceToFloor = data.getStepping().getDistanceToLevelFloor(windSpeed.getKnots()); - SpeedWithConfidence windSpeedFloor = getInterpolatedSpeedBetweenAngles(boatClass, data, - windSpeedIndexFloor, angleToTheWind, angleToTheWindFloor, angleToTheWindCeiling); - SpeedWithConfidence windSpeedCeiling = getInterpolatedSpeedBetweenAngles(boatClass, data, - windSpeedIndexCeiling, angleToTheWind, angleToTheWindFloor, angleToTheWindCeiling); - speed = interpolateSpeed(windSpeedFloor, windSpeedCeiling, windSpeedIndexDistanceToFloor); - } else if (useFloor) { - speed = getInterpolatedSpeedBetweenAngles(boatClass, data, windSpeedIndexFloor, angleToTheWind, - angleToTheWindFloor, angleToTheWindCeiling); - } else { - speed = getInterpolatedSpeedBetweenAngles(boatClass, data, windSpeedIndexCeiling, angleToTheWind, - angleToTheWindFloor, angleToTheWindCeiling); - } - - return speed; - } - - private SpeedWithConfidence getInterpolatedSpeedBetweenAngles(BoatClass boatClass, PolarSheetsData data, - int windSpeedIndex, double angleToTheWind, int angleToTheWindFloor, int angleToTheWindCeiling) - throws NoPolarDataAvailableException { - SpeedWithConfidence speedFloor = getSpeedForConcreteValues(data, windSpeedIndex, angleToTheWindFloor, - boatClass); - SpeedWithConfidence speedCeiling = getSpeedForConcreteValues(data, windSpeedIndex, - angleToTheWindCeiling, boatClass); - SpeedWithConfidence speed = interpolateSpeed(speedFloor, speedCeiling, angleToTheWind - - angleToTheWindFloor); - return speed; - } - - private SpeedWithConfidence interpolateSpeed(SpeedWithConfidence speedFloor, - SpeedWithConfidence speedCeiling, double distanceToFloor) { - double distanceOverConfidenceFactor = 0.8; - double floorFactor = distanceOverConfidenceFactor * (1 - distanceToFloor) + (1 - distanceOverConfidenceFactor) - * speedFloor.getConfidence(); - double ceilingFactor = distanceOverConfidenceFactor * distanceToFloor + (1 - distanceOverConfidenceFactor) - * speedCeiling.getConfidence(); - double alpha = 1 / (floorFactor + ceilingFactor); - double scaledFactorFloor = floorFactor * alpha; - double scaledFactorCeiling = ceilingFactor * alpha; - // FIXME remove this assert when testing is done - assert (1 - (scaledFactorCeiling + scaledFactorFloor) < 0.005 && 1 - (scaledFactorCeiling + scaledFactorFloor) > -0.005); - double interpolatedSpeedInKnots = scaledFactorFloor * speedFloor.getObject().getKnots() + scaledFactorCeiling - * speedCeiling.getObject().getKnots(); - double interpolatedConfidence = scaledFactorFloor * speedFloor.getConfidence() + scaledFactorCeiling - * speedCeiling.getConfidence(); - Speed interpolatedSpeed = new KnotSpeedImpl(interpolatedSpeedInKnots); - int numberOfUnderlyingFixes = speedFloor.getRelativeTo() + speedCeiling.getRelativeTo(); - return new SpeedWithConfidenceImpl(interpolatedSpeed, interpolatedConfidence, numberOfUnderlyingFixes); - } - - private SpeedWithConfidence getSpeedForConcreteValues(PolarSheetsData data, int windSpeedIndex, - int angleToTheWind, BoatClass boatClass) throws NoPolarDataAvailableException { - Number rawSpeed = data.getAveragedPolarDataByWindSpeed()[windSpeedIndex][angleToTheWind]; - Speed speed = new KnotSpeedImpl(rawSpeed.doubleValue()); - PolarSheetsHistogramData histogramData = data.getHistogramDataMap().get(windSpeedIndex).get(angleToTheWind); - SpeedWithConfidenceImpl speedWithConfidence; - if (histogramData != null) { - double confidence = histogramData.getConfidenceMeasure(); - int numberOfUnderlyingFixes = histogramData.getDataCount(); - speedWithConfidence = new SpeedWithConfidenceImpl(speed, confidence, - numberOfUnderlyingFixes); - } else { - NoPolarDataAvailableException e = new NoPolarDataAvailableException(boatClass, new KnotSpeedImpl(data - .getStepping().getRawStepping()[windSpeedIndex]), new DegreeBearingImpl(angleToTheWind)); - logger.warning(e.getLocalizedMessage()); - throw e; - } - return speedWithConfidence; + throws NotEnoughDataHasBeenAddedException { + return polarDataMiner.estimateBoatSpeed(boatClass, windSpeed, bearingToTheWind); } @@ -164,24 +67,24 @@ public class PolarDataServiceImpl implements PolarDataService { Executor executor) throws InterruptedException, ExecutionException { Set fixes; // If settings are default, look into cache - if (settings.areDefault()) { - try { - fixes = polarFixCache.getFixesForTrackedRaces(trackedRaces); - } catch (NoCacheEntryException e) { - // If there is no cache entry for at least one of the races: Aggregate for non-cached races. - fixes = e.getCachedResultList(); - PolarFixAggregator aggregator = new PolarFixAggregator( - new SimplePolarFixRaceInterval(e.getNotCached()), - settings, executor); - aggregator.startPolarFixAggregation(); - fixes.addAll(aggregator.getAggregationResultAsSingleList()); - } - } else { - PolarFixAggregator aggregator = new PolarFixAggregator(new SimplePolarFixRaceInterval(trackedRaces), - settings, executor); - aggregator.startPolarFixAggregation(); - fixes = aggregator.getAggregationResultAsSingleList(); - } + // if (settings.areDefault()) { + // try { + // fixes = polarFixCache.getFixesForTrackedRaces(trackedRaces); + // } catch (NoCacheEntryException e) { + // // If there is no cache entry for at least one of the races: Aggregate for non-cached races. + // fixes = e.getCachedResultList(); + // PolarFixAggregator aggregator = new PolarFixAggregator( + // new SimplePolarFixRaceInterval(e.getNotCached()), + // settings, executor); + // aggregator.startPolarFixAggregation(); + // fixes.addAll(aggregator.getAggregationResultAsSingleList()); + // } + // } else { + PolarFixAggregator aggregator = new PolarFixAggregator(new SimplePolarFixRaceInterval(trackedRaces), settings, + executor); + aggregator.startPolarFixAggregation(); + fixes = aggregator.getAggregationResultAsSingleList(); + // } PolarSheetGenerator generator = new PolarSheetGenerator(fixes, settings); return generator.generate(); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index da5515cf869..3bb70da22e0 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -47,7 +47,7 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint()); Bearing bearing = boatSpeed.getBearing(); - return bearing.getDifferenceTo(wind.getBearing()); + return wind.getFrom().getDifferenceTo(bearing); } @Override diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index 0e42bcc1bbe..2e4f1051eef 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -4,6 +4,8 @@ import java.util.HashMap; import java.util.Map; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; @@ -53,7 +55,8 @@ public class IncrementalRegressionProcessor implements Processor estimateBoatSpeed(final BoatClass boatClass, final Speed windSpeed, + final Bearing angleToTheWind) throws NotEnoughDataHasBeenAddedException { PolarClusterKey key = new PolarClusterKey() { @@ -85,7 +88,9 @@ public class IncrementalRegressionProcessor implements Processor(speedWithoutConfidence, /* FIXME */1.0, /* FIXME */0); } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index ac7f728b2bc..f5784ed5cc5 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -10,6 +10,7 @@ import java.util.concurrent.TimeUnit; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.Speed; @@ -90,7 +91,7 @@ public class PolarDataMiner { return isActive || hasQueue; } - public Speed estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind) + public SpeedWithConfidence estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind) throws NotEnoughDataHasBeenAddedException { return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windcluster/SpeedCluster.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windcluster/SpeedCluster.java new file mode 100644 index 00000000000..ea60dde7244 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windcluster/SpeedCluster.java @@ -0,0 +1,77 @@ +package com.sap.sailing.polars.windcluster; + +import java.util.ArrayList; +import java.util.Collection; + +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sse.datamining.data.Cluster; +import com.sap.sse.datamining.data.ClusterBoundary; +import com.sap.sse.datamining.impl.data.ClusterWithBoundaries; +import com.sap.sse.datamining.impl.data.ComparableClusterBoundary; +import com.sap.sse.datamining.impl.data.ComparisonStrategy; +import com.sap.sse.datamining.impl.data.FixClusterGroup; + +public class SpeedCluster extends FixClusterGroup { + + /** + * + * @param name + * @param levelMidsInKnots sorted low -> high. E.g. 2 4 6 10 15 20 30 + * @param maxDistanceInKnots the clusters will max span <-maxDistanceInKnots-|mid|-maxDistanceinKnots-> + */ + public SpeedCluster(String name, double[] levelMidsInKnots, double maxDistanceInKnots) { + super(name, createClustersForLevelMids(levelMidsInKnots, maxDistanceInKnots)); + } + + private static Collection> createClustersForLevelMids(double[] levelMidsInKnots, + double maxDistanceInKnots) { + ArrayList> clusterList = new ArrayList>(); + for (int index = 0; index < levelMidsInKnots.length; index++) { + ClusterBoundary lowerBoundary = createLowerBoundary(levelMidsInKnots, maxDistanceInKnots, index); + ClusterBoundary upperBoundary = createUpperBoundary(levelMidsInKnots, maxDistanceInKnots, index); + Cluster cluster = new ClusterWithBoundaries(levelMidsInKnots[index] + "kn", lowerBoundary, + upperBoundary); + clusterList.add(cluster); + } + return null; + } + + private static ClusterBoundary createUpperBoundary(double[] levelMidsInKnots, double maxDistanceInKnots, + int index) { + ClusterBoundary upperBoundary; + double upperBoundaryValue; + if (index == levelMidsInKnots.length - 1) { + upperBoundaryValue = levelMidsInKnots[index] + maxDistanceInKnots; + } else { + double biggestPossibleUpperBoundary = levelMidsInKnots[index] + maxDistanceInKnots; + double midBetweenCurrentLevelMidAndUpperLevelMid = levelMidsInKnots[index] + + (0.5 * (levelMidsInKnots[index + 1] - levelMidsInKnots[index])); + upperBoundaryValue = biggestPossibleUpperBoundary < midBetweenCurrentLevelMidAndUpperLevelMid ? biggestPossibleUpperBoundary + : midBetweenCurrentLevelMidAndUpperLevelMid; + } + upperBoundary = new ComparableClusterBoundary(new KnotSpeedImpl(upperBoundaryValue), + ComparisonStrategy.LOWER_THAN); + return upperBoundary; + } + + private static ClusterBoundary createLowerBoundary(double[] levelMidsInKnots, double maxDistanceInKnots, + int index) { + ClusterBoundary lowerBoundary; + double lowerBoundaryValue; + if (index > 0) { + double lowestPossibleLowerBoundary = levelMidsInKnots[index] - maxDistanceInKnots; + double midBetweenLowerLevelMidAndCurrentLevelMid = levelMidsInKnots[index - 1] + + (0.5 * (levelMidsInKnots[index] - levelMidsInKnots[index - 1])); + lowerBoundaryValue = lowestPossibleLowerBoundary > midBetweenLowerLevelMidAndCurrentLevelMid ? lowestPossibleLowerBoundary + : midBetweenLowerLevelMidAndCurrentLevelMid; + } else { + double lowestPossibleLowerBoundary = levelMidsInKnots[index] - maxDistanceInKnots; + lowerBoundaryValue = lowestPossibleLowerBoundary <= 0 ? 0 : lowestPossibleLowerBoundary; + } + lowerBoundary = new ComparableClusterBoundary(new KnotSpeedImpl(lowerBoundaryValue), + ComparisonStrategy.GREATER_EQUALS_THAN); + return lowerBoundary; + } + +} diff --git a/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF index ea844222c52..5a35898e7fe 100644 --- a/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.gateway/META-INF/MANIFEST.MF @@ -6,6 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: com.sap.sailing.polars, + com.sap.sailing.polars.regression, com.sap.sailing.server, com.sap.sailing.server.operationaltransformation, javax.ws.rs;version="1.1.1", diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java index 474767b9762..cc60e5fc29b 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java @@ -15,7 +15,7 @@ import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; -import com.sap.sailing.polars.NoPolarDataAvailableException; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; import com.sap.sailing.server.gateway.serialization.impl.SpeedWithConfidenceWithIntegerRelationJsonSerializer; @@ -37,7 +37,7 @@ public class PolarResource extends AbstractSailingServerResource { SpeedWithConfidenceWithIntegerRelationJsonSerializer serializer = new SpeedWithConfidenceWithIntegerRelationJsonSerializer(); JSONObject jsonObj = serializer.serialize(speedWithConfidence); responseBuilder = Response.ok(jsonObj.toJSONString(), MediaType.APPLICATION_JSON); - } catch (NoPolarDataAvailableException e) { + } catch (NotEnoughDataHasBeenAddedException e) { responseBuilder = Response.noContent(); } diff --git a/java/com.sap.sse.datamining/META-INF/MANIFEST.MF b/java/com.sap.sse.datamining/META-INF/MANIFEST.MF index 9efd0c25a72..eb5f3819625 100644 --- a/java/com.sap.sse.datamining/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.datamining/META-INF/MANIFEST.MF @@ -8,6 +8,7 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.7 Import-Package: org.osgi.framework;version="1.7.0" Export-Package: com.sap.sse.datamining;uses:="com.sap.sse.datamining.functions,com.sap.sse.datamining.shared", com.sap.sse.datamining.components;uses:="com.sap.sse.datamining.shared", + com.sap.sse.datamining.data, com.sap.sse.datamining.data.deprecated, com.sap.sse.datamining.factories;uses:="com.sap.sse.datamining.impl.functions", com.sap.sse.datamining.functions;uses:="com.sap.sse.datamining.shared.dto,com.sap.sse.datamining.i18n,com.sap.sse.datamining.components", @@ -23,6 +24,7 @@ Export-Package: com.sap.sse.datamining;uses:="com.sap.sse.datamining.functions,c com.sap.sse.datamining.impl.components.deprecated, com.sap.sse.datamining.impl.criterias;uses:="com.sap.sse.datamining.data,com.sap.sse.datamining.functions,com.sap.sse.datamining.components", com.sap.sse.datamining.impl.criterias.deprecated, + com.sap.sse.datamining.impl.data, com.sap.sse.datamining.impl.data.deprecated;uses:="com.sap.sse.datamining.data", com.sap.sse.datamining.impl.deprecated, com.sap.sse.datamining.impl.i18n;uses:="com.sap.sse.datamining.shared,com.sap.sse.datamining.i18n", From 7333c569036ca3a9d7d5a4f59a1b72aa5e1a4013 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 9 Apr 2014 11:49:03 +0200 Subject: [PATCH 33/72] Added test for speed cluster group --- .../clusters/test/SpeedClusterGroupTest.java | 72 +++++++++++++++++++ .../SpeedClusterGroup.java} | 18 +++-- 2 files changed, 84 insertions(+), 6 deletions(-) create mode 100644 java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/clusters/test/SpeedClusterGroupTest.java rename java/com.sap.sailing.polars/src/com/sap/sailing/polars/{windcluster/SpeedCluster.java => clusters/SpeedClusterGroup.java} (82%) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/clusters/test/SpeedClusterGroupTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/clusters/test/SpeedClusterGroupTest.java new file mode 100644 index 00000000000..b1e3e35aa02 --- /dev/null +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/clusters/test/SpeedClusterGroupTest.java @@ -0,0 +1,72 @@ +package com.sap.sailing.polars.clusters.test; + +import static org.hamcrest.Matchers.equalTo; +import static org.hamcrest.Matchers.notNullValue; +import static org.hamcrest.Matchers.nullValue; +import static org.junit.Assert.assertThat; + +import org.junit.Test; + +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.polars.clusters.SpeedClusterGroup; +import com.sap.sse.datamining.data.Cluster; + +public class SpeedClusterGroupTest { + + @Test + public void testWithZeroLevels() { + double[] levelMidsInKnots = {}; + SpeedClusterGroup group = new SpeedClusterGroup("test", levelMidsInKnots, 4); + assertThat(group.getClusterFor(new KnotSpeedImpl(0)), nullValue()); + assertThat(group.getClusterFor(new KnotSpeedImpl(6)), nullValue()); + } + + @Test + public void testWithOneLevelReachingZero() { + double[] levelMidsInKnots = { 4 }; + SpeedClusterGroup group = new SpeedClusterGroup("test", levelMidsInKnots, 4); + Cluster clusterForZero = group.getClusterFor(new KnotSpeedImpl(0)); + assertThat(clusterForZero, notNullValue()); + Cluster clusterForSix = group.getClusterFor(new KnotSpeedImpl(6)); + assertThat(clusterForSix, notNullValue()); + assertThat(clusterForSix == clusterForZero, equalTo(true)); + assertThat(group.getClusterFor(new KnotSpeedImpl(9)), nullValue()); + } + + @Test + public void testWithOneLevelNotReachingZero() { + double[] levelMidsInKnots = { 7 }; + SpeedClusterGroup group = new SpeedClusterGroup("test", levelMidsInKnots, 4); + Cluster clusterForNine = group.getClusterFor(new KnotSpeedImpl(9)); + assertThat(clusterForNine, notNullValue()); + Cluster clusterForSix = group.getClusterFor(new KnotSpeedImpl(6)); + assertThat(clusterForSix, notNullValue()); + assertThat(clusterForSix == clusterForNine, equalTo(true)); + assertThat(group.getClusterFor(new KnotSpeedImpl(0)), nullValue()); + } + + @Test + public void testWithMultipleLevelsWithNoEmptyRoomInbetween() { + double[] levelMidsInKnots = { 4, 6, 8, 10, 12, 15 }; + SpeedClusterGroup group = new SpeedClusterGroup("test", levelMidsInKnots, 4); + Cluster clusterForFourteen = group.getClusterFor(new KnotSpeedImpl(14)); + assertThat(clusterForFourteen, notNullValue()); + Cluster clusterForThirteen = group.getClusterFor(new KnotSpeedImpl(13)); + assertThat(clusterForThirteen, notNullValue()); + assertThat(clusterForThirteen == clusterForFourteen, equalTo(false)); + assertThat(group.getClusterFor(new KnotSpeedImpl(20)), nullValue()); + } + + @Test + public void testWithMultipleLevelsWithRoomInbetween() { + double[] levelMidsInKnots = { 4, 6, 15 }; + SpeedClusterGroup group = new SpeedClusterGroup("test", levelMidsInKnots, 4); + Cluster clusterForFourteen = group.getClusterFor(new KnotSpeedImpl(14)); + assertThat(clusterForFourteen, notNullValue()); + Cluster clusterForSeven = group.getClusterFor(new KnotSpeedImpl(7)); + assertThat(clusterForSeven, notNullValue()); + assertThat(clusterForSeven == clusterForFourteen, equalTo(false)); + assertThat(group.getClusterFor(new KnotSpeedImpl(10.5)), nullValue()); + } +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windcluster/SpeedCluster.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/clusters/SpeedClusterGroup.java similarity index 82% rename from java/com.sap.sailing.polars/src/com/sap/sailing/polars/windcluster/SpeedCluster.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/clusters/SpeedClusterGroup.java index ea60dde7244..9e7bbb58109 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windcluster/SpeedCluster.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/clusters/SpeedClusterGroup.java @@ -1,4 +1,4 @@ -package com.sap.sailing.polars.windcluster; +package com.sap.sailing.polars.clusters; import java.util.ArrayList; import java.util.Collection; @@ -12,15 +12,21 @@ import com.sap.sse.datamining.impl.data.ComparableClusterBoundary; import com.sap.sse.datamining.impl.data.ComparisonStrategy; import com.sap.sse.datamining.impl.data.FixClusterGroup; -public class SpeedCluster extends FixClusterGroup { +public class SpeedClusterGroup extends FixClusterGroup { /** + * A {@link SpeedClusterGroup} lets the user set the level mids for all its clusters. The Boundaries will + * automatically be determined. They are in the middle between each level mid but only if the distance between level + * mid and boundary is smaller or equal {@code maxDistance} + * * * @param name - * @param levelMidsInKnots sorted low -> high. E.g. 2 4 6 10 15 20 30 - * @param maxDistanceInKnots the clusters will max span <-maxDistanceInKnots-|mid|-maxDistanceinKnots-> + * @param levelMidsInKnots + * sorted low -> high. E.g. [2,4,6,10,15,20,30] + * @param maxDistanceInKnots + * the clusters will max span <-maxDistanceInKnots-|mid|-maxDistanceinKnots-> */ - public SpeedCluster(String name, double[] levelMidsInKnots, double maxDistanceInKnots) { + public SpeedClusterGroup(String name, double[] levelMidsInKnots, double maxDistanceInKnots) { super(name, createClustersForLevelMids(levelMidsInKnots, maxDistanceInKnots)); } @@ -34,7 +40,7 @@ public class SpeedCluster extends FixClusterGroup { upperBoundary); clusterList.add(cluster); } - return null; + return clusterList; } private static ClusterBoundary createUpperBoundary(double[] levelMidsInKnots, double maxDistanceInKnots, From 85c21ced4b3ddfeb679bfb4f6e97649d6b4ac356 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 11 Apr 2014 11:59:07 +0200 Subject: [PATCH 34/72] Polar Data Mining now uses the cluster group instead of windstepping for grouping wind speeds to clusters --- .../mining/GPSFixMovingWithPolarContext.java | 15 +++++++------ .../IncrementalRegressionProcessor.java | 21 ++++++++++++------- .../polars/mining/PolarClusterKey.java | 6 ++++-- .../PolarDataDimensionCollectionFactory.java | 2 +- .../sailing/polars/mining/PolarDataMiner.java | 11 ++++++++-- ...edClusterGroupFromWindSteppingCreator.java | 21 +++++++++++++++++++ 6 files changed, 55 insertions(+), 21 deletions(-) create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedClusterGroupFromWindSteppingCreator.java diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index 3bb70da22e0..4a9debd73e9 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -3,27 +3,27 @@ package com.sap.sailing.polars.mining; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.Bearing; -import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; -import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.Wind; +import com.sap.sse.datamining.data.Cluster; +import com.sap.sse.datamining.data.ClusterGroup; public class GPSFixMovingWithPolarContext implements PolarClusterKey { private final GPSFixMoving fix; private final TrackedRace race; private final Competitor competitor; - private final PolarSheetGenerationSettings defaultPolarSheetGenerationSettings; + private final ClusterGroup windSpeedClusterGroup; public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor, - PolarSheetGenerationSettings defaultPolarSheetGenerationSettings) { + ClusterGroup windSpeedClusterGroup) { this.fix = fix; this.race = race; this.competitor = competitor; - this.defaultPolarSheetGenerationSettings = defaultPolarSheetGenerationSettings; + this.windSpeedClusterGroup = windSpeedClusterGroup; } public GPSFixMoving getFix() { @@ -51,10 +51,9 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { } @Override - public WindSpeedLevel getWindSpeedLevel() { + public Cluster getWindSpeedCluster() { Speed windSpeed = getWindSpeed(); - WindSteppingWithMaxDistance stepping = defaultPolarSheetGenerationSettings.getWindStepping(); - return new WindSpeedLevel(windSpeed, stepping); + return windSpeedClusterGroup.getClusterFor(windSpeed); } public Speed getWindSpeed() { diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index 2e4f1051eef..021aa0fe570 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -9,11 +9,12 @@ import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; -import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.polars.regression.BoatSpeedEstimator; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sse.datamining.AdditionalResultDataBuilder; import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.data.Cluster; +import com.sap.sse.datamining.data.ClusterGroup; import com.sap.sse.datamining.factories.GroupKeyFactory; import com.sap.sse.datamining.impl.components.GroupedDataEntry; import com.sap.sse.datamining.shared.GroupKey; @@ -22,6 +23,12 @@ public class IncrementalRegressionProcessor implements Processor boatSpeedEstimators = new HashMap(); + private final ClusterGroup speedClusterGroup; + + public IncrementalRegressionProcessor(ClusterGroup speedClusterGroup) { + this.speedClusterGroup = speedClusterGroup; + } + @Override public void onElement(GroupedDataEntry element) { GroupKey key = element.getKey(); @@ -59,13 +66,6 @@ public class IncrementalRegressionProcessor implements Processor getWindSpeedCluster() { + return speedClusterGroup.getClusterFor(windSpeed); + } }; GroupKey compoundKey; try { diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarClusterKey.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarClusterKey.java index f455e44793e..51e89b3298d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarClusterKey.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarClusterKey.java @@ -1,6 +1,8 @@ package com.sap.sailing.polars.mining; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.Speed; +import com.sap.sse.datamining.data.Cluster; import com.sap.sse.datamining.shared.annotations.Dimension; public interface PolarClusterKey { @@ -14,10 +16,10 @@ public interface PolarClusterKey { /** * - * @return wind speed level + * @return wind speed cluster */ @Dimension(messageKey = "windSpeed") - WindSpeedLevel getWindSpeedLevel(); + Cluster getWindSpeedCluster(); @Dimension(messageKey = "boatClass") BoatClass getBoatClass(); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java index 6e0efb553e5..2565470cf5d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java @@ -15,7 +15,7 @@ public class PolarDataDimensionCollectionFactory { .createMethodWrappingFunction(PolarClusterKey.class.getMethod("getRoundedAngleToTheWind", new Class[0])); Function windSpeedFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class - .getMethod("getWindSpeedLevel", new Class[0])); + .getMethod("getWindSpeedCluster", new Class[0])); Function boatClassFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class .getMethod("getBoatClass", new Class[0])); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index f5784ed5cc5..cf4e33c3861 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -16,10 +16,12 @@ import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.common.impl.Util.Triple; +import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sse.datamining.components.Processor; +import com.sap.sse.datamining.data.ClusterGroup; import com.sap.sse.datamining.functions.Function; import com.sap.sse.datamining.impl.components.GroupedDataEntry; import com.sap.sse.datamining.impl.components.ParallelFilteringProcessor; @@ -54,7 +56,10 @@ public class PolarDataMiner { private void setUpWorkflow() throws ClassCastException, NoSuchMethodException, SecurityException { - incrementalRegressionProcessor = new IncrementalRegressionProcessor(); + WindSteppingWithMaxDistance stepping = defaultPolarSheetGenerationSettings.getWindStepping(); + final ClusterGroup speedClusterGroup = SpeedClusterGroupFromWindSteppingCreator + .createSpeedClusterGroupFrom(stepping); + incrementalRegressionProcessor = new IncrementalRegressionProcessor(speedClusterGroup); List>> grouperResultReceivers = new ArrayList>>(); grouperResultReceivers.add(incrementalRegressionProcessor); @@ -69,13 +74,15 @@ public class PolarDataMiner { Collection> enrichingResultReceivers = Arrays .asList(filteringProcessor); + + enrichingProcessor = new AbstractEnrichingProcessor, GPSFixMovingWithPolarContext>( executor, enrichingResultReceivers) { @Override protected GPSFixMovingWithPolarContext enrich(Triple element) { return new GPSFixMovingWithPolarContext(element.getA(), element.getB(), element.getC(), - defaultPolarSheetGenerationSettings); + speedClusterGroup); } }; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedClusterGroupFromWindSteppingCreator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedClusterGroupFromWindSteppingCreator.java new file mode 100644 index 00000000000..6bd0e192b20 --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedClusterGroupFromWindSteppingCreator.java @@ -0,0 +1,21 @@ +package com.sap.sailing.polars.mining; + +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; +import com.sap.sailing.polars.clusters.SpeedClusterGroup; +import com.sap.sse.datamining.data.ClusterGroup; + +public class SpeedClusterGroupFromWindSteppingCreator { + + public static ClusterGroup createSpeedClusterGroupFrom(WindSteppingWithMaxDistance windStepping) { + double maxDistance = windStepping.getMaxDistance(); + Integer[] rawIntegerStepping = windStepping.getRawStepping(); + double[] rawDoubleLevelMids = new double[rawIntegerStepping.length]; + for (int i = 0; i < rawIntegerStepping.length; i++) { + rawDoubleLevelMids[i] = rawIntegerStepping[i]; + } + + return new SpeedClusterGroup("Speed Cluster Group", rawDoubleLevelMids, maxDistance); + } + +} From 7bd1b832c31a72600a5543cff5930773a20884f3 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 18 Apr 2014 11:23:03 +0200 Subject: [PATCH 35/72] Added polar chart as a datamining result presenter right now it expects the last key in the groupkeys to be the angle for the chart. --- .../client/presentation/PolarChart.java | 81 +++++++++++++++++++ 1 file changed, 81 insertions(+) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java new file mode 100644 index 00000000000..a72dfe04ef7 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java @@ -0,0 +1,81 @@ +package com.sap.sailing.gwt.ui.datamining.client.presentation; + +import java.util.HashMap; +import java.util.Map; +import java.util.Map.Entry; + +import org.moxieapps.gwt.highcharts.client.Chart; +import org.moxieapps.gwt.highcharts.client.Series; +import org.moxieapps.gwt.highcharts.client.plotOptions.LinePlotOptions; + +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.ui.datamining.client.ResultsPresenter; +import com.sap.sse.datamining.shared.GroupKey; +import com.sap.sse.datamining.shared.QueryResult; + +public class PolarChart implements ResultsPresenter { + + private final Chart chart = createPolarChartWidget(); + + private final Map dataByKeyStringAndAngle = new HashMap(); + + @Override + public Widget getWidget() { + return chart; + } + + /** + * Creates a polar diagram chart of the Line type. + */ + private Chart createPolarChartWidget() { + Chart polarSheetChart = new Chart().setType(Series.Type.LINE) + .setLinePlotOptions(new LinePlotOptions().setLineWidth(1)).setZoomType(Chart.ZoomType.X_AND_Y) + .setPolar(true).setHeight100().setWidth100(); + polarSheetChart.getYAxis().setMin(0); + return polarSheetChart; + } + + @Override + public void showResult(QueryResult result) { + resetChartData(); + Map results = result.getResults(); + if (!results.isEmpty()) { + for (Entry resultEntry : results.entrySet()) { + GroupKey key = resultEntry.getKey(); + StringBuilder keyStringBuilder = new StringBuilder(); + GroupKey currentKey = key; + while (currentKey.hasSubKey()) { + keyStringBuilder.append(currentKey.getMainKey()); + currentKey = currentKey.getSubKey(); + } + int angle = Integer.parseInt(currentKey.toString()); + String keyString = keyStringBuilder.toString(); + Series series = dataByKeyStringAndAngle.get(keyString); + if (series == null) { + series = chart.createSeries(); + dataByKeyStringAndAngle.put(keyString, series); + } + series.addPoint(angle, resultEntry.getValue()); + series.setName(keyString); + } + } + } + + private void resetChartData() { + dataByKeyStringAndAngle.clear(); + chart.removeAllSeries(); + } + + @Override + public void showError(String error) { + // TODO Auto-generated method stub + + } + + @Override + public void showError(String mainError, Iterable detailedErrors) { + // TODO Auto-generated method stub + + } + +} From 4eb36fc84cc88abdfc2602a7b203dc4814069060 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 25 Apr 2014 09:30:44 +0200 Subject: [PATCH 36/72] Added comment to polar chart result presenter --- .../gwt/ui/datamining/client/presentation/PolarChart.java | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java index a72dfe04ef7..4731c7bef05 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java @@ -13,6 +13,14 @@ import com.sap.sailing.gwt.ui.datamining.client.ResultsPresenter; import com.sap.sse.datamining.shared.GroupKey; import com.sap.sse.datamining.shared.QueryResult; +/** + * Expects the inner key in the group key to be the angle for the polar chart. (x-value) Maybe the angle should be + * transfered as part of the result value in the future. But in the backend the angle is treated as a key (for polar + * sheets) for grouping all polar fixes with the same rounded angle + * + * @author Frederik Petersen D054528 + * + */ public class PolarChart implements ResultsPresenter { private final Chart chart = createPolarChartWidget(); From 6590f4ec7a3ddd86bc5e25f4ad1ce9e6816c517a Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 13 Jun 2014 15:22:14 +0200 Subject: [PATCH 37/72] Removed deprecated method call --- .../gwt/ui/datamining/client/presentation/PolarChart.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java index 4731c7bef05..5025c6b76bd 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/datamining/client/presentation/PolarChart.java @@ -37,7 +37,7 @@ public class PolarChart implements ResultsPresenter { */ private Chart createPolarChartWidget() { Chart polarSheetChart = new Chart().setType(Series.Type.LINE) - .setLinePlotOptions(new LinePlotOptions().setLineWidth(1)).setZoomType(Chart.ZoomType.X_AND_Y) + .setLinePlotOptions(new LinePlotOptions().setLineWidth(1)) .setPolar(true).setHeight100().setWidth100(); polarSheetChart.getYAxis().setMin(0); return polarSheetChart; From 10d39789381bbe8dcbf2a0c16b244c472dbe5135 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Mon, 13 Oct 2014 12:04:28 +0200 Subject: [PATCH 38/72] define OSGi-bundle-service for accessing polar-diagrams of simulator based on boat-class of race-definition; --- .../gwt/ui/server/SailingServiceImpl.java | 23 ++++--- .../META-INF/MANIFEST.MF | 8 ++- .../simulator/PolarDiagramFactory.java | 10 +++ .../sap/sailing/simulator/impl/Activator.java | 69 +++++++++++++++++++ .../impl/PolarDiagramFactoryImpl.java | 31 +++++++++ 5 files changed, 130 insertions(+), 11 deletions(-) create mode 100644 java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagramFactory.java create mode 100644 java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/Activator.java create mode 100644 java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramFactoryImpl.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index f7ef158c17c..f6464049be7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -392,10 +392,10 @@ import com.sap.sailing.server.replication.ReplicationService; import com.sap.sailing.server.replication.impl.ReplicaDescriptor; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.PolarDiagramFactory; import com.sap.sailing.simulator.SailingSimulator; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.TimedPositionWithSpeed; -import com.sap.sailing.simulator.impl.PolarDiagramCSV; import com.sap.sailing.simulator.impl.SailingSimulatorImpl; import com.sap.sailing.simulator.impl.SimulationParametersImpl; import com.sap.sailing.simulator.util.SailingSimulatorConstants; @@ -440,6 +440,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private final ServiceTracker deviceIdentifierStringSerializationHandlerTracker; + private final ServiceTracker polarDiagramService; + private final com.sap.sailing.domain.tractracadapter.persistence.MongoObjectFactory tractracMongoObjectFactory; private final DomainObjectFactory domainObjectFactory; @@ -488,6 +490,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S raceLogTrackingAdapterTracker = createAndOpenRaceLogTrackingAdapterTracker(context); deviceIdentifierStringSerializationHandlerTracker = createAndOpenDeviceIdentifierStringSerializationHandlerTracker(context); igtimiAdapterTracker = createAndOpenIgtimiTracker(context); + polarDiagramService = createAndOpenPolarDiagramService(context); baseDomainFactory = getService().getBaseDomainFactory(); mongoObjectFactory = getService().getMongoObjectFactory(); domainObjectFactory = getService().getDomainObjectFactory(); @@ -584,6 +587,13 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return result; } + protected ServiceTracker createAndOpenPolarDiagramService(BundleContext context) { + ServiceTracker result = new ServiceTracker( + context, PolarDiagramFactory.class.getName(), null); + result.open(); + return result; + } + private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); } @@ -1518,13 +1528,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S List course = new ArrayList(); course.add(startPosition); course.add(endPosition); - String csvFilePath = "PolarDiagram49STG.csv"; - PolarDiagram polarDiagram = null; - try { - polarDiagram = new PolarDiagramCSV(csvFilePath); - } catch (IOException e) { - // TODO Auto-generated catch block - e.printStackTrace(); + BoatClass boatClass = trackedRace.getRace().getBoatClass(); + PolarDiagram polarDiagram = polarDiagramService.getService().getPolarDiagram(boatClass); + if (polarDiagram == null) { + return null; } SimulationParameters simulationPars = new SimulationParametersImpl(course, polarDiagram, windField, SailingSimulatorConstants.ModeEvent, true, true); diff --git a/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF b/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF index ad311e0e04a..3137d7d7316 100644 --- a/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF @@ -9,9 +9,10 @@ Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", com.sap.sailing.domain;bundle-version="1.0.0", com.sap.sailing.server;bundle-version="1.0.0", com.sap.sailing.domain.tractracadapter, - com.sap.sse.common -Bundle-ActivationPolicy: lazy -Import-Package: org.osgi.framework;version="1.6.0" + com.sap.sse.common, + org.apache.httpcomponents.httpclient;bundle-version="4.2.6", + org.json.simple;bundle-version="1.1.0" +Import-Package: org.osgi.framework;version="1.7.0" Bundle-ClassPath: ., lib/ssj.jar Export-Package: com.sap.sailing.simulator, @@ -19,3 +20,4 @@ Export-Package: com.sap.sailing.simulator, com.sap.sailing.simulator.util, com.sap.sailing.simulator.windfield, com.sap.sailing.simulator.windfield.impl +Bundle-Activator: com.sap.sailing.simulator.impl.Activator diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagramFactory.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagramFactory.java new file mode 100644 index 00000000000..bede436f7bc --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagramFactory.java @@ -0,0 +1,10 @@ +package com.sap.sailing.simulator; + +import com.sap.sailing.domain.base.BoatClass; + + +public interface PolarDiagramFactory { + + PolarDiagram getPolarDiagram(BoatClass boatClass); + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/Activator.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/Activator.java new file mode 100644 index 00000000000..4489b2d3bb9 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/Activator.java @@ -0,0 +1,69 @@ +package com.sap.sailing.simulator.impl; + +import java.io.IOException; +import java.util.concurrent.Callable; +import java.util.concurrent.ExecutionException; +import java.util.concurrent.ExecutorService; +import java.util.concurrent.Executors; +import java.util.concurrent.Future; +import java.util.logging.Level; +import java.util.logging.Logger; + +import org.apache.http.client.ClientProtocolException; +import org.json.simple.parser.ParseException; +import org.osgi.framework.BundleActivator; +import org.osgi.framework.BundleContext; + +import com.sap.sailing.simulator.PolarDiagramFactory; + +/** + * Registers OSGI bundle service for accessing polar diagrams + * + * @author Christopher Ronnewinkel (d036654) + * + */ +public class Activator implements BundleActivator { + private static final Logger logger = Logger.getLogger(Activator.class.getName()); + private static Activator INSTANCE; + + private final Future polarFactory; + private final ExecutorService executor = Executors.newSingleThreadExecutor(); + + public Activator() throws ClientProtocolException, IllegalStateException, IOException, ParseException { + logger.info(getClass().getName()+" constructor"); + polarFactory = executor.submit(new Callable() { + @Override + public PolarDiagramFactoryImpl call() { + logger.info("Creating PolarFactory"); + return new PolarDiagramFactoryImpl(); + } + }); + } + + @Override + public void start(final BundleContext context) throws Exception { + INSTANCE = this; + executor.submit(new Runnable() { + @Override + public void run() { + try { + context.registerService(PolarDiagramFactory.class, polarFactory.get(), /* properties */ null); + } catch (InterruptedException | ExecutionException e) { + logger.log(Level.SEVERE, "Error trying to register Polar services with OSGi", e); + throw new RuntimeException(e); + } + } + }); + } + + public static Activator getInstance() throws ClientProtocolException, IllegalStateException, IOException, ParseException { + if (INSTANCE == null) { + INSTANCE = new Activator(); // probably non-OSGi case, as in test execution + } + return INSTANCE; + } + + @Override + public void stop(BundleContext context) throws Exception { + } +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramFactoryImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramFactoryImpl.java new file mode 100644 index 00000000000..c83478130ae --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramFactoryImpl.java @@ -0,0 +1,31 @@ +package com.sap.sailing.simulator.impl; + +import java.io.IOException; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.BoatClassMasterdata; +import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.PolarDiagramFactory; + +public class PolarDiagramFactoryImpl implements PolarDiagramFactory { + + @Override + public PolarDiagram getPolarDiagram(BoatClass boatClass) { + // TODO generalize to dependency on boat class; this is just the first hard-coded example + String csvFilePath; + if (boatClass.getDisplayName().equals(BoatClassMasterdata._49ER.getDisplayName())) { + csvFilePath = "PolarDiagram49STG.csv"; + } else { + return null; + } + PolarDiagram polarDiagram = null; + try { + polarDiagram = new PolarDiagramCSV(csvFilePath); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return polarDiagram; + } + +} From a133259345b9402b607582a81eee143a396b0d80 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 14 Oct 2014 17:41:51 +0200 Subject: [PATCH 39/72] add check for availability of suitable polar diagram (hasPolar) to race-map-settings; --- .../gwt/ui/actions/GetPolarAction.java | 28 ++++++++++++++++++ .../sailing/gwt/ui/client/SailingService.java | 2 ++ .../gwt/ui/client/SailingServiceAsync.java | 2 ++ .../gwt/ui/client/shared/racemap/RaceMap.java | 29 ++++++++++++++++++- .../gwt/ui/server/SailingServiceImpl.java | 8 +++++ 5 files changed, 68 insertions(+), 1 deletion(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetPolarAction.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetPolarAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetPolarAction.java new file mode 100644 index 00000000000..ea2ced8210a --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/actions/GetPolarAction.java @@ -0,0 +1,28 @@ +package com.sap.sailing.gwt.ui.actions; + +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMap; +import com.sap.sse.gwt.client.async.AsyncAction; + +/** + * An asynchronous action to determine whether a suitable polar diagram is available for the race shown on {@link RaceMap} + * + * @author Christopher Ronnewinkel (D036654) + * + */ +public class GetPolarAction implements AsyncAction { + private final SailingServiceAsync sailingService; + private final RegattaAndRaceIdentifier raceIdentifier; + + public GetPolarAction(SailingServiceAsync sailingService, RegattaAndRaceIdentifier raceIdentifier) { + this.sailingService = sailingService; + this.raceIdentifier = raceIdentifier; + } + + @Override + public void execute(AsyncCallback callback) { + sailingService.getPolarResults(raceIdentifier, callback); + } +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java index c9a4548400e..6fd6867e855 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingService.java @@ -136,6 +136,8 @@ public interface SailingService extends RemoteService { WindInfoForRaceDTO getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth, int numberOfFixes, double latDeg, double lngDeg, Collection windSources) throws NoWindException; + Boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier); + SimulatorResultsDTO getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime); CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map fromPerCompetitorIdAsString, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java index 38aa6765582..b9a8b2e2442 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/SailingServiceAsync.java @@ -182,6 +182,8 @@ public interface SailingServiceAsync { int numberOfFixes, Collection windSourceTypeNames, boolean onlyUpToNewestEvent, boolean includeCombinedWindForAllLegMiddles, AsyncCallback callback); + void getPolarResults(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback callback); + void getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime, AsyncCallback callback); void setWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO wind, AsyncCallback callback); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index d94ec3d9c98..3b8da8b3298 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -81,6 +81,7 @@ import com.sap.sailing.domain.common.impl.BoundsImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.RGBColor; import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing; +import com.sap.sailing.gwt.ui.actions.GetPolarAction; import com.sap.sailing.gwt.ui.actions.GetRaceMapDataAction; import com.sap.sailing.gwt.ui.actions.GetWindInfoAction; import com.sap.sailing.gwt.ui.client.ClientResources; @@ -123,6 +124,7 @@ import com.sap.sse.common.Util.Triple; import com.sap.sse.common.filter.Filter; import com.sap.sse.common.filter.FilterSet; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; +import com.sap.sse.gwt.client.async.MarkedAsyncCallback; import com.sap.sse.gwt.client.player.TimeListener; import com.sap.sse.gwt.client.player.Timer; import com.sap.sse.gwt.client.player.Timer.PlayModes; @@ -309,6 +311,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe private WindStreamletsRaceboardOverlay streamletOverlay; private final boolean showViewStreamlets; private final boolean showViewSimulation; + public static final String GET_POLAR_CATEGORY = "getPolar"; + private boolean hasPolar; private final RegattaAndRaceIdentifier raceIdentifier; public RaceMap(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, @@ -338,6 +342,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe isMapInitialized = false; this.showViewStreamlets = showViewStreamlets; this.showViewSimulation = showViewSimulation; + this.hasPolar = false; headerPanel = new FlowPanel(); headerPanel.setStyleName("RaceMap-HeaderPanel"); panelForLeftHeaderLabels = new AbsolutePanel(); @@ -470,6 +475,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe } if (showViewSimulation) { + // determine availability of polar diagram + hasPolar(); // initialize simulation canvas simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, timer, raceIdentifier, sailingService, stringMessages, asyncActionsExecutor); simulationOverlay.addToMap(); @@ -486,6 +493,26 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe LoadApi.go(onLoad, loadLibraries, sensor, "key="+GoogleMapAPIKey.V3_APIKey); } + + public void hasPolar() { + GetPolarAction getPolar = new GetPolarAction(sailingService, raceIdentifier); + asyncActionsExecutor.execute(getPolar, GET_POLAR_CATEGORY, + new MarkedAsyncCallback<>(new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + // TODO: add corresponding message to string-messages + // Window.setStatus(stringMessages.errorFetchingWindStreamletData(caught.getMessage())); + Window.setStatus(GET_POLAR_CATEGORY); + } + + @Override + public void onSuccess(Boolean result) { + // store results + hasPolar = result.booleanValue(); + } + })); + + } /** * Creates a header panel where additional information can be displayed by using @@ -1918,7 +1945,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe @Override public SettingsDialogComponent getSettingsDialogComponent() { - return new RaceMapSettingsDialogComponent(settings, stringMessages, this.showViewSimulation); + return new RaceMapSettingsDialogComponent(settings, stringMessages, this.showViewSimulation && this.hasPolar); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index f6464049be7..904db517e7e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1469,6 +1469,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return result; } + @Override + public Boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier) { + TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); + BoatClass boatClass = trackedRace.getRace().getBoatClass(); + PolarDiagram polarDiagram = polarDiagramService.getService().getPolarDiagram(boatClass); + return new Boolean(polarDiagram != null); + }; + @Override public SimulatorResultsDTO getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime) { TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); From 7bc64daeade2f6f192849fb0d62017700b330061 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 15 Oct 2014 18:20:34 +0200 Subject: [PATCH 40/72] update race-simulation-overlay dependent on quickranks; --- .../gwt/ui/client/shared/racemap/RaceMap.java | 12 ++++- .../shared/racemap/RaceSimulationOverlay.java | 45 +++++++------------ 2 files changed, 26 insertions(+), 31 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 3b8da8b3298..549919c1654 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -478,8 +478,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe // determine availability of polar diagram hasPolar(); // initialize simulation canvas - simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, timer, raceIdentifier, sailingService, stringMessages, asyncActionsExecutor); + simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, sailingService, stringMessages, asyncActionsExecutor); simulationOverlay.addToMap(); + simulationOverlay.setVisible(false); } createHeaderPanel(map); @@ -737,6 +738,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe public void onSuccess(RaceMapDataDTO raceMapDataDTO) { if (map != null && raceMapDataDTO != null) { quickRanks = raceMapDataDTO.quickRanks; + if (showViewSimulation) { + simulationOverlay.updateLeg(getCurrentLeg(), newTime, false); + } // process response only if not received out of order if (startedProcessingRequestID < requestID) { startedProcessingRequestID = requestID; @@ -2008,7 +2012,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe if (newSettings.isShowSimulationOverlay() != settings.isShowSimulationOverlay()) { settings.setShowSimulationOverlay(newSettings.isShowSimulationOverlay()); simulationOverlay.setVisible(newSettings.isShowSimulationOverlay()); - simulationOverlay.timeChanged(timer.getTime(), null); + simulationOverlay.updateLeg(getCurrentLeg(), timer.getTime(), true); } if (requiredRedraw) { redraw(); @@ -2213,6 +2217,10 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe } return result; } + + public int getCurrentLeg() { + return this.getLeadingVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow()).getA(); + } private Image createSAPLogo() { ImageResource sapLogoResource = resources.sapLogoOverlay(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java index 5882d7d9901..5dce2d3f60b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java @@ -28,8 +28,6 @@ import com.sap.sailing.gwt.ui.simulator.util.ColorPalette; import com.sap.sailing.gwt.ui.simulator.util.ColorPaletteGenerator; import com.sap.sse.gwt.client.async.AsyncActionsExecutor; import com.sap.sse.gwt.client.async.MarkedAsyncCallback; -import com.sap.sse.gwt.client.player.TimeListener; -import com.sap.sse.gwt.client.player.Timer; /** * A Google Maps overlay based on an HTML5 canvas for drawing simulation results on the {@link RaceMap}. @@ -37,7 +35,7 @@ import com.sap.sse.gwt.client.player.Timer; * @author Christopher Ronnewinkel (D036654) * */ -public class RaceSimulationOverlay extends FullCanvasOverlay implements TimeListener { +public class RaceSimulationOverlay extends FullCanvasOverlay { public static final String GET_SIMULATION_CATEGORY = "getSimulation"; private final String textColor = "Black"; @@ -47,21 +45,18 @@ public class RaceSimulationOverlay extends FullCanvasOverlay implements TimeLis private double rectWidth = 20; private double rectHeight = 20; private final StringMessages stringMessages; - private final Timer timer; - private Date lastSimulationTime; - private final long deltaSimulationTime = 15000; private final RegattaAndRaceIdentifier raceIdentifier; private final SailingServiceAsync sailingService; private final AsyncActionsExecutor asyncActionsExecutor; private final ColorPalette colors; private SimulatorResultsDTO simulationResult; private PathDTO racePath; + private int raceLeg = 0; private Date prevStartTime; private Canvas simulationLegend; - public RaceSimulationOverlay(MapWidget map, int zIndex, final Timer timer, RegattaAndRaceIdentifier raceIdentifier, SailingServiceAsync sailingService, StringMessages stringMessages, AsyncActionsExecutor asyncActionsExecutor) { + public RaceSimulationOverlay(MapWidget map, int zIndex, RegattaAndRaceIdentifier raceIdentifier, SailingServiceAsync sailingService, StringMessages stringMessages, AsyncActionsExecutor asyncActionsExecutor) { super(map, zIndex); - this.timer = timer; this.raceIdentifier = raceIdentifier; this.sailingService = sailingService; this.stringMessages = stringMessages; @@ -69,30 +64,22 @@ public class RaceSimulationOverlay extends FullCanvasOverlay implements TimeLis this.colors = new ColorPaletteGenerator(); } - @Override - public void timeChanged(final Date newTime, final Date oldTime) { - if (lastSimulationTime == null) { - lastSimulationTime = new Date(newTime.getTime() - deltaSimulationTime); - } - if (Math.abs(newTime.getTime() - lastSimulationTime.getTime()) >= deltaSimulationTime) { - lastSimulationTime = newTime; - this.simulate(newTime); - } - } - - @Override - public void addToMap() { - if (timer != null) { - timer.addTimeListener(this); - } + public void updateLeg(int newLeg, Date newTime, boolean clearCanvas) { + if ((newLeg != raceLeg)&&(this.isVisible())) { + raceLeg = newLeg; + if (clearCanvas) { + this.clearCanvas(); + } + this.simulate(newTime); + } } @Override - public void removeFromMap() { - if (timer != null) { - timer.removeTimeListener(this); - } - this.setVisible(false); + public void setVisible(boolean isVisible) { + super.setVisible(isVisible); + if (simulationLegend != null) { + simulationLegend.setVisible(isVisible); + } } @Override From 9aa66d686a2c409c817c49c06da88df7cbdc40e0 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Mon, 20 Oct 2014 20:16:14 +0200 Subject: [PATCH 41/72] simplify tree-grow-algorithm by strict focus on left/right steps; --- .../impl/PathGeneratorTreeGrowWind.java | 31 ++----------------- 1 file changed, 2 insertions(+), 29 deletions(-) diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java index 6dee9aa366b..db06e560a74 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java @@ -163,27 +163,9 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase { return new TimedPositionImpl(nextTime, nextPosition); } - // use base direction to distinguish direction changes that do or don't require a turn - char getBaseDirection(char direction) { - char baseDirection = direction; - - if (direction == 'M') { - baseDirection = 'L'; - } - if (direction == 'S') { - baseDirection = 'R'; - } - - return baseDirection; - } - // check whether nextDirection is same base direction as previous direction, i.e. no turn boolean isSameDirection(char prevDirection, char nextDirection) { - - char prevBaseDirection = this.getBaseDirection(prevDirection); - char nextBaseDirection = this.getBaseDirection(nextDirection); - - return ((nextBaseDirection == prevBaseDirection)||(prevBaseDirection == '0')); + return ((nextDirection == prevDirection)||(prevDirection == '0')); } // get path candidate measuring height towards (local, current-apparent) wind @@ -248,9 +230,8 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase { // extend path-string by step-direction String pathStr = path.path + nextDirection; - char nextBaseDirection = this.getBaseDirection(nextDirection); - return (new PathCandidate(pathPos, reachedEnd, vrtDist, hrzDist, turnCount, pathStr, nextBaseDirection, appWind)); + return (new PathCandidate(pathPos, reachedEnd, vrtDist, hrzDist, turnCount, pathStr, nextDirection, appWind)); } @@ -279,18 +260,10 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase { newPathCand = getPathCandWind(path, 'L', timeStep, turnLoss, posStart, posEnd, tgtHeight); result.add(newPathCand); - // step wide left - //newPathCand = getPathCandWind(path, 'M', timeStep, turnLoss, posStart, posEnd, tgtHeight); - //result.add(newPathCand); - // step right newPathCand = getPathCandWind(path, 'R', timeStep, turnLoss, posStart, posEnd, tgtHeight); result.add(newPathCand); - // step wide right - //newPathCand = getPathCandWind(path, 'S', timeStep, turnLoss, posStart, posEnd, tgtHeight); - //result.add(newPathCand); - } return result; From 6d1f2a3eb4201cc250e8b45505766e429b8442ed Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 21 Oct 2014 13:08:17 +0200 Subject: [PATCH 42/72] add tree-grow-variant where path-candidate is represented by BitSets instead of Strings to compare runtime performance; unfortunately no performance improvements; --- .../sailing/simulator/test/TreeGrowTest.java | 4 +- .../sailing/simulator/impl/PathCandidate.java | 8 + .../simulator/impl/PathCandidateBitSet.java | 66 ++ ...owWind.java => PathGeneratorTreeGrow.java} | 4 +- .../impl/PathGeneratorTreeGrowBitSet.java | 713 ++++++++++++++++++ .../simulator/impl/SailingSimulatorImpl.java | 6 +- .../simulator/impl/SimulatorUtils.java | 6 +- 7 files changed, 797 insertions(+), 10 deletions(-) create mode 100644 java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidateBitSet.java rename java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/{PathGeneratorTreeGrowWind.java => PathGeneratorTreeGrow.java} (99%) create mode 100644 java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowBitSet.java diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java index 6d102285d06..561b10f72bd 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/TreeGrowTest.java @@ -20,7 +20,7 @@ import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; import com.sap.sailing.simulator.SimulationParameters; -import com.sap.sailing.simulator.impl.PathGeneratorTreeGrowWind; +import com.sap.sailing.simulator.impl.PathGeneratorTreeGrow; import com.sap.sailing.simulator.impl.PolarDiagram49STG; import com.sap.sailing.simulator.impl.RectangularGrid; import com.sap.sailing.simulator.impl.SimulationParametersImpl; @@ -62,7 +62,7 @@ public class TreeGrowTest { param.setProperty("Djikstra.gridv[int]", 10.0); param.setProperty("Djikstra.gridh[int]", 100.0);*/ - PathGeneratorTreeGrowWind treeGrow = new PathGeneratorTreeGrowWind(param); + PathGeneratorTreeGrow treeGrow = new PathGeneratorTreeGrow(param); Path path = treeGrow.getPath(); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java index 1a0c235d67f..3fd06735af1 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java @@ -23,6 +23,14 @@ public class PathCandidate implements Comparable { String path; char sid; + public int getIndexOfTurnLR() { + return path.indexOf("LR"); + } + + public int getIndexOfTurnRL() { + return path.indexOf("RL"); + } + @Override // sort descending by time, -#turns, width public int compareTo(PathCandidate other) { diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidateBitSet.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidateBitSet.java new file mode 100644 index 00000000000..595d874c491 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidateBitSet.java @@ -0,0 +1,66 @@ +package com.sap.sailing.simulator.impl; + +import java.util.BitSet; + +import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.simulator.TimedPosition; + +public class PathCandidateBitSet implements Comparable { + + public PathCandidateBitSet(TimedPosition pos, boolean reached, double vrt, double hrz, int trn, BitSet path, int length, boolean sid, Wind wind) { + this.pos = pos; // time and position + this.reached = reached; + this.vrt = vrt; // height of target projected onto wind + this.hrz = hrz; // distance from middle line + this.trn = trn; // number of turns + this.path = path; // path as sequence of steps from start to pos + this.length = length; // length of path in steps + this.sid = sid; // side of wind of step reaching pos + } + + TimedPosition pos; + boolean reached; + double vrt; + double hrz; + int trn; + BitSet path; + int length; + boolean sid; + + public int getIndexOfTurnLR() { + for(int step=0; step<(length-1); step++) { + if ((path.get(step) == PathGeneratorTreeGrowBitSet.LEFT)&&(path.get(step+1) == PathGeneratorTreeGrowBitSet.RIGHT)) { + return step; + } + } + return -1; + } + + public int getIndexOfTurnRL() { + for(int step=0; step<(length-1); step++) { + if ((path.get(step) == PathGeneratorTreeGrowBitSet.RIGHT)&&(path.get(step+1) == PathGeneratorTreeGrowBitSet.LEFT)) { + return step; + } + } + return -1; + } + + @Override + // sort descending by time, -#turns, width + public int compareTo(PathCandidateBitSet other) { + if (Math.abs(this.pos.getTimePoint().asMillis() - other.pos.getTimePoint().asMillis()) <= 1000) { + if (this.trn == other.trn) { + if (Math.abs(this.hrz) == Math.abs(other.hrz)) { + return 0; + } else { + return (Math.abs(this.hrz) < Math.abs(other.hrz) ? -1 : +1); + } + } else { + return (this.trn < other.trn ? -1 : +1); + } + } else { + return (this.pos.getTimePoint().asMillis() < other.pos.getTimePoint().asMillis() ? -1 : +1); + } + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java similarity index 99% rename from java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java rename to java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java index db06e560a74..db9a085051b 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowWind.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java @@ -26,7 +26,7 @@ import com.sap.sailing.simulator.TimedPositionWithSpeed; import com.sap.sailing.simulator.windfield.WindFieldGenerator; import com.sap.sse.common.Util; -public class PathGeneratorTreeGrowWind extends PathGeneratorBase { +public class PathGeneratorTreeGrow extends PathGeneratorBase { private static Logger logger = Logger.getLogger("com.sap.sailing"); private boolean debugMsgOn = false; @@ -42,7 +42,7 @@ public class PathGeneratorTreeGrowWind extends PathGeneratorBase { ArrayList> isocPositions = null; String gridFile = null; - public PathGeneratorTreeGrowWind(SimulationParameters params) { + public PathGeneratorTreeGrow(SimulationParameters params) { this.parameters = params; } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowBitSet.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowBitSet.java new file mode 100644 index 00000000000..5f432e97549 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrowBitSet.java @@ -0,0 +1,713 @@ +package com.sap.sailing.simulator.impl; + +import java.io.BufferedWriter; +import java.io.FileWriter; +import java.io.IOException; +import java.util.ArrayList; +import java.util.BitSet; +import java.util.Collections; +import java.util.Comparator; +import java.util.List; +import java.util.logging.Logger; + +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; +import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.domain.tracking.impl.WindImpl; +import com.sap.sailing.simulator.Path; +import com.sap.sailing.simulator.PolarDiagram; +import com.sap.sailing.simulator.SimulationParameters; +import com.sap.sailing.simulator.TimedPosition; +import com.sap.sailing.simulator.TimedPositionWithSpeed; +import com.sap.sailing.simulator.windfield.WindFieldGenerator; +import com.sap.sse.common.Util; + +public class PathGeneratorTreeGrowBitSet extends PathGeneratorBase { + + private static Logger logger = Logger.getLogger("com.sap.sailing"); + private boolean debugMsgOn = false; + + double oobFact = 2.0; // out-of-bounds factor + int maxTurns = 0; + boolean upwindLeg = false; + String initPathStr = "0"; + PathCandidateBitSet bestCand = null; + long usedTimeStep = 0; + boolean gridStore = false; + ArrayList> gridPositions = null; + ArrayList> isocPositions = null; + String gridFile = null; + final static boolean LEFT = false; + final static boolean RIGHT = true; + + public PathGeneratorTreeGrowBitSet(SimulationParameters params) { + this.parameters = params; + } + + public void setEvaluationParameters(String startDirection, int maxTurns, String gridFile) { + if (startDirection != null) { + this.initPathStr = "0" + startDirection; + } else { + this.initPathStr = "0"; + } + this.maxTurns = maxTurns; + this.gridFile = gridFile; + if (this.gridFile != null) { + this.gridStore = true; + this.gridPositions = new ArrayList>(); + this.isocPositions = new ArrayList>(); + } else { + this.gridStore = false; + this.gridPositions = null; + this.isocPositions = null; + } + } + + class SortPathCandsAbsHorizontally implements Comparator { + + @Override + public int compare(PathCandidateBitSet p1, PathCandidateBitSet p2) { + if (Math.abs(p1.hrz) == Math.abs(p2.hrz)) { + return 0; + } else { + return (Math.abs(p1.hrz) < Math.abs(p2.hrz) ? -1 : +1); + } + } + + } + + class SortPathCandsHorizontally implements Comparator { + + @Override + public int compare(PathCandidateBitSet p1, PathCandidateBitSet p2) { + if (p1.hrz == p2.hrz) { + return 0; + } else { + return (p1.hrz < p2.hrz ? -1 : +1); + } + } + + } + + // getter for evaluating best path cand propoerties further + PathCandidateBitSet getBestCand() { + return this.bestCand; + } + + long getUsedTimeStep() { + return this.usedTimeStep; + } + + + // generate step in one of the possible directions + // default: L - left, R - right + TimedPosition getStep(TimedPosition pos, long timeStep, long turnLoss, boolean sameBaseDirection, boolean nextDirection) { + + WindFieldGenerator wf = this.parameters.getWindField(); + TimePoint curTime = pos.getTimePoint(); + Position curPosition = pos.getPosition(); + Wind posWind = wf.getWind(new TimedPositionImpl(curTime, curPosition)); + + PolarDiagram pd = this.parameters.getBoatPolarDiagram(); + pd.setWind(posWind); + + // get beat-angle left and right + Bearing travelBearing = null; + if (nextDirection == LEFT) { + if (this.upwindLeg) { + travelBearing = pd.optimalDirectionsUpwind()[0]; + } else { + travelBearing = pd.optimalDirectionsDownwind()[0]; + } + } else if (nextDirection == RIGHT) { + if (this.upwindLeg) { + travelBearing = pd.optimalDirectionsUpwind()[1]; + } else { + travelBearing = pd.optimalDirectionsDownwind()[1]; + } + } + + // determine beat-speed left and right + SpeedWithBearing travelSpeed = pd.getSpeedAtBearing(travelBearing); + + TimePoint travelTime; + TimePoint nextTime = new MillisecondsTimePoint(curTime.asMillis()+timeStep); + if (sameBaseDirection) { + travelTime = nextTime; + } else { + travelTime = new MillisecondsTimePoint(nextTime.asMillis() - turnLoss); + } + + Position nextPosition = travelSpeed.travelTo(curPosition, curTime, travelTime); + return new TimedPositionImpl(nextTime, nextPosition); + } + + // check whether nextDirection is same base direction as previous direction, i.e. no turn + boolean isSameDirection(int length, boolean prevDirection, boolean nextDirection) { + return ((nextDirection == prevDirection)||(length <= 1)); + } + + // get path candidate measuring height towards (local, current-apparent) wind + PathCandidateBitSet getPathCandWind(PathCandidateBitSet path, boolean nextDirection, long timeStep, long turnLoss, Position posStart, Position posEnd, double tgtHeight) { + + boolean prevDirection = path.path.get(path.length-1); + boolean sameBaseDirection = this.isSameDirection(path.length, prevDirection, nextDirection); + + int turnCount = path.trn; + if (!sameBaseDirection) { + turnCount++; + } + + // calculate next path position (taking turn-loss into account) + TimedPosition pathPos = this.getStep(path.pos, timeStep, turnLoss, sameBaseDirection, nextDirection); + + // determine apparent wind at next path position & time + Wind posWind = this.parameters.getWindField().getWind(pathPos); + PolarDiagram pd = this.parameters.getBoatPolarDiagram(); + pd.setWind(posWind); + Wind appWind = new WindImpl(posWind.getPosition(), posWind.getTimePoint(), pd.getWind()); + + // calculate height-position with reference to apparent wind + Position posHeight = pathPos.getPosition().projectToLineThrough(posEnd, appWind.getBearing()); + + // calculate vertical distance as distance of height-position to end + Bearing bearHeight = posEnd.getBearingGreatCircle(posHeight); + double bearHeightSide = appWind.getBearing().getDifferenceTo(bearHeight).getDegrees(); + double vrtSide = (this.upwindLeg ? -1.0 : +1.0); + if (Math.abs(bearHeightSide) > 170.0) { + vrtSide = (this.upwindLeg ? +1.0 : -1.0); + } + double vrtDist = vrtSide*Math.round(posHeight.getDistance(posEnd).getMeters()*1000.0)/1000.0; + + // scale last step to exactly reach height of posEnd (in reference to appWind) and adjust time correspondingly + boolean reachedEnd = false; + if ((!path.reached) && (vrtDist > 0.0)) { + // scale last step so that vrtDist ~ tgtHeight + Position prevPos = path.pos.getPosition(); + TimePoint prevTime = path.pos.getTimePoint(); + double heightFrac = path.vrt / (path.vrt - vrtDist); + Position newPos = prevPos.translateGreatCircle(prevPos.getBearingGreatCircle(pathPos.getPosition()), prevPos.getDistance(pathPos.getPosition()).scale(heightFrac)); + long newTimeMillis = Math.round((prevTime.asMillis() + (pathPos.getTimePoint().asMillis() - prevTime.asMillis()) * heightFrac)/1000.0)*1000; + TimePoint newTime = new MillisecondsTimePoint(newTimeMillis); + pathPos = new TimedPositionImpl(newTime, newPos); + reachedEnd = true; + } + + // calculate horizontal side: left or right in reference to race course + double posSide = 1; + Bearing posBear = posStart.getBearingGreatCircle(pathPos.getPosition()); + Bearing bearVrt = posStart.getBearingGreatCircle(posEnd); + double posBearDiff = bearVrt.getDifferenceTo(posBear).getDegrees(); + if ((posBearDiff < 0.0)||(posBearDiff > 180.0)) { + posSide = -1; + } else if ((posBearDiff == 0.0)||(posBearDiff == 180.0)) { + posSide = 0; + } + // calculate horizontal distance as distance of height-position to current position + Position posHeightTrgt = pathPos.getPosition().projectToLineThrough(posStart, bearVrt); + double hrzDist = Math.round(posSide*posHeightTrgt.getDistance(pathPos.getPosition()).getMeters()*1000.0)/1000.0; + + // extend path-string by step-direction + BitSet newPath = new BitSet(path.length+1); + newPath.or(path.path); + newPath.set(path.length, nextDirection); + + return (new PathCandidateBitSet(pathPos, reachedEnd, vrtDist, hrzDist, turnCount, newPath, path.length+1, nextDirection, appWind)); + } + + + // generate path candidates based on beat angles + List getPathCandsBeatWind(PathCandidateBitSet path, long timeStep, long turnLoss, Position posStart, Position posEnd, double tgtHeight) { + + List result = new ArrayList(); + PathCandidateBitSet newPathCand; + + if (this.maxTurns > 0) { + + boolean prevDirection = path.path.get(path.length-1); + + if ((path.trn < this.maxTurns)||(this.isSameDirection(path.length, prevDirection, LEFT))) { + newPathCand = getPathCandWind(path, LEFT, timeStep, turnLoss, posStart, posEnd, tgtHeight); + result.add(newPathCand); + } + + if ((path.trn < this.maxTurns)||(this.isSameDirection(path.length, prevDirection, RIGHT))) { + newPathCand = getPathCandWind(path, RIGHT, timeStep, turnLoss, posStart, posEnd, tgtHeight); + result.add(newPathCand); + } + + } else { + + // step left + newPathCand = getPathCandWind(path, LEFT, timeStep, turnLoss, posStart, posEnd, tgtHeight); + result.add(newPathCand); + + // step right + newPathCand = getPathCandWind(path, RIGHT, timeStep, turnLoss, posStart, posEnd, tgtHeight); + result.add(newPathCand); + + } + + return result; + } + + Util.Pair,List> generateCandidate(List oldPaths, long timeStep, long turnLoss, Position posStart, Position posMiddle, Position posEnd, double tgtHeight) { + + List newPathCands; + List leftPaths = new ArrayList(); + List rightPaths = new ArrayList(); + for(PathCandidateBitSet curPath : oldPaths) { + + if (curPath.reached) { + continue; + } + + newPathCands = this.getPathCandsBeatWind(curPath, timeStep, turnLoss, posStart, posEnd, tgtHeight); + for (PathCandidateBitSet curNewPath : newPathCands) { + // check whether path is *outside* regatta-area + double distFromMiddleMeters = posMiddle.getDistance(curPath.pos.getPosition()).getMeters(); + if (distFromMiddleMeters > oobFact * tgtHeight) { + continue; // ignore curPath + } + + if (curNewPath.sid == LEFT) { + leftPaths.add(curNewPath); + } else if (curNewPath.sid == RIGHT) { + rightPaths.add(curNewPath); + } + + } + + } + + Util.Pair,List> newPaths = new Util.Pair,List>(leftPaths, rightPaths); + return newPaths; + } + + + List filterCandidates(List allCands, double hrzBinWidth) { + + boolean[] filterMap = new boolean[allCands.size()]; + + // sort candidates by horizontal distance + Comparator sortHorizontal = new SortPathCandsHorizontally(); + Collections.sort(allCands, sortHorizontal); + + // start scan with index 0 + int idxL = 0; + int idxR = 0; + + // for each candidate, check the neighborhoods and identify bad candidates + for(int idx = 0; idx < allCands.size(); idx++) { + + // current horizontal distance + double hrzDist = allCands.get(idx).hrz; + + // align left index + while(Math.abs(hrzDist - allCands.get(idxL).hrz) > hrzBinWidth) { + idxL++; + } + + // align right index + boolean finished = false; + while(!finished && (idxR < (allCands.size()-1))) { + if (Math.abs(hrzDist - allCands.get(idxR+1).hrz) <= hrzBinWidth) { + idxR++; + } else { + finished = true; + } + } + + // search maximum height + // in neighborhood idxL, ..., idxR + + // init max for search + int vrtIdx = idxL; + double vrtMax = allCands.get(vrtIdx).vrt; + filterMap[vrtIdx] = false; + + // evaluate remainder of neighborhood + if (idxL < idxR) { + for(int jdx = (idxL+1); jdx <= idxR; jdx++) { + if (allCands.get(jdx).vrt > vrtMax) { + // reset previous max candidate + filterMap[vrtIdx] = true; + // keep max height + vrtMax = allCands.get(jdx).vrt; + // keep max index + vrtIdx = jdx; + // set current max candidate + filterMap[vrtIdx] = false; + } else { + filterMap[jdx] = true; + } + } + } + + } // endfor each candidate + + // collect all good candidates (i.e. filterMap == false) + List filterCands = new ArrayList(); + for(int idx=0; idx < allCands.size(); idx++) { + if (!filterMap[idx]) { + filterCands.add(allCands.get(idx)); + } + } + + // return remaining good candidates + return filterCands; + } + + + List filterIsochrone(List allCands, double hrzBinWidth) { + + boolean[] filterMap = new boolean[allCands.size()]; + for(int idx = 0; idx < allCands.size(); idx++) { + filterMap[idx] = true; + } + + // sort candidates by horizontal distance + Comparator sortHorizontal = new SortPathCandsHorizontally(); + Collections.sort(allCands, sortHorizontal); + + // start scan with index 0 + int idxL = 0; + int idxR = 0; + + // for each candidate, check the neighborhoods and identify bad candidates + for(int idx = 0; idx < allCands.size(); idx++) { + + // current horizontal distance + double hrzDist = allCands.get(idx).hrz; + + // align left index + while(Math.abs(hrzDist - allCands.get(idxL).hrz) > hrzBinWidth) { + idxL++; + } + + // align right index + boolean finished = false; + while(!finished && (idxR < (allCands.size()-1))) { + if (Math.abs(hrzDist - allCands.get(idxR+1).hrz) <= hrzBinWidth) { + idxR++; + } else { + finished = true; + } + } + + // search maximum height + // in neighborhood idxL, ..., idxR + + // init max for search + ArrayList vrtIdx = new ArrayList(); + vrtIdx.add(idxL); + double vrtMax = allCands.get(idxL).vrt; + + // evaluate remainder of neighborhood + if (idxL < idxR) { + for(int jdx = (idxL+1); jdx <= idxR; jdx++) { + if (allCands.get(jdx).vrt > vrtMax) { + // keep max height + vrtMax = allCands.get(jdx).vrt; + // keep max index + vrtIdx = new ArrayList(); + vrtIdx.add(jdx); + } else if (allCands.get(jdx).vrt == vrtMax) { + // add further max indexes + vrtIdx.add(jdx); + } + } + } + + for(Integer jdx : vrtIdx) { + filterMap[jdx] = false; + } + + } // endfor each candidate + + // collect all good candidates (i.e. filterMap == false) + List filterCands = new ArrayList(); + for(int idx=0; idx < allCands.size(); idx++) { + if (!filterMap[idx]) { + filterCands.add(allCands.get(idx)); + } + } + + // return remaining good candidates + return filterCands; + } + + @Override + public Path getPath() { + WindFieldGenerator wf = this.parameters.getWindField(); + PolarDiagram pd = this.parameters.getBoatPolarDiagram(); + + Position startPos = this.parameters.getCourse().get(0); + Position endPos = this.parameters.getCourse().get(1); + + // test downwind: exchange start and end + //Position startPos = this.parameters.getCourse().get(1); + //Position endPos = this.parameters.getCourse().get(0); + + TimePoint startTime = wf.getStartTime();// new MillisecondsTimePoint(0); + List path = new ArrayList(); + + Position currentPosition = startPos; + TimePoint currentTime = startTime; + + Distance distStartEnd = startPos.getDistance(endPos); + double distStartEndMeters = distStartEnd.getMeters(); + + Wind wndStart = wf.getWind(new TimedPositionWithSpeedImpl(startTime, startPos, null)); + logger.fine("wndStart speed:" + wndStart.getKnots() + " angle:" + wndStart.getBearing().getDegrees()); + pd.setWind(wndStart); + Bearing bearVrt = startPos.getBearingGreatCircle(endPos); + //Bearing bearHrz = bearVrt.add(new DegreeBearingImpl(90.0)); + Position middlePos = startPos.translateGreatCircle(bearVrt, distStartEnd.scale(0.5)); + + Bearing bearRCWind = wndStart.getBearing().getDifferenceTo(bearVrt); + String legType = "downwind"; + this.upwindLeg = false; + + if ((Math.abs(bearRCWind.getDegrees()) > 90.0)&&(Math.abs(bearRCWind.getDegrees()) < 270.0)) { + legType = "upwind"; + this.upwindLeg = true; + } + + if (debugMsgOn) { + System.out.println("start : "+startPos.getLatDeg()+", "+startPos.getLngDeg()); + System.out.println("middle: "+middlePos.getLatDeg()+", "+middlePos.getLngDeg()); + System.out.println("end : "+endPos.getLatDeg()+", "+endPos.getLngDeg()); + } + logger.info("Leg Direction: "+legType); + + long turnLoss = pd.getTurnLoss(); // time lost when doing a turn + if (!this.upwindLeg) { + turnLoss = turnLoss / 2; + } + logger.info("Turnloss :" + turnLoss); + + this.usedTimeStep = turnLoss + 1000; // time-step larger than turn-loss is required (this may be removed by extended handling of turn-loss) + logger.info("Time step :" + usedTimeStep); + + // calculate initial position according to initPathStr + PathCandidateBitSet initPath = new PathCandidateBitSet(new TimedPositionImpl(currentTime, currentPosition), false, 0.0, 0.0, 0, new BitSet(1), 1, LEFT, wndStart); + if (initPathStr.length()>1) { + char nextDirectionChar = '0'; + for(int idx=1; idx allPaths = new ArrayList(); + List trgPaths = new ArrayList(); + allPaths.add(initPath); + + + TimedPosition tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), usedTimeStep, turnLoss, true, LEFT); + double tstDist1 = startPos.getDistance(tstPosition.getPosition()).getMeters(); + tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), usedTimeStep, turnLoss, true, RIGHT); + double tstDist2 = startPos.getDistance(tstPosition.getPosition()).getMeters(); + + double hrzBinSize = (tstDist1 + tstDist2)/6.0; // horizontal bin size in meters + if (debugMsgOn) { + System.out.println("Horizontal Bin Size: "+hrzBinSize); + } + + boolean reachedEnd = false; + int addSteps = 0; + int finalSteps = 0; // maximum number of additional steps after first target-path found + + while ((!reachedEnd)||(addSteps,List> newPaths = this.generateCandidate(allPaths, usedTimeStep, turnLoss, startPos, middlePos, endPos, distStartEndMeters); + + + // select good candidates + List leftPaths = this.filterCandidates(newPaths.getA(), hrzBinSize/2.0); + List rightPaths = this.filterCandidates(newPaths.getB(), hrzBinSize/2.0); + + List nextPaths = new ArrayList (); + nextPaths.addAll(leftPaths); + nextPaths.addAll(rightPaths); + + allPaths = nextPaths; + + if (this.gridStore) { + + /*ArrayList isoChrone = new ArrayList(); + for(PathCand curCand : allPaths) { + isoChrone.add(curCand.pos); + } + this.gridPositions.add(isoChrone);*/ + + this.gridPositions.add(allPaths); + + List isocPaths = this.filterIsochrone(allPaths, hrzBinSize); + this.isocPositions.add(isocPaths); + + } + + // check if there are still paths in the regatta-area + if (allPaths.size() > 0) { + + for(PathCandidateBitSet curPath : allPaths) { + // terminate path-search if paths are found that are close enough to target + //if ((curPath.vrt > distStartEndMeters)) { + if (curPath.reached) { + //logger.info("\ntPath: " + curPath.path + "\n Time: " + (Math.round((curPath.pos.getTimePoint().asMillis()-startTime.asMillis())/1000.0/60.0*10.0)/10.0)+", Height: "+curPath.vrt+" of "+(Math.round(startPos.getDistance(endPos).getMeters()*100.0)/100.0)+", Dist: "+curPath.hrz+"m ~ "+(Math.round(curPath.pos.getPosition().getDistance(endPos).getMeters()*100.0)/100.0)+"m"); + int curBin = (int)Math.round(Math.floor( (curPath.hrz + hrzBinSize/2.0) / hrzBinSize )); + if ((Math.abs(curBin) <= 4)) { + reachedEnd = true; + trgPaths.add(curPath); // add path to list of target-paths + } + } + } + + } else { + // terminate path-search as no path inside regatta-area are left + reachedEnd = true; + } + + } + + if (this.gridStore) { + + double distResolution = distStartEndMeters*0.01; + BufferedWriter outputCSV; + try { + outputCSV = new BufferedWriter(new FileWriter(this.gridFile+"-grid.csv")); + outputCSV.write("step; lat; lng; time; side; path; vrt\n"); + outputCSV.write("0; "+startPos.getLatDeg()+"; "+startPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; "+(-distStartEndMeters)+"\n"); + outputCSV.write("0; "+endPos.getLatDeg()+"; "+endPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; 0\n"); + int stepCount = 0; + for(List isoChrone : this.gridPositions) { + stepCount++; + PathCandidateBitSet prevPos = null; + for(PathCandidateBitSet isoPos : isoChrone) { + + if (prevPos != null) { + if (prevPos.pos.getPosition().getDistance(isoPos.pos.getPosition()).getMeters() < distResolution) { + continue; + } + } + + String outStr = ""+stepCount+"; "+isoPos.pos.getPosition().getLatDeg()+"; "+isoPos.pos.getPosition().getLngDeg()+"; "+(isoPos.pos.getTimePoint().asMillis()/1000)+"; "+isoPos.sid; + outStr += "; "+isoPos.path+"; "+isoPos.vrt; + outStr += "\n"; + outputCSV.write(outStr); + + prevPos = isoPos; + } + } + outputCSV.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + try { + outputCSV = new BufferedWriter(new FileWriter(this.gridFile+"-isoc.csv")); + outputCSV.write("step; lat; lng; time; side; path; vrt\n"); + outputCSV.write("0; "+startPos.getLatDeg()+"; "+startPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; "+(-distStartEndMeters)+"\n"); + outputCSV.write("0; "+endPos.getLatDeg()+"; "+endPos.getLngDeg()+"; "+(startTime.asMillis()/1000)+"; 0; 0; 0\n"); + int stepCount = 0; + for(List isoChrone : this.isocPositions) { + stepCount++; + PathCandidateBitSet prevPos = null; + for(PathCandidateBitSet isoPos : isoChrone) { + + if (prevPos != null) { + if (prevPos.pos.getPosition().getDistance(isoPos.pos.getPosition()).getMeters() < distResolution) { + continue; + } + } + + String outStr = ""+stepCount+"; "+isoPos.pos.getPosition().getLatDeg()+"; "+isoPos.pos.getPosition().getLngDeg()+"; "+(isoPos.pos.getTimePoint().asMillis()/1000)+"; "+isoPos.sid; + outStr += "; "+isoPos.path+"; "+isoPos.vrt; + outStr += "\n"; + outputCSV.write(outStr); + + prevPos = isoPos; + } + } + outputCSV.close(); + } catch (IOException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + + } + + + // if no target-paths were found, return empty path + if (trgPaths.size() == 0) { + //trgPaths = allPaths; // TODO: only for testing; remove lateron + TimedPositionWithSpeed curPosition = new TimedPositionWithSpeedImpl(startTime, startPos, null); + path.add(curPosition); + return new PathImpl(path, wf); // return empty path + } + + // sort target-paths ascending by distance-to-target + Collections.sort(trgPaths); + + // debug output + for(PathCandidateBitSet curPath : trgPaths) { + logger.info("\nPath: " + curPath.path + "\n Time: " + (curPath.pos.getTimePoint().asMillis()-startTime.asMillis()) +", Height: "+curPath.vrt+" of "+(Math.round(startPos.getDistance(endPos).getMeters()*100.0)/100.0)+", Dist: "+curPath.hrz+"m ~ "+(Math.round(curPath.pos.getPosition().getDistance(endPos).getMeters()*100.0)/100.0)+"m"); + //System.out.print(""+curPath.path+": "+curPath.pos.getTimePoint().asMillis()+", "+curPath.pos.getPosition().getLatDeg()+", "+curPath.pos.getPosition().getLngDeg()+", "); + //System.out.println(" height:"+curPath.vrt+" of "+startPos.getDistance(endPos).getMeters()+", dist:"+curPath.hrz+" ~ "+curPath.pos.getPosition().getDistance(endPos)); + } + + // + // fill gwt-path + // + + // generate intermediate steps + bestCand = trgPaths.get(0); // target-path ending closest to target + TimedPositionWithSpeed curPosition = null; + boolean nextDirection = LEFT; + boolean prevDirection = LEFT; + for(int step=0; step<(bestCand.length-1); step++) { + + nextDirection = bestCand.path.get(step); + + if (step == 0) { + + curPosition = new TimedPositionWithSpeedImpl(startTime, startPos, null); + path.add(curPosition); + + } else { + + boolean sameBaseDirection = this.isSameDirection(step, prevDirection, nextDirection); + TimedPosition newPosition = this.getStep(curPosition, usedTimeStep, turnLoss, sameBaseDirection, nextDirection); + curPosition = new TimedPositionWithSpeedImpl(newPosition.getTimePoint(), newPosition.getPosition(), null); + path.add(curPosition); + + } + + prevDirection = nextDirection; + } + + // add final position (rescaled before to end on height of target) + path.add(new TimedPositionWithSpeedImpl(bestCand.pos.getTimePoint(), bestCand.pos.getPosition(), null)); + + return new PathImpl(path, wf); + + } + +} diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java index e5b97963a33..9e6ef55024a 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SailingSimulatorImpl.java @@ -113,7 +113,7 @@ public class SailingSimulatorImpl implements SailingSimulator { // // get instance of heuristic searcher - PathGeneratorTreeGrowWind genTreeGrow = new PathGeneratorTreeGrowWind(this.simulationParameters); + PathGeneratorTreeGrow genTreeGrow = new PathGeneratorTreeGrow(this.simulationParameters); // search best left-starting 1-turner genTreeGrow.setEvaluationParameters("L", 1, null); @@ -123,7 +123,7 @@ public class SailingSimulatorImpl implements SailingSimulator { long left1TurnMidtime = 1000000000; long left1TurnTimestep = genTreeGrow.getUsedTimeStep(); if (leftBestCand != null) { - left1TurnMiddle = leftBestCand.path.indexOf("LR"); + left1TurnMiddle = leftBestCand.getIndexOfTurnLR(); left1TurnMidtime = left1TurnMiddle * left1TurnTimestep; } @@ -135,7 +135,7 @@ public class SailingSimulatorImpl implements SailingSimulator { long right1TurnMidtime = 1000000000; long right1TurnTimestep = genTreeGrow.getUsedTimeStep(); if (rightBestCand != null) { - right1TurnMiddle = rightBestCand.path.indexOf("RL"); + right1TurnMiddle = rightBestCand.getIndexOfTurnRL(); right1TurnMidtime = right1TurnMiddle * right1TurnTimestep; } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorUtils.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorUtils.java index b2520ce72b0..056d8f5b50a 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorUtils.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/SimulatorUtils.java @@ -374,7 +374,7 @@ public class SimulatorUtils { Map paths = new HashMap(); // get instance of heuristic searcher - PathGeneratorTreeGrowWind genTreeGrow = new PathGeneratorTreeGrowWind(parameters); + PathGeneratorTreeGrow genTreeGrow = new PathGeneratorTreeGrow(parameters); // search best left-starting 1-turner genTreeGrow.setEvaluationParameters("L", 1, null); @@ -383,7 +383,7 @@ public class SimulatorUtils { PathCandidate leftBestCand = genTreeGrow.getBestCand(); int left1TurnMiddle = 1000; if (leftBestCand != null) { - left1TurnMiddle = leftBestCand.path.indexOf("LR"); + left1TurnMiddle = leftBestCand.getIndexOfTurnLR(); } // search best right-starting 1-turner @@ -393,7 +393,7 @@ public class SimulatorUtils { PathCandidate rightBestCand = genTreeGrow.getBestCand(); int right1TurnMiddle = 1000; if (rightBestCand != null) { - right1TurnMiddle = rightBestCand.path.indexOf("RL"); + right1TurnMiddle = rightBestCand.getIndexOfTurnRL(); } // search best multi-turn course From 4564a44eed5ca7b1e9791e64ba8e1cce5b927fa7 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 22 Oct 2014 11:33:00 +0200 Subject: [PATCH 43/72] Started adapting polar data mining to supply sheets to existing UI --- .../polars/impl/PolarDataServiceImpl.java | 6 +- .../IncrementalRegressionProcessor.java | 9 +++ .../sailing/polars/mining/PolarDataMiner.java | 62 +++++++++++++++++++ 3 files changed, 75 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 2fefbbd8c49..0a8c4121131 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -109,12 +109,14 @@ public class PolarDataServiceImpl implements PolarDataService { @Override public PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass) { - return polarSheetPerBoatClassCache.get(boatClass, false); + return polarDataMiner.createFullSheetForBoatClass(boatClass); + //return polarSheetPerBoatClassCache.get(boatClass, false); } @Override public Set getAllBoatClassesWithPolarSheetsAvailable() { - return polarSheetPerBoatClassCache.keySet(); + return polarDataMiner.getAvailableBoatClasses(); + //return polarSheetPerBoatClassCache.keySet(); } @Override diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index 374e8f38134..95a68118a37 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -1,7 +1,9 @@ package com.sap.sailing.polars.mining; import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.SpeedWithConfidence; @@ -24,6 +26,8 @@ public class IncrementalRegressionProcessor implements Processor boatSpeedEstimators = new HashMap(); private final ClusterGroup speedClusterGroup; + + private final Set availableBoatClasses = new HashSet<>(); public IncrementalRegressionProcessor(ClusterGroup speedClusterGroup) { this.speedClusterGroup = speedClusterGroup; @@ -36,6 +40,7 @@ public class IncrementalRegressionProcessor implements Processor getAvailableBoatClasses() { + return availableBoatClasses; + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index 07334c685f6..68d36519ccb 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -3,7 +3,10 @@ package com.sap.sailing.polars.mining; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; +import java.util.HashMap; import java.util.List; +import java.util.Map; +import java.util.Set; import java.util.concurrent.LinkedBlockingQueue; import java.util.concurrent.ThreadPoolExecutor; import java.util.concurrent.TimeUnit; @@ -11,10 +14,17 @@ import java.util.concurrent.TimeUnit; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.domain.common.PolarSheetsHistogramData; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; +import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; +import com.sap.sailing.domain.common.impl.PolarSheetsHistogramDataImpl; import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; @@ -104,4 +114,56 @@ public class PolarDataMiner { return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind); } + + + public PolarSheetsData createFullSheetForBoatClass(BoatClass boatClass) { + Integer[] defaultWindSpeeds = defaultPolarSheetGenerationSettings.getWindStepping().getRawStepping(); + Number[][] averagedPolarDataByWindSpeed = new Number[defaultWindSpeeds.length][360]; + for (int windIndex = 0; windIndex < defaultWindSpeeds.length - 1; windIndex++) { + Integer windSpeed = defaultWindSpeeds[windIndex]; + for (int angle = 0; angle < 360; angle++) { + SpeedWithConfidence speedWithConfidence; + try { + speedWithConfidence = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(angle)); + } catch (NotEnoughDataHasBeenAddedException e) { + speedWithConfidence = new SpeedWithConfidenceImpl(new KnotSpeedImpl(0), 0, 0); + } + averagedPolarDataByWindSpeed[windIndex][angle] = speedWithConfidence.getScalableValue().getValue(); + } + } + + // FIXME Remove hard coded size values + Map dataCountPerAngleForWindspeed = new HashMap<>(); + Map> histogramDataMap = new HashMap<>(); + for (int windIndex = 0; windIndex < defaultWindSpeeds.length - 1; windIndex++) { + Integer[] perAngle = new Integer[360]; + Map perWindSpeed = new HashMap<>(); + for (int angle = 0; angle < 360; angle++) { + perAngle[angle] = /* FIXME */100; + Number[] xValues = {5}; + Number[] yValues = {10};; + Map yValuesByGaugeIds = new HashMap<>(); + Map yValuesByDay = new HashMap<>(); + Map yValuesByDayAndGaugeId = new HashMap<>(); + int dataCount = 100; + double coefficiantOfVariation = 0.8; + perWindSpeed.put(angle, new PolarSheetsHistogramDataImpl(angle, xValues, yValues, yValuesByGaugeIds, + yValuesByDay, yValuesByDayAndGaugeId, dataCount, coefficiantOfVariation)); + } + histogramDataMap.put(windIndex, perWindSpeed); + dataCountPerAngleForWindspeed.put(windIndex, perAngle); + } + + + PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, /* FIXME */100000, + dataCountPerAngleForWindspeed, defaultPolarSheetGenerationSettings.getWindStepping(), histogramDataMap); + return data; + } + + + + public Set getAvailableBoatClasses() { + return incrementalRegressionProcessor.getAvailableBoatClasses(); + } + } From afe364a78db1f1a11ce50a70ae4e1eea0228e704 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 22 Oct 2014 11:55:53 +0200 Subject: [PATCH 44/72] Fixed server launch configs --- java/com.sap.sailing.gwt.ui/SailingGWT.launch | 2 +- java/com.sap.sailing.server/SailingServer (No Proxy).launch | 1 + .../SailingServer (No Proxy, Cached MTB).launch | 1 + .../SailingServer (No Proxy, Jetty on 8889).launch | 1 + ...ilingServer (No Proxy, Jetty on 8889, Cached MTB).launch | 6 +++--- .../SailingServer (No Proxy, No Tests).launch | 1 + .../SailingServer (No Proxy, Remote Debug SAP VM).launch | 1 + .../SailingServer (No Proxy, winddbTest).launch | 1 + java/com.sap.sailing.server/SailingServer (Proxy).launch | 1 + .../SailingServer (Proxy, Jetty on 8889).launch | 1 + .../SailingServer (Proxy, Remote Debug SAP VM).launch | 1 + ...ailingServer (Proxy, SwissTiming Live Simulation).launch | 1 + .../SailingServer (Proxy, winddbTest).launch | 1 + 13 files changed, 15 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT.launch b/java/com.sap.sailing.gwt.ui/SailingGWT.launch index 85ca243f036..1b2a4d1d0b3 100644 --- a/java/com.sap.sailing.gwt.ui/SailingGWT.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT.launch @@ -48,7 +48,7 @@ - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index 29b8319d54f..c20e858e10a 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -29,6 +29,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch index 7c606c45997..674726e1e17 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Cached MTB).launch @@ -28,6 +28,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch index 85203037897..e7dccecf548 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889).launch @@ -22,6 +22,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch index 034574f802a..769f3097e1c 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Jetty on 8889, Cached MTB).launch @@ -16,17 +16,17 @@ - + - + - + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch index 53257f9739f..a30d158bdd7 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, No Tests).launch @@ -21,6 +21,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch index 5d4abec70b7..bf6b8a91c57 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, Remote Debug SAP VM).launch @@ -21,6 +21,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch index 806b66a5b16..9c25ff3b370 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy, winddbTest).launch @@ -28,6 +28,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy).launch b/java/com.sap.sailing.server/SailingServer (Proxy).launch index 4590e23886b..16d92dd2cec 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy).launch @@ -29,6 +29,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch index 87b28030c5e..3d617d5a2b4 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Jetty on 8889).launch @@ -22,6 +22,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch b/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch index 5a7478bd1b3..25feadbf360 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, Remote Debug SAP VM).launch @@ -22,6 +22,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch b/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch index 63ad8a6ebf4..000439b6adf 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, SwissTiming Live Simulation).launch @@ -21,6 +21,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch b/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch index 1c05f339bc9..b296e7db375 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy, winddbTest).launch @@ -22,6 +22,7 @@ + From 4763026d8669ebe56e76d5dbbac65fc09844a882 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 22 Oct 2014 13:05:27 +0200 Subject: [PATCH 45/72] First end-to-end functionality between polar api and ui now works a lot of data still stubbed --- .../sailing/polars/mining/PolarDataMiner.java | 18 ++++++++++++------ 1 file changed, 12 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index 68d36519ccb..06bbe8992d9 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -119,23 +119,27 @@ public class PolarDataMiner { public PolarSheetsData createFullSheetForBoatClass(BoatClass boatClass) { Integer[] defaultWindSpeeds = defaultPolarSheetGenerationSettings.getWindStepping().getRawStepping(); Number[][] averagedPolarDataByWindSpeed = new Number[defaultWindSpeeds.length][360]; - for (int windIndex = 0; windIndex < defaultWindSpeeds.length - 1; windIndex++) { + for (int windIndex = 0; windIndex < defaultWindSpeeds.length; windIndex++) { Integer windSpeed = defaultWindSpeeds[windIndex]; for (int angle = 0; angle < 360; angle++) { SpeedWithConfidence speedWithConfidence; try { - speedWithConfidence = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(angle)); + int convertedAngle = angle; + if (angle > 180) { + convertedAngle = angle - 360; + } + speedWithConfidence = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); } catch (NotEnoughDataHasBeenAddedException e) { speedWithConfidence = new SpeedWithConfidenceImpl(new KnotSpeedImpl(0), 0, 0); } - averagedPolarDataByWindSpeed[windIndex][angle] = speedWithConfidence.getScalableValue().getValue(); + averagedPolarDataByWindSpeed[windIndex][angle] = speedWithConfidence.getObject().getKnots(); } } // FIXME Remove hard coded size values Map dataCountPerAngleForWindspeed = new HashMap<>(); Map> histogramDataMap = new HashMap<>(); - for (int windIndex = 0; windIndex < defaultWindSpeeds.length - 1; windIndex++) { + for (int windIndex = 0; windIndex < defaultWindSpeeds.length; windIndex++) { Integer[] perAngle = new Integer[360]; Map perWindSpeed = new HashMap<>(); for (int angle = 0; angle < 360; angle++) { @@ -147,8 +151,10 @@ public class PolarDataMiner { Map yValuesByDayAndGaugeId = new HashMap<>(); int dataCount = 100; double coefficiantOfVariation = 0.8; - perWindSpeed.put(angle, new PolarSheetsHistogramDataImpl(angle, xValues, yValues, yValuesByGaugeIds, - yValuesByDay, yValuesByDayAndGaugeId, dataCount, coefficiantOfVariation)); + PolarSheetsHistogramDataImpl polarSheetsHistogramDataImpl = new PolarSheetsHistogramDataImpl(angle, xValues, yValues, yValuesByGaugeIds, + yValuesByDay, yValuesByDayAndGaugeId, dataCount, coefficiantOfVariation); + polarSheetsHistogramDataImpl.setConfidenceMeasure(0.5); + perWindSpeed.put(angle, polarSheetsHistogramDataImpl); } histogramDataMap.put(windIndex, perWindSpeed); dataCountPerAngleForWindspeed.put(windIndex, perAngle); From 0b9843cb5660a158f2e938da2e9b76d78dd3b16b Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Sun, 26 Oct 2014 10:12:26 +0100 Subject: [PATCH 46/72] Only estimated wind is used for bearing in backend polars --- .../PolarSheetGenerationSettingsImpl.java | 4 +- .../ui/polarsheets/PolarSheetsChartPanel.java | 8 ++- .../mining/GPSFixMovingWithPolarContext.java | 35 +++++++++- .../IncrementalRegressionProcessor.java | 8 ++- .../sailing/polars/mining/PolarDataMiner.java | 70 ++++++++++++------- .../polars/regression/BoatSpeedEstimator.java | 4 ++ .../IncrementalLinearRegressionProcessor.java | 2 + .../IncrementalLeastSquaresProcessor.java | 5 ++ 8 files changed, 102 insertions(+), 34 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java index 426eef99c03..031fab4aa03 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetGenerationSettingsImpl.java @@ -5,9 +5,9 @@ import com.sap.sailing.domain.common.PolarSheetGenerationSettings; public class PolarSheetGenerationSettingsImpl implements PolarSheetGenerationSettings { public static PolarSheetGenerationSettings createStandardPolarSettings() { - Integer[] levels = { 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34 }; + Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 }; WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.5); - return new PolarSheetGenerationSettingsImpl(200, 0.1, 10, 20, 0.1, true, true, 2, 0.05, true, windStepping, + return new PolarSheetGenerationSettingsImpl(50, 0.1, 20, 20, 0.1, true, true, 2, 0.05, true, windStepping, false); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsChartPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsChartPanel.java index db392c63865..04e660160c4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsChartPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsChartPanel.java @@ -121,7 +121,7 @@ public class PolarSheetsChartPanel extends DockLayoutPanel { createSeriesForWindspeed(seriesId, i, result.getStepping().getRawStepping()[i]); } Series series = seriesMap.get(seriesId)[i]; - series.setPoints(result.getAveragedPolarDataByWindSpeed()[i], false); + //series.setPoints(result.getAveragedPolarDataByWindSpeed()[i], false); Point[] points = createPointsWithMarkerAlphaAccordingToDataCount(result, i); if (points != null) { series.setPoints(points); @@ -154,14 +154,16 @@ public class PolarSheetsChartPanel extends DockLayoutPanel { Point[] points = new Point[360]; List dataCountList = Arrays.asList(result.getDataCountPerAngleForWindspeed(windspeed)); Integer max = Collections.max(dataCountList); - if (max <= 0) { + if (max < settings.getMinimumDataCountPerAngle()) { return null; } for (int i = 0; i < 360; i++) { if (result.getHistogramDataMap().get(windspeed) == null || result.getHistogramDataMap().get(windspeed).get(i) == null || result.getHistogramDataMap().get(windspeed).get(i).getConfidenceMeasure() < settings - .getMinimumConfidenceMeasure()) { + .getMinimumConfidenceMeasure() + || result.getHistogramDataMap().get(windspeed).get(i).getDataCount() < settings + .getMinimumDataCountPerAngle()) { points[i] = new Point(0); continue; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index 4a9debd73e9..ba0a1711060 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -1,10 +1,15 @@ package com.sap.sailing.polars.mining; +import java.util.HashSet; +import java.util.Set; + import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.WindSource; +import com.sap.sailing.domain.common.WindSourceType; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.Wind; @@ -17,6 +22,7 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { private final TrackedRace race; private final Competitor competitor; private final ClusterGroup windSpeedClusterGroup; + private final Set windSourcesToExcludeForBearing; public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor, ClusterGroup windSpeedClusterGroup) { @@ -24,6 +30,7 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { this.race = race; this.competitor = competitor; this.windSpeedClusterGroup = windSpeedClusterGroup; + this.windSourcesToExcludeForBearing = collectWindSourcesToIgnoreForBearing(); } public GPSFixMoving getFix() { @@ -45,7 +52,7 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { public Bearing getAngleToTheWind() { SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); - Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint()); + Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForBearing); Bearing bearing = boatSpeed.getBearing(); return wind.getFrom().getDifferenceTo(bearing); } @@ -69,5 +76,31 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { public BoatClass getBoatClass() { return race.getRace().getBoatClass(); } + + private Set collectWindSourcesToIgnoreForBearing() { + Set windSourcesToExclude = new HashSet(); + Iterable combinedSources = race.getWindSources(WindSourceType.COMBINED); + for (WindSource combinedSource : combinedSources) { + windSourcesToExclude.add(combinedSource); + } + Iterable courseSources = race.getWindSources(WindSourceType.COURSE_BASED); + for (WindSource courseSource : courseSources) { + windSourcesToExclude.add(courseSource); + } + + Iterable expSources = race.getWindSources(WindSourceType.EXPEDITION); + for (WindSource expSource : expSources) { + windSourcesToExclude.add(expSource); + } + Iterable rcSources = race.getWindSources(WindSourceType.RACECOMMITTEE); + for (WindSource rcSource : rcSources) { + windSourcesToExclude.add(rcSource); + } + Iterable webSources = race.getWindSources(WindSourceType.WEB); + for (WindSource webSource : webSources) { + windSourcesToExclude.add(webSource); + } + return windSourcesToExclude; + } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index 95a68118a37..32525f3a77b 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -13,6 +13,7 @@ import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.polars.regression.BoatSpeedEstimator; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; +import com.sap.sse.common.Util.Pair; import com.sap.sse.datamining.AdditionalResultDataBuilder; import com.sap.sse.datamining.components.Processor; import com.sap.sse.datamining.data.Cluster; @@ -67,9 +68,10 @@ public class IncrementalRegressionProcessor implements Processor estimateBoatSpeed(final BoatClass boatClass, final Speed windSpeed, + public Pair, Integer> estimateBoatSpeed(final BoatClass boatClass, final Speed windSpeed, final Bearing angleToTheWind) throws NotEnoughDataHasBeenAddedException { + int dataCount = 0; PolarClusterKey key = new PolarClusterKey() { @Override public RoundedAngleToTheWind getRoundedAngleToTheWind() { @@ -100,7 +102,9 @@ public class IncrementalRegressionProcessor implements Processor(speedWithoutConfidence, /* FIXME */1.0, /* FIXME */0); + dataCount = boatSpeedEstimator.getDataCount(); + return new Pair, Integer>(new SpeedWithConfidenceImpl( + speedWithoutConfidence, /* FIXME */1.0, /* FIXME */0), dataCount); } @Override diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index 06bbe8992d9..aa5c4866981 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -29,6 +29,7 @@ import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; +import com.sap.sse.common.Util.Pair; import com.sap.sse.common.Util.Triple; import com.sap.sse.datamining.components.Processor; import com.sap.sse.datamining.data.ClusterGroup; @@ -111,7 +112,7 @@ public class PolarDataMiner { public SpeedWithConfidence estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind) throws NotEnoughDataHasBeenAddedException { - return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind); + return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind).getA(); } @@ -119,41 +120,39 @@ public class PolarDataMiner { public PolarSheetsData createFullSheetForBoatClass(BoatClass boatClass) { Integer[] defaultWindSpeeds = defaultPolarSheetGenerationSettings.getWindStepping().getRawStepping(); Number[][] averagedPolarDataByWindSpeed = new Number[defaultWindSpeeds.length][360]; + + Map dataCountPerAngleForWindspeed = new HashMap<>(); + Map> histogramDataMap = new HashMap<>(); + + int totalDataCount = 0; + for (int windIndex = 0; windIndex < defaultWindSpeeds.length; windIndex++) { Integer windSpeed = defaultWindSpeeds[windIndex]; + + Integer[] perAngle = new Integer[360]; + Map perWindSpeed = new HashMap<>(); for (int angle = 0; angle < 360; angle++) { - SpeedWithConfidence speedWithConfidence; + Pair, Integer> speedWithConfidenceAndDataCount; try { int convertedAngle = angle; if (angle > 180) { convertedAngle = angle - 360; } - speedWithConfidence = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); + speedWithConfidenceAndDataCount = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); } catch (NotEnoughDataHasBeenAddedException e) { - speedWithConfidence = new SpeedWithConfidenceImpl(new KnotSpeedImpl(0), 0, 0); + speedWithConfidenceAndDataCount = new Pair, Integer>(new SpeedWithConfidenceImpl(new KnotSpeedImpl(0), 0, 0), 0); } - averagedPolarDataByWindSpeed[windIndex][angle] = speedWithConfidence.getObject().getKnots(); - } - } - - // FIXME Remove hard coded size values - Map dataCountPerAngleForWindspeed = new HashMap<>(); - Map> histogramDataMap = new HashMap<>(); - for (int windIndex = 0; windIndex < defaultWindSpeeds.length; windIndex++) { - Integer[] perAngle = new Integer[360]; - Map perWindSpeed = new HashMap<>(); - for (int angle = 0; angle < 360; angle++) { - perAngle[angle] = /* FIXME */100; - Number[] xValues = {5}; - Number[] yValues = {10};; - Map yValuesByGaugeIds = new HashMap<>(); - Map yValuesByDay = new HashMap<>(); - Map yValuesByDayAndGaugeId = new HashMap<>(); - int dataCount = 100; + + averagedPolarDataByWindSpeed[windIndex][angle] = speedWithConfidenceAndDataCount.getA().getObject().getKnots(); + int dataCount = speedWithConfidenceAndDataCount.getB(); + + totalDataCount = totalDataCount + dataCount; + //FIXME hard coded double coefficiantOfVariation = 0.8; - PolarSheetsHistogramDataImpl polarSheetsHistogramDataImpl = new PolarSheetsHistogramDataImpl(angle, xValues, yValues, yValuesByGaugeIds, - yValuesByDay, yValuesByDayAndGaugeId, dataCount, coefficiantOfVariation); - polarSheetsHistogramDataImpl.setConfidenceMeasure(0.5); + double confidenceMeasure = 0.5; + + PolarSheetsHistogramDataImpl polarSheetsHistogramDataImpl = createEmptyHistogramData(perAngle, angle, + dataCount, coefficiantOfVariation, confidenceMeasure); perWindSpeed.put(angle, polarSheetsHistogramDataImpl); } histogramDataMap.put(windIndex, perWindSpeed); @@ -161,13 +160,32 @@ public class PolarDataMiner { } - PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, /* FIXME */100000, + PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, totalDataCount, dataCountPerAngleForWindspeed, defaultPolarSheetGenerationSettings.getWindStepping(), histogramDataMap); return data; } + private PolarSheetsHistogramDataImpl createEmptyHistogramData(Integer[] perAngle, int angle, int dataCount, + double coefficiantOfVariation, double confidenceMeasure) { + perAngle[angle] = dataCount; + Number[] xValues = {}; + Number[] yValues = {};; + Map yValuesByGaugeIds = new HashMap<>(); + Map yValuesByDay = new HashMap<>(); + Map yValuesByDayAndGaugeId = new HashMap<>(); + + + PolarSheetsHistogramDataImpl polarSheetsHistogramDataImpl = new PolarSheetsHistogramDataImpl(angle, xValues, yValues, yValuesByGaugeIds, + yValuesByDay, yValuesByDayAndGaugeId, dataCount, coefficiantOfVariation); + + polarSheetsHistogramDataImpl.setConfidenceMeasure(confidenceMeasure); + return polarSheetsHistogramDataImpl; + } + + + public Set getAvailableBoatClasses() { return incrementalRegressionProcessor.getAvailableBoatClasses(); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java index 6ffd8719cfc..93339d7bd42 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java @@ -47,4 +47,8 @@ public class BoatSpeedEstimator { } } + public int getDataCount() { + return windSpeedRegression.getDataCount(); + } + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java index a22275a7ab3..a094deab94e 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/IncrementalLinearRegressionProcessor.java @@ -10,4 +10,6 @@ public interface IncrementalLinearRegressionProcessor { double getIntercept(); + int getDataCount(); + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java index d84061448a9..02acf4c3533 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/impl/IncrementalLeastSquaresProcessor.java @@ -114,4 +114,9 @@ public class IncrementalLeastSquaresProcessor implements IncrementalLinearRegres return alpha0; } + @Override + public int getDataCount() { + return numberOfPointsAdded; + } + } From 8b2bfe43b62258f2eb9117aa812e4df93c3bf2aa Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 28 Oct 2014 21:10:31 +0100 Subject: [PATCH 47/72] performance tree-grow: minimize calls to TrackedRace.getWind(...); --- .../sap/sailing/simulator/impl/PathCandidate.java | 2 ++ .../simulator/impl/PathGeneratorTreeGrow.java | 15 +++++++-------- 2 files changed, 9 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java index 3fd06735af1..b3025cc9d22 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathCandidate.java @@ -7,6 +7,7 @@ public class PathCandidate implements Comparable { public PathCandidate(TimedPosition pos, boolean reached, double vrt, double hrz, int trn, String path, char sid, Wind wind) { this.pos = pos; // time and position + this.wind = wind; this.reached = reached; this.vrt = vrt; // height of target projected onto wind this.hrz = hrz; // distance from middle line @@ -16,6 +17,7 @@ public class PathCandidate implements Comparable { } TimedPosition pos; + Wind wind; boolean reached; double vrt; double hrz; diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java index db9a085051b..0e6fd5af919 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java @@ -104,13 +104,11 @@ public class PathGeneratorTreeGrow extends PathGeneratorBase { // generate step in one of the possible directions // default: L - left, R - right // extended: M - wide left, S - wide right - TimedPosition getStep(TimedPosition pos, long timeStep, long turnLoss, boolean sameBaseDirection, char nextDirection) { + TimedPosition getStep(TimedPosition pos, Wind posWind, long timeStep, long turnLoss, boolean sameBaseDirection, char nextDirection) { double offDeg = 3.0; - WindFieldGenerator wf = this.parameters.getWindField(); TimePoint curTime = pos.getTimePoint(); Position curPosition = pos.getPosition(); - Wind posWind = wf.getWind(new TimedPositionImpl(curTime, curPosition)); PolarDiagram pd = this.parameters.getBoatPolarDiagram(); pd.setWind(posWind); @@ -180,7 +178,7 @@ public class PathGeneratorTreeGrow extends PathGeneratorBase { } // calculate next path position (taking turn-loss into account) - TimedPosition pathPos = this.getStep(path.pos, timeStep, turnLoss, sameBaseDirection, nextDirection); + TimedPosition pathPos = this.getStep(path.pos, path.wind, timeStep, turnLoss, sameBaseDirection, nextDirection); // determine apparent wind at next path position & time Wind posWind = this.parameters.getWindField().getWind(pathPos); @@ -231,7 +229,7 @@ public class PathGeneratorTreeGrow extends PathGeneratorBase { // extend path-string by step-direction String pathStr = path.path + nextDirection; - return (new PathCandidate(pathPos, reachedEnd, vrtDist, hrzDist, turnCount, pathStr, nextDirection, appWind)); + return (new PathCandidate(pathPos, reachedEnd, vrtDist, hrzDist, turnCount, pathStr, nextDirection, posWind)); } @@ -523,9 +521,9 @@ public class PathGeneratorTreeGrow extends PathGeneratorBase { allPaths.add(initPath); - TimedPosition tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), usedTimeStep, turnLoss, true, 'L'); + TimedPosition tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), wndStart, usedTimeStep, turnLoss, true, 'L'); double tstDist1 = startPos.getDistance(tstPosition.getPosition()).getMeters(); - tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), usedTimeStep, turnLoss, true, 'R'); + tstPosition = this.getStep(new TimedPositionImpl(startTime, startPos), wndStart, usedTimeStep, turnLoss, true, 'R'); double tstDist2 = startPos.getDistance(tstPosition.getPosition()).getMeters(); double hrzBinSize = (tstDist1 + tstDist2)/6.0; // horizontal bin size in meters @@ -702,7 +700,8 @@ public class PathGeneratorTreeGrow extends PathGeneratorBase { } else { boolean sameBaseDirection = this.isSameDirection(prevDirection, nextDirection); - TimedPosition newPosition = this.getStep(curPosition, usedTimeStep, turnLoss, sameBaseDirection, nextDirection); + Wind curWind = wf.getWind(curPosition); + TimedPosition newPosition = this.getStep(curPosition, curWind, usedTimeStep, turnLoss, sameBaseDirection, nextDirection); curPosition = new TimedPositionWithSpeedImpl(newPosition.getTimePoint(), newPosition.getPosition(), null); path.add(curPosition); From 76645928893a25724020df39d72bc8bf1fa6b872 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 28 Oct 2014 22:25:31 +0100 Subject: [PATCH 48/72] performance tree-grow: quantize positions & timepoints when accessing TrackedRace.getWind(...) to support caching; --- .../impl/WindFieldTrackedRaceImpl.java | 17 ++++++++++++++++- 1 file changed, 16 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java index b8a45c627fb..3d9d94e589d 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java @@ -1,5 +1,10 @@ package com.sap.sailing.simulator.windfield.impl; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.common.impl.MeterDistance; +import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.simulator.TimedPosition; @@ -16,6 +21,8 @@ import com.sap.sailing.simulator.windfield.WindFieldGenerator; public class WindFieldTrackedRaceImpl extends WindFieldGeneratorImpl implements WindFieldGenerator { private static final long serialVersionUID = -7005970781594631010L; + private static final double EPSILON_DISTANCE_METER = 20; + private static final long EPSILON_TIME_MILLIS = 5000; private TrackedRace race; public WindFieldTrackedRaceImpl(TrackedRace race) { @@ -25,7 +32,15 @@ public class WindFieldTrackedRaceImpl extends WindFieldGeneratorImpl implements @Override public Wind getWind(TimedPosition timedPosition) { - return this.race.getWind(timedPosition.getPosition(), timedPosition.getTimePoint()); + Position pos = timedPosition.getPosition(); + double epsLat = (new MeterDistance(EPSILON_DISTANCE_METER)).getCentralAngleDeg(); + double qLat = Math.floor(pos.getLatDeg() / epsLat) * epsLat; + double epsLng = epsLat / Math.cos(pos.getLatDeg()); + double qLng = Math.floor(pos.getLngDeg() / epsLng) * epsLng; + TimePoint time = timedPosition.getTimePoint(); + long epsTime = EPSILON_TIME_MILLIS; + long qTime = (long) (Math.floor(time.asMillis() / epsTime) * epsTime); + return this.race.getWind(new DegreePosition(qLat, qLng), new MillisecondsTimePoint(qTime)); } } From 2307f8a3a948186d80766e84cacf558b02e42321 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 29 Oct 2014 10:19:30 +0100 Subject: [PATCH 49/72] Added new histogram to polar sheets panel showing datacount over angle --- .../AngleOverDataSizeHistogramPanel.java | 104 ++++++++++++++++++ .../ui/polarsheets/PolarSheetsChartPanel.java | 6 +- .../gwt/ui/polarsheets/PolarSheetsPanel.java | 14 ++- .../mining/GPSFixMovingWithPolarContext.java | 29 ++++- 4 files changed, 147 insertions(+), 6 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/AngleOverDataSizeHistogramPanel.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/AngleOverDataSizeHistogramPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/AngleOverDataSizeHistogramPanel.java new file mode 100644 index 00000000000..f5f62742d29 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/AngleOverDataSizeHistogramPanel.java @@ -0,0 +1,104 @@ +package com.sap.sailing.gwt.ui.polarsheets; + +import java.util.Map; + +import org.moxieapps.gwt.highcharts.client.AxisTitle; +import org.moxieapps.gwt.highcharts.client.Chart; +import org.moxieapps.gwt.highcharts.client.Point; +import org.moxieapps.gwt.highcharts.client.Series; +import org.moxieapps.gwt.highcharts.client.Series.Type; +import org.moxieapps.gwt.highcharts.client.labels.XAxisLabels; +import org.moxieapps.gwt.highcharts.client.plotOptions.AreaPlotOptions; +import org.moxieapps.gwt.highcharts.client.plotOptions.Marker; +import org.moxieapps.gwt.highcharts.client.plotOptions.PlotOptions.Stacking; + +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.DockLayoutPanel; +import com.sap.sailing.domain.common.PolarSheetsHistogramData; +import com.sap.sailing.gwt.ui.client.StringMessages; + +/** + * Shows the underlying distribution for any point in the polar diagrams + * of the {@link PolarSheetsChartPanel}. + * + * @author d054528 Frederik Petersen + * + */ +public class AngleOverDataSizeHistogramPanel extends DockLayoutPanel { + + private final Chart chart; + private final StringMessages stringMessages; + + public AngleOverDataSizeHistogramPanel(StringMessages stringMessages) { + super(Unit.PX); + this.stringMessages = stringMessages; + setSize("100%", "100%"); + + chart = createHistogramChart(); + chart.getElement().setAttribute("align", "top"); + add(chart); + } + + private Chart createHistogramChart() { + Chart histogramChart = new Chart().setType(Type.AREA).setHeight100().setWidth100(); + histogramChart.setChartTitleText(stringMessages.histogram()); + histogramChart.getYAxis().setMin(0).setAxisTitle(new AxisTitle().setText(stringMessages.numberOfDataPoints())); + histogramChart.getXAxis().setLabels(new XAxisLabels().setRotation(-90f).setY(10)) + .setAxisTitle(new AxisTitle().setText(stringMessages.beatAngle())); + histogramChart.setAreaPlotOptions(new AreaPlotOptions().setStacking(Stacking.NORMAL).setLineColor("#666666") + .setLineWidth(1).setMarker(new Marker().setLineWidth(1).setLineColor("#666666"))); + return histogramChart; + } + + public void addData(Map histogramDataPerAngle) { + chart.setTitle(""); + Series series = chart.createSeries(); + + Point[] points = toPoints(histogramDataPerAngle); + series.setPoints(points); + chart.addSeries(series); + } + + private Point[] toPoints(Map histogramDataPerAngle) { + Number[] xValues = new Number[360]; + Number[] yValues = new Number[360]; + for (int i = 0; i < 360; i++) { + xValues[i] = i; + yValues[i] = histogramDataPerAngle.get(i).getDataCount(); + } + + return toPoints(xValues, yValues); + } + + public Point[] toPoints(Number[] xValues, Number[] yValues) { + Point[] points = new Point[xValues.length]; + for (int i = 0; i < xValues.length; i++) { + points[i] = new Point(xValues[i], yValues[i]); + } + return points; + } + + @Override + protected void onLoad() { + Timer timer = new Timer() { + + @Override + public void run() { + chart.setSizeToMatchContainer(); + } + }; + timer.schedule(200); + super.onLoad(); + } + + @Override + public void onResize() { + chart.setSizeToMatchContainer(); + super.onResize(); + } + + + + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsChartPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsChartPanel.java index 04e660160c4..22aba90c31f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsChartPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsChartPanel.java @@ -42,10 +42,13 @@ public class PolarSheetsChartPanel extends DockLayoutPanel { private PolarSheetGenerationSettings settings; private final Map polarSheetsDataMap; + + private final AngleOverDataSizeHistogramPanel angleOverDataSizeHistogramPanel; - public PolarSheetsChartPanel(StringMessages stringMessages) { + public PolarSheetsChartPanel(StringMessages stringMessages, AngleOverDataSizeHistogramPanel angleOverDataSizeHistogramPanel) { super(Unit.PCT); this.stringMessages = stringMessages; + this.angleOverDataSizeHistogramPanel = angleOverDataSizeHistogramPanel; polarSheetsDataMap = new HashMap(); setSize("100%", "100%"); chart = createPolarSheetChart(); @@ -125,6 +128,7 @@ public class PolarSheetsChartPanel extends DockLayoutPanel { Point[] points = createPointsWithMarkerAlphaAccordingToDataCount(result, i); if (points != null) { series.setPoints(points); + angleOverDataSizeHistogramPanel.addData(result.getHistogramDataMap().get(i)); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsPanel.java index 0602bcdeace..9b1ffa26343 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsPanel.java @@ -60,6 +60,7 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC private List selectedRaces; private PolarSheetsChartPanel chartPanel; private PolarSheetsHistogramPanel histogramPanel; + private AngleOverDataSizeHistogramPanel angleOverDataSizeHistogramPanel; private Label polarSheetsGenerationLabel; private Label dataCountLabel; @@ -97,7 +98,8 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC leftPanel.add(namingContainer); addBoatClassDiagramArea(leftPanel); DockLayoutPanel rightPanel = new DockLayoutPanel(Unit.PCT); - PolarSheetsChartPanel polarSheetsChartPanel = createPolarSheetsChartPanel(); + angleOverDataSizeHistogramPanel = new AngleOverDataSizeHistogramPanel(stringMessages); + PolarSheetsChartPanel polarSheetsChartPanel = createPolarSheetsChartPanel(angleOverDataSizeHistogramPanel); DockLayoutPanel polarChartAndControlPanel = new DockLayoutPanel(Unit.PCT); PolarChartControlPanel polarChartControlPanel = new PolarChartControlPanel(stringMessages, polarSheetsChartPanel); @@ -111,7 +113,11 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC rightPanel.addNorth(polarChartAndControlPanel, 70); histogramPanel = new PolarSheetsHistogramPanel(stringMessages); histogramPanel.getElement().setAttribute("align", "top"); - rightPanel.addSouth(histogramPanel, 30); + //rightPanel.addSouth(histogramPanel, 30); + + + angleOverDataSizeHistogramPanel.getElement().setAttribute("align", "top"); + rightPanel.addSouth(angleOverDataSizeHistogramPanel, 30); add(rightPanel); @@ -198,8 +204,8 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC return polarSheetsGenerationStatusLabel; } - private PolarSheetsChartPanel createPolarSheetsChartPanel() { - chartPanel = new PolarSheetsChartPanel(stringMessages); + private PolarSheetsChartPanel createPolarSheetsChartPanel(AngleOverDataSizeHistogramPanel angleOverDataSizeHistogramPanel) { + chartPanel = new PolarSheetsChartPanel(stringMessages, angleOverDataSizeHistogramPanel); return chartPanel; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index ba0a1711060..cd261b52bdc 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -23,6 +23,7 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { private final Competitor competitor; private final ClusterGroup windSpeedClusterGroup; private final Set windSourcesToExcludeForBearing; + private final Iterable windSourcesToExcludeForSpeed; public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor, ClusterGroup windSpeedClusterGroup) { @@ -31,6 +32,7 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { this.competitor = competitor; this.windSpeedClusterGroup = windSpeedClusterGroup; this.windSourcesToExcludeForBearing = collectWindSourcesToIgnoreForBearing(); + this.windSourcesToExcludeForSpeed = collectWindSourcesToIgnoreForSpeed(); } public GPSFixMoving getFix() { @@ -64,7 +66,7 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { } public Speed getWindSpeed() { - Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint()); + Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForSpeed); return wind; } @@ -102,5 +104,30 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { } return windSourcesToExclude; } + + private Iterable collectWindSourcesToIgnoreForSpeed() { + Set windSourcesToExclude = new HashSet(); + Iterable combinedSources = race.getWindSources(WindSourceType.COMBINED); + for (WindSource combinedSource : combinedSources) { + windSourcesToExclude.add(combinedSource); + } + Iterable courseSources = race.getWindSources(WindSourceType.COURSE_BASED); + for (WindSource courseSource : courseSources) { + windSourcesToExclude.add(courseSource); + } + Iterable trackBasedSources = race.getWindSources(WindSourceType.TRACK_BASED_ESTIMATION); + for (WindSource trackBasedSource : trackBasedSources) { + windSourcesToExclude.add(trackBasedSource); + } + Iterable rcSources = race.getWindSources(WindSourceType.RACECOMMITTEE); + for (WindSource rcSource : rcSources) { + windSourcesToExclude.add(rcSource); + } + Iterable webSources = race.getWindSources(WindSourceType.WEB); + for (WindSource webSource : webSources) { + windSourcesToExclude.add(webSource); + } + return windSourcesToExclude; + } } From 3bd6a0f797173f0f073cd818a64daa9d5527f9e2 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 29 Oct 2014 20:09:06 +0100 Subject: [PATCH 50/72] performance tree-grow: fix cos(lat) factor in longitude quantization; add wind-cache to wind-field-tracked-race used by tree-grow; --- .../simulator/impl/TimedPositionImpl.java | 19 ++++++++++++++++++- .../impl/WindFieldTrackedRaceImpl.java | 18 +++++++++++++++--- 2 files changed, 33 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/TimedPositionImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/TimedPositionImpl.java index e24f240c049..485dd6786d0 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/TimedPositionImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/TimedPositionImpl.java @@ -10,7 +10,8 @@ public class TimedPositionImpl implements TimedPosition { * */ private static final long serialVersionUID = -675796846985362731L; - + private transient int hashCode; + TimePoint timePoint; Position position; @@ -29,4 +30,20 @@ public class TimedPositionImpl implements TimedPosition { return position; } + @Override + public boolean equals(Object other) { + TimedPosition that = (TimedPosition) other; + return (this.position.equals(that.getPosition())&&(this.timePoint.equals(that.getTimePoint()))); + } + + @Override + public int hashCode( ) { + if ( hashCode == 0 ) { + hashCode = 17; + hashCode = 37 * hashCode + ( timePoint != null ? timePoint.hashCode( ) : 0 ); + hashCode = 37 * hashCode + ( position != null ? position.hashCode( ) : 0 ); + } + return hashCode; + } + } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java index 3d9d94e589d..0a29c351b7d 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java @@ -1,5 +1,7 @@ package com.sap.sailing.simulator.windfield.impl; +import java.util.concurrent.ConcurrentHashMap; + import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreePosition; @@ -8,6 +10,7 @@ import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.simulator.TimedPosition; +import com.sap.sailing.simulator.impl.TimedPositionImpl; import com.sap.sailing.simulator.windfield.WindFieldGenerator; /** @@ -24,10 +27,12 @@ public class WindFieldTrackedRaceImpl extends WindFieldGeneratorImpl implements private static final double EPSILON_DISTANCE_METER = 20; private static final long EPSILON_TIME_MILLIS = 5000; private TrackedRace race; + private final ConcurrentHashMap cache; public WindFieldTrackedRaceImpl(TrackedRace race) { super(null, null); this.race = race; + this.cache = new ConcurrentHashMap<>(); } @Override @@ -35,12 +40,19 @@ public class WindFieldTrackedRaceImpl extends WindFieldGeneratorImpl implements Position pos = timedPosition.getPosition(); double epsLat = (new MeterDistance(EPSILON_DISTANCE_METER)).getCentralAngleDeg(); double qLat = Math.floor(pos.getLatDeg() / epsLat) * epsLat; - double epsLng = epsLat / Math.cos(pos.getLatDeg()); + double epsLng = epsLat / Math.cos(qLat * Math.PI / 180.0); double qLng = Math.floor(pos.getLngDeg() / epsLng) * epsLng; TimePoint time = timedPosition.getTimePoint(); long epsTime = EPSILON_TIME_MILLIS; - long qTime = (long) (Math.floor(time.asMillis() / epsTime) * epsTime); - return this.race.getWind(new DegreePosition(qLat, qLng), new MillisecondsTimePoint(qTime)); + TimePoint qTime = new MillisecondsTimePoint((long) (Math.floor(time.asMillis() / epsTime) * epsTime)); + Position qPosition = new DegreePosition(qLat, qLng); + TimedPosition qTimedPosition = new TimedPositionImpl(qTime, qPosition); + Wind wind = cache.get(qTimedPosition); + if (wind == null) { + wind = this.race.getWind(qPosition, qTime); + cache.put(qTimedPosition, wind); + } + return wind; } } From 04714f3592dfd53c9513e0a22a76f44a1449173c Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 29 Oct 2014 20:25:49 +0100 Subject: [PATCH 51/72] fix leg start- and end-time for simulation; --- .../com/sap/sailing/gwt/ui/server/SailingServiceImpl.java | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 904db517e7e..efd8515859a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1490,13 +1490,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S if (trackedLeg == null) { return result; } - - Waypoint fromWaypoint = trackedLeg.getLeg().getFrom(); - Position startPosition = trackedRace.getApproximatePosition(fromWaypoint, fromTimePoint); + Waypoint fromWaypoint = trackedLeg.getLeg().getFrom(); // get next mark as end-position Waypoint toWaypoint = trackedLeg.getLeg().getTo(); - Position endPosition = trackedRace.getApproximatePosition(toWaypoint, fromTimePoint); TimePoint startTimePoint = null; TimePoint endTimePoint = null; @@ -1513,6 +1510,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } } legDuration = endTimePoint.asMillis() - startTimePoint.asMillis(); + Position startPosition = trackedRace.getApproximatePosition(fromWaypoint, startTimePoint); + Position endPosition = trackedRace.getApproximatePosition(toWaypoint, endTimePoint); if (startTimePoint.asDate().equals(prevStartTime)) { return new SimulatorResultsDTO(startTimePoint.asDate(), 0, null, null, null, null); From 8dead5f70deeb9b921c5ac3b892413dcfa1fc6a3 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Thu, 30 Oct 2014 20:44:57 +0100 Subject: [PATCH 52/72] performance tree-grow: remove local cache, because short-time-wind-cache does work correctly now; --- .../windfield/impl/WindFieldTrackedRaceImpl.java | 12 +----------- 1 file changed, 1 insertion(+), 11 deletions(-) diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java index 0a29c351b7d..5e6b7e429c4 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/windfield/impl/WindFieldTrackedRaceImpl.java @@ -1,7 +1,5 @@ package com.sap.sailing.simulator.windfield.impl; -import java.util.concurrent.ConcurrentHashMap; - import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.TimePoint; import com.sap.sailing.domain.common.impl.DegreePosition; @@ -10,7 +8,6 @@ import com.sap.sailing.domain.common.impl.MillisecondsTimePoint; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.simulator.TimedPosition; -import com.sap.sailing.simulator.impl.TimedPositionImpl; import com.sap.sailing.simulator.windfield.WindFieldGenerator; /** @@ -27,12 +24,10 @@ public class WindFieldTrackedRaceImpl extends WindFieldGeneratorImpl implements private static final double EPSILON_DISTANCE_METER = 20; private static final long EPSILON_TIME_MILLIS = 5000; private TrackedRace race; - private final ConcurrentHashMap cache; public WindFieldTrackedRaceImpl(TrackedRace race) { super(null, null); this.race = race; - this.cache = new ConcurrentHashMap<>(); } @Override @@ -46,12 +41,7 @@ public class WindFieldTrackedRaceImpl extends WindFieldGeneratorImpl implements long epsTime = EPSILON_TIME_MILLIS; TimePoint qTime = new MillisecondsTimePoint((long) (Math.floor(time.asMillis() / epsTime) * epsTime)); Position qPosition = new DegreePosition(qLat, qLng); - TimedPosition qTimedPosition = new TimedPositionImpl(qTime, qPosition); - Wind wind = cache.get(qTimedPosition); - if (wind == null) { - wind = this.race.getWind(qPosition, qTime); - cache.put(qTimedPosition, wind); - } + Wind wind = this.race.getWind(qPosition, qTime); return wind; } From 0066b170c25ef581fd7ddfbf346f6649bea5dbe9 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 5 Nov 2014 13:01:03 +0100 Subject: [PATCH 53/72] Fixed test and dataming update problems --- java/com.sap.sailing.polars.test/.classpath | 2 +- .../.settings/org.eclipse.jdt.core.prefs | 7 ---- .../META-INF/MANIFEST.MF | 2 +- .../mining/test/PolarDataMinerTest.java | 1 + .../mining/AbstractEnrichingProcessor.java | 5 +-- .../mining/GPSFixMovingWithOriginInfo.java | 33 +++++++++++++++++++ .../IncrementalRegressionProcessor.java | 7 ++-- .../sailing/polars/mining/PolarDataMiner.java | 15 ++++----- 8 files changed, 51 insertions(+), 21 deletions(-) delete mode 100644 java/com.sap.sailing.polars.test/.settings/org.eclipse.jdt.core.prefs create mode 100644 java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java diff --git a/java/com.sap.sailing.polars.test/.classpath b/java/com.sap.sailing.polars.test/.classpath index 098194ca4b7..16d067f284a 100644 --- a/java/com.sap.sailing.polars.test/.classpath +++ b/java/com.sap.sailing.polars.test/.classpath @@ -1,7 +1,7 @@ - + diff --git a/java/com.sap.sailing.polars.test/.settings/org.eclipse.jdt.core.prefs b/java/com.sap.sailing.polars.test/.settings/org.eclipse.jdt.core.prefs deleted file mode 100644 index f42de363afa..00000000000 --- a/java/com.sap.sailing.polars.test/.settings/org.eclipse.jdt.core.prefs +++ /dev/null @@ -1,7 +0,0 @@ -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.compliance=1.7 -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.7 diff --git a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF index d402c7e44f0..2501ceeaaa6 100644 --- a/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.polars.test/META-INF/MANIFEST.MF @@ -4,7 +4,7 @@ Bundle-Name: PolarsTest Bundle-SymbolicName: com.sap.sailing.polars.test Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP -Bundle-RequiredExecutionEnvironment: JavaSE-1.7 +Bundle-RequiredExecutionEnvironment: JavaSE-1.8 Fragment-Host: com.sap.sailing.polars Import-Package: com.sap.sailing.domain.test.mock, org.junit;version="4.8.2" diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java index a8e605d5780..ba11401af12 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java @@ -171,6 +171,7 @@ public class PolarDataMinerTest { Wind wind = new WindImpl(new DegreePosition(54.431952, 10.186767), startOfRace, windSpeed); // Always return same wind when(trackedRace.getWind(any(Position.class), any(TimePoint.class))).thenReturn(wind); + when(trackedRace.getWind(any(Position.class), any(TimePoint.class), any())).thenReturn(wind); return trackedRace; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java index be87815e4ec..67fc27b49b6 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AbstractEnrichingProcessor.java @@ -11,8 +11,9 @@ import com.sap.sse.datamining.impl.components.AbstractSimpleParallelProcessor; public abstract class AbstractEnrichingProcessor extends AbstractSimpleParallelProcessor { - public AbstractEnrichingProcessor(ExecutorService executor, Collection> resultReceivers) { - super(InputType, ResultType, executor, resultReceivers); + public AbstractEnrichingProcessor(Class inputType, Class resultType, + ExecutorService executor, Collection> resultReceivers) { + super(inputType, resultType, executor, resultReceivers); } @Override diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java new file mode 100644 index 00000000000..b3b90ed4bad --- /dev/null +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithOriginInfo.java @@ -0,0 +1,33 @@ +package com.sap.sailing.polars.mining; + +import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.tracking.GPSFixMoving; +import com.sap.sailing.domain.tracking.TrackedRace; + +public class GPSFixMovingWithOriginInfo { + + private final GPSFixMoving fix; + private final TrackedRace trackedRace; + private final Competitor competitor; + + public GPSFixMovingWithOriginInfo(GPSFixMoving fix, TrackedRace trackedRace, Competitor competitor) { + this.fix = fix; + this.trackedRace = trackedRace; + this.competitor = competitor; + } + + public GPSFixMoving getFix() { + return fix; + } + + public TrackedRace getTrackedRace() { + return trackedRace; + } + + public Competitor getCompetitor() { + return competitor; + } + + + +} diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index f8c7fb7b382..0a6712fdd0e 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -4,6 +4,7 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Map; import java.util.Set; +import java.util.logging.Logger; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.SpeedWithConfidence; @@ -23,6 +24,8 @@ import com.sap.sse.datamining.impl.components.GroupedDataEntry; import com.sap.sse.datamining.shared.GroupKey; public class IncrementalRegressionProcessor implements Processor,Void> { + + private static final Logger logger = Logger.getLogger(IncrementalRegressionProcessor.class.getName()); private final Map boatSpeedEstimators = new HashMap(); @@ -109,8 +112,8 @@ public class IncrementalRegressionProcessor implements Processor getAvailableBoatClasses() { diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index 7e4bc251240..cf7f3554f8d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -29,7 +29,6 @@ import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sse.common.Util.Pair; -import com.sap.sse.common.Util.Triple; import com.sap.sse.datamining.components.Processor; import com.sap.sse.datamining.data.ClusterGroup; import com.sap.sse.datamining.functions.Function; @@ -49,7 +48,7 @@ public class PolarDataMiner { } - private AbstractEnrichingProcessor, GPSFixMovingWithPolarContext> enrichingProcessor; + private AbstractEnrichingProcessor enrichingProcessor; private IncrementalRegressionProcessor incrementalRegressionProcessor; public PolarDataMiner() { @@ -82,16 +81,16 @@ public class PolarDataMiner { Processor filteringProcessor = new ParallelFilteringProcessor( GPSFixMovingWithPolarContext.class, executor, filteringResultReceivers, new PolarFixFilterCriteria()); - Collection> enrichingResultReceivers = Arrays + Collection> enrichingResultReceivers = Arrays .asList(filteringProcessor); - enrichingProcessor = new AbstractEnrichingProcessor, GPSFixMovingWithPolarContext>( - executor, enrichingResultReceivers) { + enrichingProcessor = new AbstractEnrichingProcessor( + GPSFixMovingWithOriginInfo.class, GPSFixMovingWithPolarContext.class, executor, enrichingResultReceivers) { @Override - protected GPSFixMovingWithPolarContext enrich(Triple element) { - return new GPSFixMovingWithPolarContext(element.getA(), element.getB(), element.getC(), + protected GPSFixMovingWithPolarContext enrich(GPSFixMovingWithOriginInfo element) { + return new GPSFixMovingWithPolarContext(element.getFix(), element.getTrackedRace(), element.getCompetitor(), speedClusterGroup); } }; @@ -99,7 +98,7 @@ public class PolarDataMiner { public void addFix(GPSFixMoving fix, Competitor competitor, TrackedRace trackedRace) { - enrichingProcessor.processElement(new Triple(fix, trackedRace, + enrichingProcessor.processElement(new GPSFixMovingWithOriginInfo(fix, trackedRace, competitor)); } From 4d42f06a5438c0ce353a99215e2d87777d693bd7 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 5 Nov 2014 16:21:05 +0100 Subject: [PATCH 54/72] Changed to polarSheetAnalyzer to behave as discussed with christopher --- .../domain/common/PolarSheetsData.java | 2 +- .../polars/analysis/PolarSheetAnalyzer.java | 15 +- .../analysis/impl/PolarSheetAnalyzerImpl.java | 148 +++++++++++++----- 3 files changed, 121 insertions(+), 44 deletions(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsData.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsData.java index 5c06f20c3c5..26f46b1853e 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsData.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsData.java @@ -9,7 +9,7 @@ public interface PolarSheetsData extends Serializable { int getDataCount(); - Integer[] getDataCountPerAngleForWindspeed(int beaufort); + Integer[] getDataCountPerAngleForWindspeed(int windIndex); WindStepping getStepping(); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java index e35327e863d..4d7a13e5176 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java @@ -3,13 +3,20 @@ package com.sap.sailing.polars.analysis; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; public interface PolarSheetAnalyzer { - SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, - Speed windSpeed); + SpeedWithBearing getOptimalUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, + Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, - Speed windSpeed); + SpeedWithBearing getOptimalDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, + Speed windSpeed) throws NotEnoughDataHasBeenAddedException; + + SpeedWithBearing getOptimalUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, + Speed windSpeed) throws NotEnoughDataHasBeenAddedException; + + SpeedWithBearing getOptimalDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, + Speed windSpeed) throws NotEnoughDataHasBeenAddedException; } \ No newline at end of file diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java index f3980c083ad..b67524dd82b 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java @@ -1,6 +1,12 @@ package com.sap.sailing.polars.analysis.impl; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; + import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; @@ -8,6 +14,7 @@ import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; /** * Extracts typical measures from polar sheets. @@ -17,6 +24,7 @@ import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; */ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { + private static final int MINIMUM_DATA_COUNT_FOR_ONE_ANGLE = 10; private PolarDataService polarDataService; public PolarSheetAnalyzerImpl(PolarDataService polarDataService) { @@ -24,53 +32,115 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } @Override - public SpeedWithBearing getOptimalUpwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { - PolarSheetsData data = polarDataService.getPolarSheetForBoatClass(boatClass); - Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed(); - int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); - Number[] dataForProvidedWindSpeed = dataPerWindSpeed[windSpeedIndex]; - if (dataForProvidedWindSpeed.length == 360) { - dataForProvidedWindSpeed = convertToOneSidedPolar(dataForProvidedWindSpeed); - } - double optUpwindSpeed = 0; - int optUpwindDeg = 0; - for (int i = 0; i < 90; i++) { - Number speed = dataForProvidedWindSpeed[i]; - double speedUpwind = speed.doubleValue() * Math.cos(Math.toRadians(i)); - if (speedUpwind > optUpwindSpeed) { - optUpwindDeg = i; - optUpwindSpeed = speedUpwind; - } - } - return new KnotSpeedWithBearingImpl(optUpwindSpeed, new DegreeBearingImpl(optUpwindDeg)); + public SpeedWithBearing getOptimalUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException { + int startAngleInclusive = 1; + int endAngleExclusive = 90; + SpeedWithBearing speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, + endAngleExclusive); + return speedWithBearing; + } + + + @Override + public SpeedWithBearing getOptimalDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException { + int startAngleInclusive = 91; + int endAngleExclusive = 180; + SpeedWithBearing speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, + endAngleExclusive); + return speedWithBearing; + } + + @Override + public SpeedWithBearing getOptimalDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException { + int startAngleInclusive = 181; + int endAngleExclusive = 270; + SpeedWithBearing speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, + endAngleExclusive); + return speedWithBearing; } @Override - public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) { - PolarSheetsData data = polarDataService.getPolarSheetForBoatClass(boatClass); - Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed(); - int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots()); - Number[] dataForProvidedWindSpeed = dataPerWindSpeed[windSpeedIndex]; - double optDownwindSpeed = 0; - int optUpwindDeg = 0; - for (int i = 90; i < 180; i++) { - Number speed = dataForProvidedWindSpeed[i]; - double speedUpwind = speed.doubleValue() * Math.sin(Math.toRadians(i - 90)); - if (speedUpwind > optDownwindSpeed) { - optUpwindDeg = i; - optDownwindSpeed = speedUpwind; + public SpeedWithBearing getOptimalUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException { + int startAngleInclusive = 271; + int endAngleExclusive = 360; + SpeedWithBearing speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, + endAngleExclusive); + return speedWithBearing; + } + + private SpeedWithBearing estimateAnglePeakAndAverageSpeed(BoatClass boatClass, Speed windSpeed, + int startAngleInclusive, int endAngleExclusive) throws NotEnoughDataHasBeenAddedException { + int estimatedPeak = estimatePeak(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); + + double averagedSpeed = averageSpeed(boatClass, windSpeed, estimatedPeak); + + Bearing bearing = new DegreeBearingImpl(estimatedPeak); + SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(averagedSpeed, bearing); + return speedWithBearing; + } + + private double averageSpeed(BoatClass boatClass, Speed windSpeed, int estimatedPeak) + throws NotEnoughDataHasBeenAddedException { + //Having the estimated peak we get the speed by averaging in a small area (3 points) around the peak + int numberOfSpeedValuesAdded = 0; + double sumOfSpeeds = 0; + int numberOfExceptions = 0; + for (int i = estimatedPeak - 1; i < estimatedPeak + 2; i++) { + try { + + int convertedAngleIfOver180 = i; + if (i > 180) { + convertedAngleIfOver180 = i - 360; + } + SpeedWithConfidence speed = polarDataService.getSpeed(boatClass, windSpeed, + new DegreeBearingImpl(convertedAngleIfOver180)); + // TODO use the confidence (has to be implemented Bottom to Top through the polar pipe + sumOfSpeeds = sumOfSpeeds + speed.getObject().getKnots(); + numberOfSpeedValuesAdded = numberOfSpeedValuesAdded + 1; + } catch (NotEnoughDataHasBeenAddedException e) { + if (numberOfExceptions > 0) { + // If more than one query throws error -> Throw error + throw e; + } + numberOfExceptions++; } } - return new KnotSpeedWithBearingImpl(optDownwindSpeed, new DegreeBearingImpl(optUpwindDeg)); + + double averagedSpeed = sumOfSpeeds / numberOfSpeedValuesAdded; + return averagedSpeed; } - private Number[] convertToOneSidedPolar(Number[] dataForProvidedWindSpeed) { - Number[] oneSided = new Number[180]; - for (int i = 181; i < 360; i++) { - oneSided[360 - i] = (dataForProvidedWindSpeed[360 - i].doubleValue() + dataForProvidedWindSpeed[i] - .doubleValue()) / 2; + private int estimatePeak(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) + throws NotEnoughDataHasBeenAddedException { + PolarSheetsData sheet = polarDataService.getPolarSheetForBoatClass(boatClass); + int windIndex = sheet.getStepping().getLevelIndexForValue(windSpeed.getKnots()); + Integer[] dataCountPerAngle = sheet.getDataCountPerAngleForWindspeed(windIndex); + + // Find peak by averaging the angles that have at least 50% of the max datacount + List dataCountList = Arrays.asList(dataCountPerAngle); + int maxDataCount = Collections.max(dataCountList); + if (maxDataCount < MINIMUM_DATA_COUNT_FOR_ONE_ANGLE) { + // The angle with the most data points doesn't have sufficient data, for the polar data to have any + // significance. + throw new NotEnoughDataHasBeenAddedException(); } - return oneSided; + int sumOfAllUpperHalfAngles = 0; + int numberOfAnglesAdded = 0; + for (int i = startAngleInclusive; i < endAngleExclusive; i++) { + if (dataCountList.get(i) > maxDataCount / 2) { + sumOfAllUpperHalfAngles = sumOfAllUpperHalfAngles + i; + numberOfAnglesAdded = numberOfAnglesAdded + 1; + } + } + + int estimatedPeak = sumOfAllUpperHalfAngles / numberOfAnglesAdded; + return estimatedPeak; } + + } From 07ac462db6b6dd65e41181aae4bc495056e20865 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Thu, 6 Nov 2014 21:47:45 +0100 Subject: [PATCH 55/72] Added realdata test case for polar sheet analyzer also fixed a little problem with the calculation of peak candidates --- .../polars/test/PolarSheetAnalyzerTest.java | 272 +++++++++++++++--- .../analysis/impl/PolarSheetAnalyzerImpl.java | 22 +- .../mining/GPSFixMovingWithPolarContext.java | 8 +- .../IncrementalRegressionProcessor.java | 7 +- 4 files changed, 258 insertions(+), 51 deletions(-) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java index 41d2e1d0bf2..890faa19a54 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -1,72 +1,264 @@ package com.sap.sailing.polars.test; -import java.util.concurrent.Executor; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.closeTo; +import static org.mockito.Matchers.any; +import static org.mockito.Matchers.argThat; +import static org.mockito.Mockito.mock; +import static org.mockito.Mockito.when; + +import java.util.HashMap; +import java.util.Map; -import org.junit.Assert; import org.junit.Test; +import org.mockito.ArgumentMatcher; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; +import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; +import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.analysis.impl.PolarSheetAnalyzerImpl; -import com.sap.sailing.polars.impl.PolarDataServiceImpl; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; public class PolarSheetAnalyzerTest { @Test - public void testOptimalUpwindSpeedCalculation() { + public void testSpeedAndBearingCalculation() throws NotEnoughDataHasBeenAddedException { + + PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(createMockedPolarDataService()); + BoatClass boatClass = mock(BoatClass.class); + + SpeedWithBearing result = analyzer.getOptimalUpwindSpeedWithBearingOnStarboardTackFor(boatClass, + new KnotSpeedImpl(14)); + assertThat(result.getKnots(), closeTo(8.5, 0.1)); + assertThat(result.getBearing().getDegrees(), closeTo(50, 0.1)); + + SpeedWithBearing result2 = analyzer.getOptimalDownwindSpeedWithBearingOnStarboardTackFor(boatClass, + new KnotSpeedImpl(14)); + assertThat(result2.getKnots(), closeTo(13.8, 0.1)); + assertThat(result2.getBearing().getDegrees(), closeTo(142, 0.1)); + + SpeedWithBearing result3 = analyzer.getOptimalUpwindSpeedWithBearingOnPortTackFor(boatClass, + new KnotSpeedImpl(14)); + assertThat(result3.getKnots(), closeTo(8.5, 0.1)); + assertThat(result3.getBearing().getDegrees(), closeTo(-50, 0.1)); + + SpeedWithBearing result4 = analyzer.getOptimalDownwindSpeedWithBearingOnPortTackFor(boatClass, + new KnotSpeedImpl(14)); + assertThat(result4.getKnots(), closeTo(13.5, 0.1)); + assertThat(result4.getBearing().getDegrees(), closeTo(-144, 0.1)); + + } + + private PolarDataService createMockedPolarDataService() throws NotEnoughDataHasBeenAddedException { + PolarDataService mockedPolarDataService = mock(PolarDataService.class); + PolarSheetsData data = createPolarSheetsData(); + when(mockedPolarDataService.getPolarSheetForBoatClass(any(BoatClass.class))).thenReturn(data); + + //Starboard Upwind + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(49)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.478048671702888), 0.5, 1)); + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(50)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.466303997538812), 0.5, 1)); + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(51)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.583383077026435), 0.5, 1)); + + //Starboard Downwind + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(141)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.794354053931528), 0.5, 1)); + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(142)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.665782579628802), 0.5, 1)); + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(143)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(14.169301888320263), 0.5, 1)); + + //Port Upwind + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(-51)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.445599410456111), 0.5, 1)); + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(-50)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.553274292235153), 0.5, 1)); + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(-49)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.614582090896583), 0.5, 1)); + + //Port Downwind + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(-145)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.78894715705271), 0.5, 1)); + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(-144)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.420656294986587), 0.5, 1)); + when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + argThat(new BearingMatcher(-143)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.268607457651942), 0.5, 1)); + + return mockedPolarDataService; + } + + private PolarSheetsData createPolarSheetsData() { PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); Number[][] averagedPolarDataByWindSpeed = { {}, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.3, 0.4, 0.5, 0.501, 0.502, 0.503, 0.504, 0.505, 0.506, 0.507, - 0.508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, {} }; - PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, - settings.getWindStepping(), null); - PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); - SpeedWithBearing result = analyzer.getOptimalUpwindSpeedWithBearingFor(null, new KnotSpeedImpl(4)); - Assert.assertEquals(0.5 * Math.cos(Math.toRadians(45)), result.getKnots(), 0.01); - Assert.assertEquals(45, result.getBearing().getDegrees(), 0.01); - } - - @Test - public void testOptimalDownwindSpeedCalculation() { - PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); - Number[][] averagedPolarDataByWindSpeed = { {}, - { 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4.5, 4, 3, 2, 1, 0, 0, 0, - 0, 0, 0, 0, 0, 0, 0 }, {} }; - PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null, - settings.getWindStepping(), null); - PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(new MockedPolarDataService(null, data)); - SpeedWithBearing result = analyzer.getOptimalDownwindSpeedWithBearingFor(null, new KnotSpeedImpl(4)); - Assert.assertEquals(4.5 * Math.sin(Math.toRadians(85)), result.getKnots(), 0.01); - Assert.assertEquals(175, result.getBearing().getDegrees(), 0.01); + {}, + {}, + {}, + { 0.0, 0.0, 0.35653425045212994, 0.0, 0.20934280998529808, 0.0, 0.0, 0.0, 0.3145227273331494, 0.0, + 0.5123377765857685, 0.0, 0.0, 0.0, 0.3910305177324864, 0.01848543712702339, 74.14368617329595, + 0.19873208170945428, 1.7877762410964606, 13.813130768721209, 10.642219209509047, + 13.401166322502394, 8.369967315755705, 8.612484953161221, 6.830752063578309, 6.731226410756168, + 7.534989526402173, 7.417304789344726, 7.689655016976699, 7.856820838665987, 7.877814207628512, + 7.686942654891556, 7.94739600490786, 7.250053609589324, 7.658903932314423, 8.264104048379547, + 7.97995749658058, 8.328973177890093, 8.445018706583248, 8.391458379377287, 8.316439838911991, + 8.308509782296186, 8.41538252133265, 8.485027362920015, 8.301702107250204, 8.419623807296604, + 8.624320785824453, 8.459533164762528, 8.563247204808349, 8.478048671702888, 8.466303997538812, + 8.583383077026435, 8.459935549408716, 8.536070928645852, 8.484085598981617, 8.354759109745252, + 8.273057288860901, 8.234006491204791, 7.8843340092591445, 7.832604832601752, 7.752662035724477, + 7.383456897289133, 6.821527524231923, 6.316822983662531, 5.94010223470201, 5.778283414530163, + 5.288261984314158, 5.0632678301953415, 4.575991234971232, 4.4246483551331615, + 3.754104138232388, 3.574202962043522, 2.809782370429744, 2.3756561838866554, + 1.4733065799328493, 1.4254216934512096, 2.3480707715085587, 2.2261678547163744, + 1.3404842722592116, 1.3284863115290033, 1.2125511969957223, 1.6516010763946074, + 1.3858564062376648, 1.206826186297077, 1.3739210408131197, 1.484483916721746, 1.16345069673396, + 1.030671321957646, 1.0099211183312304, 1.128789637547173, 1.3136200715041277, + 1.0889100793268662, 1.619317841041691, 1.3193441080791892, 1.0031422557092482, + 0.7919668067046017, 0.9306316555528391, 0.8006819358007504, 1.4692741310417397, + 1.0078551872416406, 0.18256090691178703, 1.0285132794809515, 0.6438471218131188, + 0.6380303949260124, 0.664014588165121, 0.7808063306582489, 0.7898512748200681, + 0.6511549225287516, 0.6376995913182613, 0.7570886726617079, 0.6619740768653626, + 0.7632028965886186, 0.6338755107017753, 0.6640484201429284, 2.127282972819131, + 2.8987479648532997, 6.683424580288278, 8.507014641722314, 7.703364841210107, 4.71357814053702, + 8.000825810341617, 7.671441013221641, 9.115713955723916, 9.023412736773132, 7.998471846760262, + 10.3966884832781, 9.795531845297319, 8.84626879400292, 10.7522343414125, 12.416303781075975, + 11.722882328824802, 11.955629251917987, 13.323328215681695, 12.902836778810396, + 12.82088099069422, 13.274245192043882, 13.06739796258905, 13.047121734187872, + 13.442188461750433, 13.332939764570671, 13.558632900491695, 13.794354053931528, + 13.665782579628802, 14.169301888320263, 13.974157665863203, 14.222780950415583, + 14.47187020132089, 14.341301813979271, 14.194209653738831, 14.344773681878142, + 13.962146190803251, 14.11383996792033, 14.281588010608605, 14.603553724187172, + 14.762753353002006, 14.123456040381438, 15.046630203667128, 14.39273398895829, + 14.224594624139609, 15.655628777674853, 15.21593321294868, 13.558581393009002, + 11.63031430546637, 11.885139926443486, 8.090192790566025, 8.85949813118447, 9.734779186465921, + 9.822728590064322, 9.45181850091312, 12.306111307584684, 13.943542199831565, + 11.610604465928457, 12.093955433640602, 12.352243834497187, 5.509081709082437, + 9.386182259798517, 11.796573155778503, 10.752292039895153, 11.909890662822576, + 10.116169101754338, 12.694529106484943, 12.670730206147631, 10.379478057323539, + 12.672626215427158, 10.724408451389763, 10.087325562474476, 6.243751559092033, + 10.222427424506762, 10.02529936912795, 11.0353088918245, 11.096075421534366, + 11.682256641020984, 10.803765317446135, 11.21085498256219, 12.986416296517614, + 13.6093151775165, 11.851727180875798, 15.451306097090033, 12.382813246378253, + 14.86900369646574, 14.349497782030753, 13.368463669152609, 14.914498105361215, + 13.627605853093092, 14.511034781204497, 15.204877751897692, 13.56324380463669, + 13.796723457212012, 14.126664346993518, 14.083851682454043, 13.984023085926546, + 13.77727116288626, 13.924407601290493, 14.167888563062107, 13.969213312748776, + 13.78894715705271, 13.420656294986587, 13.268607457651942, 13.107087350500771, + 12.968302178632449, 12.900886466337163, 12.63727656702256, 13.324437322806876, + 12.447444273895949, 11.929720685469128, 11.866720727517666, 11.845857431417869, + 12.34750950745547, 11.82999403657738, 11.56808623971416, 11.005790647087728, + 11.199641554710265, 11.147034585879346, 9.581506313873323, 9.377505334232003, + 9.165927874941573, 9.039110322467469, 9.158860532503148, 8.335083032404867, 7.794601241888998, + 8.095394521682454, 5.288448397940189, 5.032462558577374, 6.0372815329868175, 5.550230684686165, + 5.469127647370254, 7.712573013931298, 6.905684954724151, 6.244400641700857, 4.733792287830745, + 5.703038182173567, 5.705814871750967, 3.6710816576685543, 4.290237091844638, + 3.9287689933642715, 3.8420701003482005, 4.049827829086237, 3.2238964501634513, + 3.153176378342545, 2.7186371105079665, 4.5320344683521725, 6.205007707190399, + 0.9290805079838338, 5.461334397488564, 0.11769821370235434, 4.821232524035565, + 6.535593439138594, 1.430640812116557, 1.6028097505376921, 2.444286329582734, 3.432921232002375, + 1.5524241414822946, 4.463117209069594, 9.233008385665372, 6.701689021468631, 6.091887266547403, + 6.736705820169654, 6.638753611795072, 8.315606347683866, 7.192407235278191, 7.065178905446558, + 7.340556868387216, 7.8140170631317565, 8.490879454652937, 8.869186790970112, 8.089554910312003, + 8.773712136668893, 8.48502143463395, 8.506940480879672, 8.426924203848873, 8.470754959099478, + 8.34341243311524, 8.812850540132228, 8.471103442329234, 8.95942249226152, 8.368125204092756, + 8.101176539059828, 8.536922798672386, 8.039899341914161, 8.66061693085369, 8.522478004005418, + 8.354071503807528, 8.299401288177423, 8.308870759000826, 8.382553283761265, 8.508054693539464, + 8.508341232737465, 8.416280733708618, 8.624730989938895, 8.445599410456111, 8.553274292235153, + 8.614582090896583, 8.619979792865113, 8.497641411226578, 8.59426371914717, 8.638910090156958, + 8.720583873644639, 8.582290408559516, 8.508363936388186, 8.555021199411986, 8.575018051008957, + 8.415002407687826, 8.570989437948075, 8.47499610688996, 8.153991578948714, 8.20568750487526, + 8.16792006664647, 8.035532991444567, 8.374627467796898, 8.53819686502552, 7.998112484617343, + 5.001488152415163, 4.248045472089586, 8.06284784348518, 6.586154602284839, 9.171084517442075, + 9.047519598278601, 8.290140115687464, 6.627513257025532, 9.118867780171255, 0.0, + 0.15031820068279983, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.19624692515939246, 0.0, 0.0, 0.0, + 0.6993196184976416, 0.0, 0.0, 0.0, 0.0, 0.0, 0.37664203710504496 }, {}, {}, {}, {} }; + + Map dataCountsPerAngleAndWindSpeed = new HashMap<>(); + Integer[] dataCountsPerAngle = { 0, 0, 2, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 3, 1, 2, 1, 6, 2, 2, 2, 1, 6, 6, 17, + 13, 19, 30, 43, 32, 50, 48, 57, 70, 76, 107, 129, 177, 186, 222, 237, 275, 285, 321, 322, 401, 451, + 466, 413, 468, 479, 459, 429, 360, 345, 317, 288, 278, 202, 175, 130, 113, 104, 100, 73, 45, 51, 52, + 37, 40, 36, 26, 23, 32, 33, 29, 29, 22, 24, 27, 23, 22, 23, 22, 21, 15, 18, 20, 14, 15, 21, 20, 19, 6, + 12, 10, 9, 10, 8, 1, 9, 6, 6, 14, 16, 7, 7, 6, 2, 5, 3, 6, 5, 8, 7, 14, 14, 12, 9, 20, 14, 22, 28, 14, + 20, 22, 29, 30, 38, 45, 50, 72, 87, 117, 114, 110, 145, 148, 156, 165, 150, 194, 224, 211, 196, 199, + 198, 182, 155, 125, 85, 69, 65, 49, 46, 61, 47, 48, 51, 45, 21, 14, 8, 9, 8, 8, 3, 7, 5, 2, 10, 8, 7, + 7, 10, 8, 13, 13, 11, 5, 9, 9, 8, 15, 6, 3, 5, 11, 9, 12, 5, 13, 12, 18, 15, 31, 25, 35, 36, 39, 59, + 71, 55, 52, 64, 72, 66, 70, 89, 104, 81, 120, 130, 117, 107, 113, 137, 136, 126, 108, 103, 92, 85, 76, + 65, 75, 75, 70, 57, 49, 49, 35, 44, 35, 18, 22, 17, 18, 10, 6, 4, 10, 9, 10, 8, 4, 4, 4, 10, 4, 4, 4, + 9, 8, 8, 7, 7, 6, 4, 7, 4, 1, 6, 6, 4, 6, 5, 5, 5, 6, 5, 9, 7, 12, 16, 10, 25, 25, 30, 29, 47, 44, 31, + 69, 68, 79, 62, 66, 86, 72, 84, 89, 75, 73, 62, 104, 110, 147, 137, 170, 204, 310, 347, 354, 377, 433, + 457, 419, 436, 467, 502, 513, 525, 463, 422, 405, 363, 309, 282, 262, 186, 162, 123, 97, 81, 45, 35, + 30, 24, 20, 12, 9, 8, 5, 6, 4, 6, 7, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1 }; + dataCountsPerAngleAndWindSpeed.put(5, dataCountsPerAngle); + PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 50000, + dataCountsPerAngleAndWindSpeed, settings.getWindStepping(), null); + return data; } + + private class SpeedMatcher extends ArgumentMatcher { + + private final double speedToMatchInKnots; + + public SpeedMatcher(double speedToMatchInKnots) { + this.speedToMatchInKnots = speedToMatchInKnots; + } - private class MockedPolarDataService extends PolarDataServiceImpl { - private PolarSheetsData mockedData; + @Override + public boolean matches(Object argument) { + boolean result = false; + if (argument != null) { + Speed speed = (Speed) argument; + if (speed.getKnots() > speedToMatchInKnots - 0.05 && speed.getKnots() < speedToMatchInKnots + 0.05) { + result = true; + } + } + return result; + } + + } + + private class BearingMatcher extends ArgumentMatcher { - public MockedPolarDataService(Executor executor, PolarSheetsData mockedData) { - super(executor); - this.mockedData = mockedData; + private final double bearingToMatchInDegrees; + + public BearingMatcher(double bearingToMatchInDegrees) { + this.bearingToMatchInDegrees = bearingToMatchInDegrees; } @Override - public PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass) { - return mockedData; + public boolean matches(Object argument) { + boolean result = false; + if (argument != null) { + Bearing bearing = (Bearing) argument; + if (bearing.getDegrees() > bearingToMatchInDegrees - 0.05 + && bearing.getDegrees() < bearingToMatchInDegrees + 0.05) { + result = true; + } + } + return result; } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java index b67524dd82b..75f406d8091 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java @@ -78,11 +78,21 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { double averagedSpeed = averageSpeed(boatClass, windSpeed, estimatedPeak); - Bearing bearing = new DegreeBearingImpl(estimatedPeak); + int convertedAngleIfOver180 = convertAngleIfNecessary(estimatedPeak); + + Bearing bearing = new DegreeBearingImpl(convertedAngleIfOver180); SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(averagedSpeed, bearing); return speedWithBearing; } + private int convertAngleIfNecessary(int estimatedPeak) { + int convertedAngleIfOver180 = estimatedPeak; + if (estimatedPeak > 180) { + convertedAngleIfOver180 = estimatedPeak - 360; + } + return convertedAngleIfOver180; + } + private double averageSpeed(BoatClass boatClass, Speed windSpeed, int estimatedPeak) throws NotEnoughDataHasBeenAddedException { //Having the estimated peak we get the speed by averaging in a small area (3 points) around the peak @@ -92,10 +102,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { for (int i = estimatedPeak - 1; i < estimatedPeak + 2; i++) { try { - int convertedAngleIfOver180 = i; - if (i > 180) { - convertedAngleIfOver180 = i - 360; - } + int convertedAngleIfOver180 = convertAngleIfNecessary(i); SpeedWithConfidence speed = polarDataService.getSpeed(boatClass, windSpeed, new DegreeBearingImpl(convertedAngleIfOver180)); // TODO use the confidence (has to be implemented Bottom to Top through the polar pipe @@ -121,7 +128,8 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { Integer[] dataCountPerAngle = sheet.getDataCountPerAngleForWindspeed(windIndex); // Find peak by averaging the angles that have at least 50% of the max datacount - List dataCountList = Arrays.asList(dataCountPerAngle); + List dataCountList = Arrays.asList(Arrays.copyOfRange(dataCountPerAngle, startAngleInclusive, + endAngleExclusive)); int maxDataCount = Collections.max(dataCountList); if (maxDataCount < MINIMUM_DATA_COUNT_FOR_ONE_ANGLE) { // The angle with the most data points doesn't have sufficient data, for the polar data to have any @@ -131,7 +139,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { int sumOfAllUpperHalfAngles = 0; int numberOfAnglesAdded = 0; for (int i = startAngleInclusive; i < endAngleExclusive; i++) { - if (dataCountList.get(i) > maxDataCount / 2) { + if (dataCountPerAngle[i] > maxDataCount / 2) { sumOfAllUpperHalfAngles = sumOfAllUpperHalfAngles + i; numberOfAnglesAdded = numberOfAnglesAdded + 1; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index 33fb3bfb5aa..ebc34b10461 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -55,8 +55,12 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { public Bearing getAngleToTheWind() { SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForBearing); - Bearing bearing = boatSpeed.getBearing(); - return wind.getFrom().getDifferenceTo(bearing); + Bearing result = null; + if (wind != null && boatSpeed != null) { + Bearing bearing = boatSpeed.getBearing(); + result = wind.getFrom().getDifferenceTo(bearing); + } + return result; } @Override diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index 0a6712fdd0e..1e7a6dfa9a1 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -51,8 +51,11 @@ public class IncrementalRegressionProcessor implements Processor Date: Thu, 6 Nov 2014 22:56:14 +0100 Subject: [PATCH 56/72] Added rest service for average polar api debugging also: -we now use polar data service as a facade to the polar sheets analyzer -Peak is now double instead of int --- .../polars/test/PolarSheetAnalyzerTest.java | 10 ++-- .../sap/sailing/polars/PolarDataService.java | 18 +++++-- .../polars/analysis/PolarSheetAnalyzer.java | 8 +-- .../analysis/impl/PolarSheetAnalyzerImpl.java | 53 +++++++------------ .../polars/impl/PolarDataServiceImpl.java | 30 +++++++++-- .../gateway/jaxrs/api/PolarResource.java | 37 +++++++++++++ 6 files changed, 104 insertions(+), 52 deletions(-) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java index 890faa19a54..923bdfd631c 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -36,22 +36,22 @@ public class PolarSheetAnalyzerTest { PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(createMockedPolarDataService()); BoatClass boatClass = mock(BoatClass.class); - SpeedWithBearing result = analyzer.getOptimalUpwindSpeedWithBearingOnStarboardTackFor(boatClass, + SpeedWithBearing result = analyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, new KnotSpeedImpl(14)); assertThat(result.getKnots(), closeTo(8.5, 0.1)); assertThat(result.getBearing().getDegrees(), closeTo(50, 0.1)); - SpeedWithBearing result2 = analyzer.getOptimalDownwindSpeedWithBearingOnStarboardTackFor(boatClass, + SpeedWithBearing result2 = analyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, new KnotSpeedImpl(14)); - assertThat(result2.getKnots(), closeTo(13.8, 0.1)); + assertThat(result2.getKnots(), closeTo(13.7, 0.1)); assertThat(result2.getBearing().getDegrees(), closeTo(142, 0.1)); - SpeedWithBearing result3 = analyzer.getOptimalUpwindSpeedWithBearingOnPortTackFor(boatClass, + SpeedWithBearing result3 = analyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, new KnotSpeedImpl(14)); assertThat(result3.getKnots(), closeTo(8.5, 0.1)); assertThat(result3.getBearing().getDegrees(), closeTo(-50, 0.1)); - SpeedWithBearing result4 = analyzer.getOptimalDownwindSpeedWithBearingOnPortTackFor(boatClass, + SpeedWithBearing result4 = analyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, new KnotSpeedImpl(14)); assertThat(result4.getKnots(), closeTo(13.5, 0.1)); assertThat(result4.getBearing().getDegrees(), closeTo(-144, 0.1)); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 46dd07f322e..59726ddf439 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -11,6 +11,7 @@ import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; @@ -23,7 +24,7 @@ import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; * @author Frederik Petersen (D054528) * */ -public interface PolarDataService { +public interface PolarDataService extends PolarSheetAnalyzer { /** * @@ -36,6 +37,18 @@ public interface PolarDataService { */ SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) throws NotEnoughDataHasBeenAddedException; + + SpeedWithBearing getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException; + + SpeedWithBearing getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException; + + SpeedWithBearing getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException; + + SpeedWithBearing getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException; /** * Generates a polar sheet for geven races and settings using the provided executor for the worker threads. This @@ -76,9 +89,8 @@ public interface PolarDataService { * {@link PolarDataService#getPolarSheetForBoatClass(BoatClass)} */ Set getAllBoatClassesWithPolarSheetsAvailable(); - - PolarSheetAnalyzer getAnalyzer(); void competitorPositionChanged(GPSFixMoving fix, Competitor competitor, TrackedRace createdTrackedRace); + } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java index 4d7a13e5176..c2046e9778b 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java @@ -7,16 +7,16 @@ import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; public interface PolarSheetAnalyzer { - SpeedWithBearing getOptimalUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, + SpeedWithBearing getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getOptimalDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, + SpeedWithBearing getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getOptimalUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, + SpeedWithBearing getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getOptimalDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, + SpeedWithBearing getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; } \ No newline at end of file diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java index 75f406d8091..787786aa634 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java @@ -32,7 +32,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } @Override - public SpeedWithBearing getOptimalUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearing getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 1; int endAngleExclusive = 90; @@ -43,7 +43,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { @Override - public SpeedWithBearing getOptimalDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearing getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 91; int endAngleExclusive = 180; @@ -53,7 +53,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } @Override - public SpeedWithBearing getOptimalDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearing getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 181; int endAngleExclusive = 270; @@ -63,7 +63,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } @Override - public SpeedWithBearing getOptimalUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearing getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 271; int endAngleExclusive = 360; @@ -74,54 +74,37 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { private SpeedWithBearing estimateAnglePeakAndAverageSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) throws NotEnoughDataHasBeenAddedException { - int estimatedPeak = estimatePeak(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); + double estimatedPeak = estimatePeak(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); + + double convertedAngleIfOver180 = convertAngleIfNecessary(estimatedPeak); + + double averagedSpeed = estimatedSpeed(boatClass, windSpeed, convertedAngleIfOver180); - double averagedSpeed = averageSpeed(boatClass, windSpeed, estimatedPeak); - int convertedAngleIfOver180 = convertAngleIfNecessary(estimatedPeak); Bearing bearing = new DegreeBearingImpl(convertedAngleIfOver180); SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(averagedSpeed, bearing); return speedWithBearing; } - private int convertAngleIfNecessary(int estimatedPeak) { - int convertedAngleIfOver180 = estimatedPeak; + private double convertAngleIfNecessary(double estimatedPeak) { + double convertedAngleIfOver180 = estimatedPeak; if (estimatedPeak > 180) { convertedAngleIfOver180 = estimatedPeak - 360; } return convertedAngleIfOver180; } - private double averageSpeed(BoatClass boatClass, Speed windSpeed, int estimatedPeak) + private double estimatedSpeed(BoatClass boatClass, Speed windSpeed, double estimatedPeak) throws NotEnoughDataHasBeenAddedException { - //Having the estimated peak we get the speed by averaging in a small area (3 points) around the peak - int numberOfSpeedValuesAdded = 0; - double sumOfSpeeds = 0; - int numberOfExceptions = 0; - for (int i = estimatedPeak - 1; i < estimatedPeak + 2; i++) { - try { + SpeedWithConfidence speed = polarDataService.getSpeed(boatClass, windSpeed, new DegreeBearingImpl( + estimatedPeak)); + // TODO use the confidence (has to be implemented Bottom to Top through the polar pipe - int convertedAngleIfOver180 = convertAngleIfNecessary(i); - SpeedWithConfidence speed = polarDataService.getSpeed(boatClass, windSpeed, - new DegreeBearingImpl(convertedAngleIfOver180)); - // TODO use the confidence (has to be implemented Bottom to Top through the polar pipe - sumOfSpeeds = sumOfSpeeds + speed.getObject().getKnots(); - numberOfSpeedValuesAdded = numberOfSpeedValuesAdded + 1; - } catch (NotEnoughDataHasBeenAddedException e) { - if (numberOfExceptions > 0) { - // If more than one query throws error -> Throw error - throw e; - } - numberOfExceptions++; - } - } - - double averagedSpeed = sumOfSpeeds / numberOfSpeedValuesAdded; - return averagedSpeed; + return speed.getObject().getKnots(); } - private int estimatePeak(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) + private double estimatePeak(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) throws NotEnoughDataHasBeenAddedException { PolarSheetsData sheet = polarDataService.getPolarSheetForBoatClass(boatClass); int windIndex = sheet.getStepping().getLevelIndexForValue(windSpeed.getKnots()); @@ -145,7 +128,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } } - int estimatedPeak = sumOfAllUpperHalfAngles / numberOfAnglesAdded; + double estimatedPeak = (double) sumOfAllUpperHalfAngles / (double) numberOfAnglesAdded; return estimatedPeak; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 0a8c4121131..c5a774093ce 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -15,6 +15,7 @@ import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.PolarDataService; @@ -60,6 +61,30 @@ public class PolarDataServiceImpl implements PolarDataService { throws NotEnoughDataHasBeenAddedException { return polarDataMiner.estimateBoatSpeed(boatClass, windSpeed, bearingToTheWind); } + + @Override + public SpeedWithBearing getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException { + return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + } + + @Override + public SpeedWithBearing getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException { + return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + } + + @Override + public SpeedWithBearing getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException { + return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + } + + @Override + public SpeedWithBearing getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + throws NotEnoughDataHasBeenAddedException { + return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + } @Override @@ -119,11 +144,6 @@ public class PolarDataServiceImpl implements PolarDataService { //return polarSheetPerBoatClassCache.keySet(); } - @Override - public PolarSheetAnalyzer getAnalyzer() { - return polarSheetAnalyzer; - } - @Override public void competitorPositionChanged(final GPSFixMoving fix, final Competitor competitor, final TrackedRace createdTrackedRace) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java index cc60e5fc29b..a905844d8cf 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java @@ -13,8 +13,11 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; import com.sap.sailing.server.gateway.serialization.impl.SpeedWithConfidenceWithIntegerRelationJsonSerializer; @@ -43,5 +46,39 @@ public class PolarResource extends AbstractSailingServerResource { return responseBuilder.build(); } + + @GET + @Produces("text/plain;charset=UTF-8") + @Path("average/{boatClassName}") + public Response getAverageSpeedAndBearing(@PathParam("boatClassName") String boatClassName, + @QueryParam("windspeed") double wSpeed, @QueryParam("coursetype") String courseType, @QueryParam("tack") String tack) { + BoatClass boatClass = getService().getDomainObjectFactory().getBaseDomainFactory() + .getOrCreateBoatClass(boatClassName); + ResponseBuilder responseBuilder; + Speed windSpeed = new KnotSpeedImpl(wSpeed); + try { + PolarDataService service = getService().getPolarDataService(); + SpeedWithBearing speedWithBearing = null; + if (tack.matches("starboard")) { + if (courseType.matches("downwind")) { + speedWithBearing = service.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + } else if (courseType.matches("upwind")) { + speedWithBearing = service.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + } + } else if (tack.matches("port")) { + if (courseType.matches("downwind")) { + speedWithBearing = service.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + } else if (courseType.matches("upwind")) { + speedWithBearing = service.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + } + } + String resultString = "Speed: " + speedWithBearing.getKnots() + "kn; Angle: " + speedWithBearing.getBearing().getDegrees() + "°"; + responseBuilder = Response.ok(resultString, MediaType.TEXT_PLAIN); + } catch (NotEnoughDataHasBeenAddedException e) { + responseBuilder = Response.noContent(); + } + + return responseBuilder.build(); + } } From edca95fa938db51f1a67686eb54eb547c111fc5d Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Fri, 7 Nov 2014 09:36:07 +0100 Subject: [PATCH 57/72] fix null-pointer-exception when determining current-leg; --- .../sailing/gwt/ui/client/shared/racemap/RaceMap.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 549919c1654..20a0937999d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -738,7 +738,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe public void onSuccess(RaceMapDataDTO raceMapDataDTO) { if (map != null && raceMapDataDTO != null) { quickRanks = raceMapDataDTO.quickRanks; - if (showViewSimulation) { + if (showViewSimulation && settings.isShowSimulationOverlay()) { simulationOverlay.updateLeg(getCurrentLeg(), newTime, false); } // process response only if not received out of order @@ -2219,7 +2219,12 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe } public int getCurrentLeg() { - return this.getLeadingVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow()).getA(); + com.sap.sse.common.Util.Pair leaderWithLeg = this.getLeadingVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow()); + if (leaderWithLeg == null) { + return 0; + } else { + return leaderWithLeg.getA(); + } } private Image createSAPLogo() { From 110d0f78edee7f8fb21d2d9626dd48e055a6f497 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Fri, 7 Nov 2014 10:12:06 +0100 Subject: [PATCH 58/72] fix handling of out-of-range wind-speeds in polardiagram base-implementation; --- .../simulator/impl/PolarDiagramBase.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java index c02ff30575c..6d080de9a8f 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java @@ -650,16 +650,34 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { windBearing = wind.getBearing().reverse(); estGybeAngleRight = null; estGybeAngleLeft = null; - Bearing floorGybeAngle = gybeAngles.floorEntry(wind).getValue(); - Bearing ceilingGybeAngle = gybeAngles.ceilingEntry(wind).getValue(); + // lookup gybe-angles based on wind-values + Map.Entry floorEntry = gybeAngles.floorEntry(wind); + Bearing floorGybeAngle = null; + if (floorEntry != null) { + floorGybeAngle = floorEntry.getValue(); + } + Map.Entry ceilingEntry = gybeAngles.ceilingEntry(wind); + Bearing ceilingGybeAngle = null; + if (ceilingEntry != null) { + ceilingGybeAngle = ceilingEntry.getValue(); + } + // handle gybe-angles for out-of-definition-range wind-values if (floorGybeAngle == null) { - floorGybeAngle = new DegreeBearingImpl(0); + floorGybeAngle = ceilingGybeAngle; } if (ceilingGybeAngle == null) { - ceilingGybeAngle = new DegreeBearingImpl(0); + ceilingGybeAngle = floorGybeAngle; } + // lookup wind-support-values based on wind-values Speed floorSpeed = gybeAngles.floorKey(wind); Speed ceilingSpeed = gybeAngles.ceilingKey(wind); + // handle out-of-definition-range wind-values + if (floorSpeed == null) { + floorSpeed = ceilingSpeed; + } + if (ceilingSpeed == null) { + ceilingSpeed = floorSpeed; + } double gybeAngle; if (floorSpeed.equals(ceilingSpeed)) { gybeAngle = floorGybeAngle.getRadians(); From 7bf811ce74467e8404055060590e3e2440e33a7d Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 7 Nov 2014 17:52:48 +0100 Subject: [PATCH 59/72] Added confidence to backend polar data aggregation confidence is aggregated by using the confidences of the windspeed, and boatspeed sources (50%) and a confidence calculated using the amount of underlying fixes (50%) --- .../mining/test/PolarDataMinerTest.java | 11 ++- .../polars/test/PolarSheetAnalyzerTest.java | 60 +++++++------- .../sap/sailing/polars/PolarDataService.java | 12 +-- .../polars/analysis/PolarSheetAnalyzer.java | 10 +-- .../analysis/impl/PolarSheetAnalyzerImpl.java | 82 +++++++++++++------ .../polars/impl/PolarDataServiceImpl.java | 12 +-- .../mining/GPSFixMovingWithPolarContext.java | 35 +++++--- .../IncrementalRegressionProcessor.java | 29 +++++-- .../sailing/polars/mining/PolarDataMiner.java | 11 ++- .../polars/regression/BoatSpeedEstimator.java | 14 +++- .../NotEnoughDataHasBeenAddedException.java | 4 + .../gateway/jaxrs/api/PolarResource.java | 28 ++++--- 12 files changed, 199 insertions(+), 109 deletions(-) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java index ba11401af12..158fc4ebe8e 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/mining/test/PolarDataMinerTest.java @@ -39,10 +39,13 @@ import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl; import com.sap.sailing.domain.tracking.impl.WindImpl; +import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl; import com.sap.sailing.polars.mining.PolarDataMiner; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; +import com.sap.sse.common.Util.Pair; public class PolarDataMinerTest { @@ -103,17 +106,17 @@ public class PolarDataMinerTest { } } - SpeedWithConfidence estimatedSpeed1 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), + SpeedWithConfidence estimatedSpeed1 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), new DegreeBearingImpl(44.9)); assertThat(estimatedSpeed1, is(notNullValue())); assertThat(estimatedSpeed1.getObject().getKnots(), is(closeTo(10.55, EPSILON))); - SpeedWithConfidence estimatedSpeed2 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), + SpeedWithConfidence estimatedSpeed2 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), new DegreeBearingImpl(42)); assertThat(estimatedSpeed2, is(notNullValue())); assertThat(estimatedSpeed2.getObject().getKnots(), is(closeTo(10.05, EPSILON))); - SpeedWithConfidence estimatedSpeed3 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), + SpeedWithConfidence estimatedSpeed3 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), new DegreeBearingImpl(42.8)); assertThat(estimatedSpeed3, is(notNullValue())); assertThat(estimatedSpeed3.getObject().getKnots(), is(closeTo(10, EPSILON))); @@ -169,9 +172,11 @@ public class PolarDataMinerTest { SpeedWithBearing windSpeed = new KnotSpeedWithBearingImpl(15, windBearing); Wind wind = new WindImpl(new DegreePosition(54.431952, 10.186767), startOfRace, windSpeed); + WindWithConfidence> windWithConfidence = new WindWithConfidenceImpl<>(wind, 0.5, null, false); // Always return same wind when(trackedRace.getWind(any(Position.class), any(TimePoint.class))).thenReturn(wind); when(trackedRace.getWind(any(Position.class), any(TimePoint.class), any())).thenReturn(wind); + when(trackedRace.getWindWithConfidence(any(Position.class), any(TimePoint.class), any())).thenReturn(windWithConfidence); return trackedRace; } diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java index 923bdfd631c..4d4cc7b279b 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -14,12 +14,12 @@ import org.junit.Test; import org.mockito.ArgumentMatcher; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; @@ -36,25 +36,29 @@ public class PolarSheetAnalyzerTest { PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(createMockedPolarDataService()); BoatClass boatClass = mock(BoatClass.class); - SpeedWithBearing result = analyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, + SpeedWithBearingWithConfidence result = analyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, new KnotSpeedImpl(14)); - assertThat(result.getKnots(), closeTo(8.5, 0.1)); - assertThat(result.getBearing().getDegrees(), closeTo(50, 0.1)); + assertThat(result.getObject().getKnots(), closeTo(8.5, 0.1)); + assertThat(result.getObject().getBearing().getDegrees(), closeTo(50, 0.1)); + assertThat(result.getConfidence(), closeTo(0.7, 0.1)); - SpeedWithBearing result2 = analyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, + SpeedWithBearingWithConfidence result2 = analyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, new KnotSpeedImpl(14)); - assertThat(result2.getKnots(), closeTo(13.7, 0.1)); - assertThat(result2.getBearing().getDegrees(), closeTo(142, 0.1)); + assertThat(result2.getObject().getKnots(), closeTo(14.2, 0.1)); + assertThat(result2.getObject().getBearing().getDegrees(), closeTo(142.8, 0.1)); + assertThat(result2.getConfidence(), closeTo(0.6, 0.1)); - SpeedWithBearing result3 = analyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, + SpeedWithBearingWithConfidence result3 = analyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, new KnotSpeedImpl(14)); - assertThat(result3.getKnots(), closeTo(8.5, 0.1)); - assertThat(result3.getBearing().getDegrees(), closeTo(-50, 0.1)); + assertThat(result3.getObject().getKnots(), closeTo(8.6, 0.1)); + assertThat(result3.getObject().getBearing().getDegrees(), closeTo(-49.5, 0.1)); + assertThat(result3.getConfidence(), closeTo(0.7, 0.1)); - SpeedWithBearing result4 = analyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, + SpeedWithBearingWithConfidence result4 = analyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, new KnotSpeedImpl(14)); - assertThat(result4.getKnots(), closeTo(13.5, 0.1)); - assertThat(result4.getBearing().getDegrees(), closeTo(-144, 0.1)); + assertThat(result4.getObject().getKnots(), closeTo(13.5, 0.1)); + assertThat(result4.getObject().getBearing().getDegrees(), closeTo(-143.6, 0.1)); + assertThat(result4.getConfidence(), closeTo(0.25, 0.1)); } @@ -66,46 +70,46 @@ public class PolarSheetAnalyzerTest { //Starboard Upwind when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(49)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.478048671702888), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.478048671702888), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(50)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.466303997538812), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.466303997538812), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(51)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.583383077026435), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.583383077026435), 0.5, null)); //Starboard Downwind when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(141)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.794354053931528), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.794354053931528), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(142)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.665782579628802), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.665782579628802), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(143)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(14.169301888320263), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(14.169301888320263), 0.5, null)); //Port Upwind when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(-51)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.445599410456111), 0.5, 1)); + argThat(new BearingMatcher(-48)))).thenReturn( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.445599410456111), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(-50)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.553274292235153), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.553274292235153), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(-49)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.614582090896583), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.614582090896583), 0.5, null)); //Port Downwind when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(-145)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.78894715705271), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.78894715705271), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(-144)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.420656294986587), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.420656294986587), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(-143)))).thenReturn( - new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.268607457651942), 0.5, 1)); + new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.268607457651942), 0.5, null)); return mockedPolarDataService; } @@ -253,8 +257,8 @@ public class PolarSheetAnalyzerTest { boolean result = false; if (argument != null) { Bearing bearing = (Bearing) argument; - if (bearing.getDegrees() > bearingToMatchInDegrees - 0.05 - && bearing.getDegrees() < bearingToMatchInDegrees + 0.05) { + if (bearing.getDegrees() > bearingToMatchInDegrees - 0.4999999 + && bearing.getDegrees() < bearingToMatchInDegrees + 0.49999999) { result = true; } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 59726ddf439..81c4007738b 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -6,12 +6,12 @@ import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; +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.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; @@ -35,19 +35,19 @@ public interface PolarDataService extends PolarSheetAnalyzer { * @return The speed the boat is moving at for the specified wind and bearing according to the polar diagram. * @throws NotEnoughDataHasBeenAddedException */ - SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) + SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; /** diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java index c2046e9778b..70a6ae0bddc 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/PolarSheetAnalyzer.java @@ -1,22 +1,22 @@ package com.sap.sailing.polars.analysis; import com.sap.sailing.domain.base.BoatClass; +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.polars.regression.NotEnoughDataHasBeenAddedException; public interface PolarSheetAnalyzer { - SpeedWithBearing getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, + SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, + SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, + SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; - SpeedWithBearing getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, + SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; } \ No newline at end of file diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java index 787786aa634..a12273fd7ab 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java @@ -5,7 +5,9 @@ import java.util.Collections; import java.util.List; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; @@ -24,7 +26,7 @@ import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; */ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { - private static final int MINIMUM_DATA_COUNT_FOR_ONE_ANGLE = 10; + private static final int MINIMUM_DATA_COUNT_FOR_ONE_ANGLE = 5; private PolarDataService polarDataService; public PolarSheetAnalyzerImpl(PolarDataService polarDataService) { @@ -32,59 +34,88 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } @Override - public SpeedWithBearing getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 1; int endAngleExclusive = 90; - SpeedWithBearing speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, + SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); return speedWithBearing; } @Override - public SpeedWithBearing getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 91; int endAngleExclusive = 180; - SpeedWithBearing speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, + SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); return speedWithBearing; } @Override - public SpeedWithBearing getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 181; int endAngleExclusive = 270; - SpeedWithBearing speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, + SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); return speedWithBearing; } @Override - public SpeedWithBearing getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 271; int endAngleExclusive = 360; - SpeedWithBearing speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, + SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); return speedWithBearing; } - private SpeedWithBearing estimateAnglePeakAndAverageSpeed(BoatClass boatClass, Speed windSpeed, + private SpeedWithBearingWithConfidence estimateAnglePeakAndAverageSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) throws NotEnoughDataHasBeenAddedException { - double estimatedPeak = estimatePeak(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); + Integer[] dataCountPerAngle = getDataCountArray(boatClass, windSpeed); + + + + double estimatedPeak = estimatePeak(boatClass, windSpeed, startAngleInclusive, endAngleExclusive, dataCountPerAngle); double convertedAngleIfOver180 = convertAngleIfNecessary(estimatedPeak); - double averagedSpeed = estimatedSpeed(boatClass, windSpeed, convertedAngleIfOver180); + SpeedWithConfidence averagedSpeedWithConfidence = estimatedSpeed(boatClass, windSpeed, convertedAngleIfOver180); + double originConfidence = averagedSpeedWithConfidence.getConfidence(); + double overallConfidence = (originConfidence + calcDataCountConfidence(dataCountPerAngle, estimatedPeak)) / 2; Bearing bearing = new DegreeBearingImpl(convertedAngleIfOver180); - SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(averagedSpeed, bearing); - return speedWithBearing; + SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(averagedSpeedWithConfidence.getObject().getKnots(), bearing); + return new SpeedWithBearingWithConfidenceImpl<>(speedWithBearing, overallConfidence, null); + } + + /** + * Uses the formula 1-​e^​((‑x)/​150) to create a confidence for the estimated peak. + * + * If the peak has 100 data points underlying, the confidence will be approx. 0.5 + * + * @param dataCountPerAngle + * @param estimatedPeak + * @return + */ + private double calcDataCountConfidence(Integer[] dataCountPerAngle, double estimatedPeak) { + int dataCount = dataCountPerAngle[(int) Math.round(estimatedPeak)]; + double confidence = 1 - Math.pow(Math.E, - dataCount / 150); + return confidence; + } + + private Integer[] getDataCountArray(BoatClass boatClass, Speed windSpeed) { + PolarSheetsData sheet = polarDataService.getPolarSheetForBoatClass(boatClass); + + int windIndex = sheet.getStepping().getLevelIndexForValue(windSpeed.getKnots()); + Integer[] dataCountPerAngle = sheet.getDataCountPerAngleForWindspeed(windIndex); + return dataCountPerAngle; } private double convertAngleIfNecessary(double estimatedPeak) { @@ -95,20 +126,15 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { return convertedAngleIfOver180; } - private double estimatedSpeed(BoatClass boatClass, Speed windSpeed, double estimatedPeak) + private SpeedWithConfidence estimatedSpeed(BoatClass boatClass, Speed windSpeed, double estimatedPeak) throws NotEnoughDataHasBeenAddedException { - SpeedWithConfidence speed = polarDataService.getSpeed(boatClass, windSpeed, new DegreeBearingImpl( + SpeedWithConfidence speed = polarDataService.getSpeed(boatClass, windSpeed, new DegreeBearingImpl( estimatedPeak)); - // TODO use the confidence (has to be implemented Bottom to Top through the polar pipe - - return speed.getObject().getKnots(); + return speed; } - private double estimatePeak(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) + private double estimatePeak(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive, Integer[] dataCountPerAngle) throws NotEnoughDataHasBeenAddedException { - PolarSheetsData sheet = polarDataService.getPolarSheetForBoatClass(boatClass); - int windIndex = sheet.getStepping().getLevelIndexForValue(windSpeed.getKnots()); - Integer[] dataCountPerAngle = sheet.getDataCountPerAngleForWindspeed(windIndex); // Find peak by averaging the angles that have at least 50% of the max datacount List dataCountList = Arrays.asList(Arrays.copyOfRange(dataCountPerAngle, startAngleInclusive, @@ -117,14 +143,16 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { if (maxDataCount < MINIMUM_DATA_COUNT_FOR_ONE_ANGLE) { // The angle with the most data points doesn't have sufficient data, for the polar data to have any // significance. - throw new NotEnoughDataHasBeenAddedException(); + throw new NotEnoughDataHasBeenAddedException("Only " + maxDataCount + + " points have been added for the angle with the most points. No significance."); } int sumOfAllUpperHalfAngles = 0; int numberOfAnglesAdded = 0; for (int i = startAngleInclusive; i < endAngleExclusive; i++) { - if (dataCountPerAngle[i] > maxDataCount / 2) { - sumOfAllUpperHalfAngles = sumOfAllUpperHalfAngles + i; - numberOfAnglesAdded = numberOfAnglesAdded + 1; + Integer dataCount = dataCountPerAngle[i]; + if (dataCount > maxDataCount / 2) { + sumOfAllUpperHalfAngles = sumOfAllUpperHalfAngles + i * dataCount; + numberOfAnglesAdded = numberOfAnglesAdded + dataCount; } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index c5a774093ce..da1288efbaa 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -9,13 +9,13 @@ import java.util.concurrent.Executor; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; +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.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.PolarDataService; @@ -57,31 +57,31 @@ public class PolarDataServiceImpl implements PolarDataService { } @Override - public SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) + public SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) throws NotEnoughDataHasBeenAddedException { return polarDataMiner.estimateBoatSpeed(boatClass, windSpeed, bearingToTheWind); } @Override - public SpeedWithBearing getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); } @Override - public SpeedWithBearing getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); } @Override - public SpeedWithBearing getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); } @Override - public SpeedWithBearing getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java index ebc34b10461..91834b7fbc6 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/GPSFixMovingWithPolarContext.java @@ -5,14 +5,22 @@ import java.util.Set; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.common.Bearing; +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.TimePoint; import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.WindSourceType; +import com.sap.sailing.domain.common.confidence.BearingWithConfidence; +import com.sap.sailing.domain.common.confidence.ConfidenceFactory; +import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl; import com.sap.sailing.domain.tracking.GPSFixMoving; +import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sailing.domain.tracking.Wind; +import com.sap.sailing.domain.tracking.WindWithConfidence; +import com.sap.sse.common.Util.Pair; import com.sap.sse.datamining.data.Cluster; import com.sap.sse.datamining.data.ClusterGroup; @@ -49,33 +57,38 @@ public class GPSFixMovingWithPolarContext implements PolarClusterKey { @Override public RoundedAngleToTheWind getRoundedAngleToTheWind() { - return new RoundedAngleToTheWind(getAngleToTheWind()); + return new RoundedAngleToTheWind(getAngleToTheWind().getObject()); } - public Bearing getAngleToTheWind() { + public BearingWithConfidence getAngleToTheWind() { SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); - Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForBearing); - Bearing result = null; + WindWithConfidence> wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForBearing); + + BearingWithConfidenceImpl result = null; if (wind != null && boatSpeed != null) { Bearing bearing = boatSpeed.getBearing(); - result = wind.getFrom().getDifferenceTo(bearing); + Bearing difference = wind.getObject().getFrom().getDifferenceTo(bearing); + result = new BearingWithConfidenceImpl(difference, wind.getConfidence(), 0); } return result; } @Override public Cluster getWindSpeedCluster() { - Speed windSpeed = getWindSpeed(); + Speed windSpeed = getWindSpeed().getObject(); return windSpeedClusterGroup.getClusterFor(windSpeed); } - public Speed getWindSpeed() { - Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForSpeed); + public WindWithConfidence> getWindSpeed() { + WindWithConfidence> wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForSpeed); return wind; } - public Speed getBoatSpeed() { - return race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); + public SpeedWithBearingWithConfidence getBoatSpeed() { + GPSFixTrack track = race.getTrack(competitor); + return track.getEstimatedSpeed(fix.getTimePoint(), ConfidenceFactory.INSTANCE.createExponentialTimeDifferenceWeigher( + // use a minimum confidence to avoid the bearing to flip to 270deg in case all is zero + track.getMillisecondsOverWhichToAverageSpeed()/2, /* minimumConfidence */ 0.00000001)); } @Override diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java index 1e7a6dfa9a1..646f1e54359 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/IncrementalRegressionProcessor.java @@ -7,11 +7,16 @@ import java.util.Set; import java.util.logging.Logger; import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.TimePoint; +import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; +import com.sap.sailing.domain.tracking.WindWithConfidence; import com.sap.sailing.polars.regression.BoatSpeedEstimator; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sse.common.Util.Pair; @@ -51,10 +56,20 @@ public class IncrementalRegressionProcessor implements Processor angleToTheWind = fix.getAngleToTheWind(); + + + + if (angleToTheWind != null) { - boatSpeedEstimator.addData(fix.getWindSpeed().getKnots(), angleToTheWind.getDegrees(), fix.getBoatSpeed() - .getKnots()); + WindWithConfidence> windWithConfidenceForSpeed = fix.getWindSpeed(); + SpeedWithBearingWithConfidence boatSpeedWithConfidence = fix.getBoatSpeed(); + double confidenceForWindSpeed = windWithConfidenceForSpeed.getConfidence(); + double confidenceForWindBearing = angleToTheWind.getConfidence(); + double confidenceForBoatSpeed = boatSpeedWithConfidence.getConfidence(); + double averagedConfidence = (confidenceForBoatSpeed + confidenceForWindBearing + confidenceForWindSpeed) / 3; + boatSpeedEstimator.addData(windWithConfidenceForSpeed.getObject().getKnots(), angleToTheWind.getObject() + .getDegrees(), boatSpeedWithConfidence.getObject().getKnots(), averagedConfidence); } } @@ -74,7 +89,7 @@ public class IncrementalRegressionProcessor implements Processor, Integer> estimateBoatSpeed(final BoatClass boatClass, final Speed windSpeed, + public Pair, Integer> estimateBoatSpeed(final BoatClass boatClass, final Speed windSpeed, final Bearing angleToTheWind) throws NotEnoughDataHasBeenAddedException { int dataCount = 0; @@ -109,8 +124,9 @@ public class IncrementalRegressionProcessor implements Processor, Integer>(new SpeedWithConfidenceImpl( - speedWithoutConfidence, /* FIXME */1.0, /* FIXME */0), dataCount); + double confidence = boatSpeedEstimator.getConfidence(); + return new Pair, Integer>(new SpeedWithConfidenceImpl( + speedWithoutConfidence, confidence, null), dataCount); } @Override @@ -131,6 +147,7 @@ public class IncrementalRegressionProcessor implements Processor getResultType() { + // No result type here, since this is a special case of a processor. It's the end of the pipe so to say. return null; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index cf7f3554f8d..464312dc0ec 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -108,7 +108,7 @@ public class PolarDataMiner { return isActive || hasQueue; } - public SpeedWithConfidence estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind) + public SpeedWithConfidence estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind) throws NotEnoughDataHasBeenAddedException { return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind).getA(); } @@ -130,15 +130,18 @@ public class PolarDataMiner { Integer[] perAngle = new Integer[360]; Map perWindSpeed = new HashMap<>(); for (int angle = 0; angle < 360; angle++) { - Pair, Integer> speedWithConfidenceAndDataCount; + Pair, Integer> speedWithConfidenceAndDataCount; try { int convertedAngle = angle; if (angle > 180) { convertedAngle = angle - 360; } - speedWithConfidenceAndDataCount = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); + speedWithConfidenceAndDataCount = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, + new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); } catch (NotEnoughDataHasBeenAddedException e) { - speedWithConfidenceAndDataCount = new Pair, Integer>(new SpeedWithConfidenceImpl(new KnotSpeedImpl(0), 0, 0), 0); + // No data so put in a 0 speed with 0 confidence + speedWithConfidenceAndDataCount = new Pair, Integer>( + new SpeedWithConfidenceImpl(new KnotSpeedImpl(0), 0, null), 0); } averagedPolarDataByWindSpeed[windIndex][angle] = speedWithConfidenceAndDataCount.getA().getObject().getKnots(); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java index 93339d7bd42..8c90aa9355b 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/BoatSpeedEstimator.java @@ -26,6 +26,10 @@ public class BoatSpeedEstimator { private ReentrantReadWriteLock lock = new ReentrantReadWriteLock(true); + private double confidenceSum = 0; + + private int dataCount = 0; + public double estimateSpeed(double windSpeed, double angleToTheWind) throws NotEnoughDataHasBeenAddedException { lock.readLock().lock(); try { @@ -37,18 +41,24 @@ public class BoatSpeedEstimator { } } - public void addData(double windSpeed, double angleToTheWind, double boatSpeed) { + public void addData(double windSpeed, double angleToTheWind, double boatSpeed, double confidence) { lock.writeLock().lock(); try { angleRegression.addMeasuredPoint(angleToTheWind, boatSpeed); windSpeedRegression.addMeasuredPoint(windSpeed, boatSpeed); + dataCount++; + confidenceSum = confidenceSum + confidence; } finally { lock.writeLock().unlock(); } } public int getDataCount() { - return windSpeedRegression.getDataCount(); + return dataCount; + } + + public double getConfidence() { + return confidenceSum / dataCount; } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NotEnoughDataHasBeenAddedException.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NotEnoughDataHasBeenAddedException.java index f6a60d87135..d15ad5ac066 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NotEnoughDataHasBeenAddedException.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/regression/NotEnoughDataHasBeenAddedException.java @@ -8,4 +8,8 @@ public class NotEnoughDataHasBeenAddedException extends Exception { super("No data has been added to the Processor yet. At least to value-pairs have to be added."); } + public NotEnoughDataHasBeenAddedException(String string) { + super(string); + } + } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java index a905844d8cf..894b79deea7 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java @@ -9,37 +9,41 @@ import javax.ws.rs.core.MediaType; import javax.ws.rs.core.Response; import javax.ws.rs.core.Response.ResponseBuilder; -import org.json.simple.JSONObject; - import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource; -import com.sap.sailing.server.gateway.serialization.impl.SpeedWithConfidenceWithIntegerRelationJsonSerializer; +/** + * Right now this service is only used for quick debugging and testing of the polar api. It used plain text responses. + * JSON should be used if this resource is to be consumed. + * + * @author Frederik Petersen + * + */ @Path("/v1/polars") public class PolarResource extends AbstractSailingServerResource { @GET - @Produces("application/json;charset=UTF-8") + @Produces("text/plain;charset=UTF-8") @Path("{boatClassName}") public Response getSpeed(@PathParam("boatClassName") String boatClassName, @QueryParam("angle") double angle, @QueryParam("windspeed") double windSpeed) { BoatClass boatClass = getService().getDomainObjectFactory().getBaseDomainFactory() .getOrCreateBoatClass(boatClassName); - SpeedWithConfidence speedWithConfidence; + SpeedWithConfidence speedWithConfidence; ResponseBuilder responseBuilder; try { speedWithConfidence = getService().getPolarDataService().getSpeed(boatClass, new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(angle)); - SpeedWithConfidenceWithIntegerRelationJsonSerializer serializer = new SpeedWithConfidenceWithIntegerRelationJsonSerializer(); - JSONObject jsonObj = serializer.serialize(speedWithConfidence); - responseBuilder = Response.ok(jsonObj.toJSONString(), MediaType.APPLICATION_JSON); + String resultString = "Speed: " + speedWithConfidence.getObject().getKnots() + "kn; Confidence: " + + speedWithConfidence.getConfidence(); + responseBuilder = Response.ok(resultString, MediaType.TEXT_PLAIN); } catch (NotEnoughDataHasBeenAddedException e) { responseBuilder = Response.noContent(); } @@ -58,7 +62,7 @@ public class PolarResource extends AbstractSailingServerResource { Speed windSpeed = new KnotSpeedImpl(wSpeed); try { PolarDataService service = getService().getPolarDataService(); - SpeedWithBearing speedWithBearing = null; + SpeedWithBearingWithConfidence speedWithBearing = null; if (tack.matches("starboard")) { if (courseType.matches("downwind")) { speedWithBearing = service.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); @@ -72,7 +76,9 @@ public class PolarResource extends AbstractSailingServerResource { speedWithBearing = service.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); } } - String resultString = "Speed: " + speedWithBearing.getKnots() + "kn; Angle: " + speedWithBearing.getBearing().getDegrees() + "°"; + String resultString = "Speed: " + speedWithBearing.getObject().getKnots() + "kn; Angle: " + + speedWithBearing.getObject().getBearing().getDegrees() + "°; Confidence: " + + speedWithBearing.getConfidence(); responseBuilder = Response.ok(resultString, MediaType.TEXT_PLAIN); } catch (NotEnoughDataHasBeenAddedException e) { responseBuilder = Response.noContent(); From 86b597a76be6a6d32a4224830629950373e288c7 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 7 Nov 2014 18:24:27 +0100 Subject: [PATCH 60/72] Added comments and a central method for average speed with bearing --- .../polars/test/PolarSheetAnalyzerTest.java | 8 +++ .../sap/sailing/polars/PolarDataService.java | 49 ++++++++++++++++++- .../polars/impl/PolarDataServiceImpl.java | 22 +++++++++ .../gateway/jaxrs/api/PolarResource.java | 20 ++------ 4 files changed, 83 insertions(+), 16 deletions(-) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java index 4d4cc7b279b..d9be2309eff 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -114,6 +114,14 @@ public class PolarSheetAnalyzerTest { return mockedPolarDataService; } + /** + * The following data is taken from a real race. (49ER yellow R2, KW 2014) + * Only the data for windspeed 14kn is used. The first block contains the + * boatspeeds for every angle to the wind. The second block contains the + * datacount for each angle (number of underlying fixes). + * + * @return + */ private PolarSheetsData createPolarSheetsData() { PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); Number[][] averagedPolarDataByWindSpeed = { diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 81c4007738b..bf84034740d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -9,9 +9,11 @@ import com.sap.sailing.domain.base.Competitor; 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.LegType; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; @@ -19,7 +21,14 @@ import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; /** - * Public Facade interface allowing access to the polars of {@link TrackedRace}s and per {@link BoatClass}. + * Public Facade interface allowing access to the polars of {@link BoatClass}es. + * + * It uses a {@link PolarSheetAnalyzer} for more advanced analysis. It's methods are facaded in this interface for + * central access. + * + * The interesting methods for a user are {@link #getSpeed(BoatClass, Speed, Bearing)} if data for a specific angle is + * needed and {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} (and the equivalents for + * different tacks and leg types) which also returns the average angle for the provided parameters. * * @author Frederik Petersen (D054528) * @@ -38,15 +47,53 @@ public interface PolarDataService extends PolarSheetAnalyzer { SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) throws NotEnoughDataHasBeenAddedException; + /** + * + * @param boatClass + * @param windSpeed + * @param legType + * Should be UpWind or DownWind, there is no information for other courses yet. Use getSpeed for the + * desired angle to get rawer information on other courses for now. + * @param tack + * Polar data can vary depending on the tack the boat is on. + * @return The estimated average speed of a boat for the supplied parameters with the estimated average bearing to + * the wind and a confidence which consists of the confidences of the wind speed, and boat speed sources (50%) + * and a confidence calculated using the amount of underlying fixes (50%). 0 <= confidence < 1
+ * A value with zero confidence doesn't have any significance!

+ * + * The bearing is somewhere between -179 to +180

+ * + * Get the speed using returnValue.getObject()

+ * + * Returns null if the leg type is not up or downwind. + * + * @throws NotEnoughDataHasBeenAddedException + * If there is not enough data to supply a value with some kind of significance. + */ + SpeedWithBearingWithConfidence getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed, + LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException; + + /** + * see {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} + */ SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; + /** + * see {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} + */ SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; + /** + * see {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} + */ SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; + /** + * see {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} + */ SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index da1288efbaa..de339bc7fce 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -12,10 +12,12 @@ import com.sap.sailing.domain.base.Competitor; 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.LegType; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.PolarDataService; @@ -62,6 +64,26 @@ public class PolarDataServiceImpl implements PolarDataService { return polarDataMiner.estimateBoatSpeed(boatClass, windSpeed, bearingToTheWind); } + @Override + public SpeedWithBearingWithConfidence getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed, + LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { + SpeedWithBearingWithConfidence speedWithBearing = null; + if (tack.equals(Tack.STARBOARD)) { + if (legType.equals(LegType.UPWIND)) { + speedWithBearing = getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + } else if (legType.equals(LegType.DOWNWIND)) { + speedWithBearing = getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + } + } else if (tack.equals(Tack.PORT)) { + if (legType.equals(LegType.UPWIND)) { + speedWithBearing = getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + } else if (legType.equals(LegType.DOWNWIND)) { + speedWithBearing = getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + } + } + return speedWithBearing; + } + @Override public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) throws NotEnoughDataHasBeenAddedException { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java index 894b79deea7..c11319d9ffb 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java @@ -12,7 +12,9 @@ import javax.ws.rs.core.Response.ResponseBuilder; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithConfidence; +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.polars.PolarDataService; @@ -55,27 +57,15 @@ public class PolarResource extends AbstractSailingServerResource { @Produces("text/plain;charset=UTF-8") @Path("average/{boatClassName}") public Response getAverageSpeedAndBearing(@PathParam("boatClassName") String boatClassName, - @QueryParam("windspeed") double wSpeed, @QueryParam("coursetype") String courseType, @QueryParam("tack") String tack) { + @QueryParam("windspeed") double wSpeed, @QueryParam("legtype") LegType legType, @QueryParam("tack") Tack tack) { BoatClass boatClass = getService().getDomainObjectFactory().getBaseDomainFactory() .getOrCreateBoatClass(boatClassName); ResponseBuilder responseBuilder; Speed windSpeed = new KnotSpeedImpl(wSpeed); try { PolarDataService service = getService().getPolarDataService(); - SpeedWithBearingWithConfidence speedWithBearing = null; - if (tack.matches("starboard")) { - if (courseType.matches("downwind")) { - speedWithBearing = service.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); - } else if (courseType.matches("upwind")) { - speedWithBearing = service.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); - } - } else if (tack.matches("port")) { - if (courseType.matches("downwind")) { - speedWithBearing = service.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); - } else if (courseType.matches("upwind")) { - speedWithBearing = service.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); - } - } + SpeedWithBearingWithConfidence speedWithBearing = service.getAverageSpeedWithBearing(boatClass, + windSpeed, legType, tack); String resultString = "Speed: " + speedWithBearing.getObject().getKnots() + "kn; Angle: " + speedWithBearing.getObject().getBearing().getDegrees() + "°; Confidence: " + speedWithBearing.getConfidence(); From 2412fd4943ae49a3e60a996ceb89cd9edbad0783 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 7 Nov 2014 19:04:13 +0100 Subject: [PATCH 61/72] Removed redundant methods in polar data service --- .../sap/sailing/polars/PolarDataService.java | 29 ++--------------- .../polars/impl/PolarDataServiceImpl.java | 32 +++---------------- 2 files changed, 7 insertions(+), 54 deletions(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index bf84034740d..cc9251f78cd 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -27,13 +27,13 @@ import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; * central access. * * The interesting methods for a user are {@link #getSpeed(BoatClass, Speed, Bearing)} if data for a specific angle is - * needed and {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} (and the equivalents for - * different tacks and leg types) which also returns the average angle for the provided parameters. + * needed and {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} + * which also returns the average angle for the provided parameters. * * @author Frederik Petersen (D054528) * */ -public interface PolarDataService extends PolarSheetAnalyzer { +public interface PolarDataService { /** * @@ -73,29 +73,6 @@ public interface PolarDataService extends PolarSheetAnalyzer { SpeedWithBearingWithConfidence getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException; - /** - * see {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} - */ - SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) - throws NotEnoughDataHasBeenAddedException; - - /** - * see {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} - */ - SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) - throws NotEnoughDataHasBeenAddedException; - - /** - * see {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} - */ - SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) - throws NotEnoughDataHasBeenAddedException; - - /** - * see {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} - */ - SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) - throws NotEnoughDataHasBeenAddedException; /** * Generates a polar sheet for geven races and settings using the provided executor for the worker threads. This diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index de339bc7fce..198b648345c 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -70,43 +70,19 @@ public class PolarDataServiceImpl implements PolarDataService { SpeedWithBearingWithConfidence speedWithBearing = null; if (tack.equals(Tack.STARBOARD)) { if (legType.equals(LegType.UPWIND)) { - speedWithBearing = getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + speedWithBearing = polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); } else if (legType.equals(LegType.DOWNWIND)) { - speedWithBearing = getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + speedWithBearing = polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); } } else if (tack.equals(Tack.PORT)) { if (legType.equals(LegType.UPWIND)) { - speedWithBearing = getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); } else if (legType.equals(LegType.DOWNWIND)) { - speedWithBearing = getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); } } return speedWithBearing; } - - @Override - public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) - throws NotEnoughDataHasBeenAddedException { - return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); - } - - @Override - public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) - throws NotEnoughDataHasBeenAddedException { - return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); - } - - @Override - public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) - throws NotEnoughDataHasBeenAddedException { - return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); - } - - @Override - public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) - throws NotEnoughDataHasBeenAddedException { - return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); - } @Override From 98e3554f1226ec00cc2d705a6c5fd567e52f671b Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Mon, 10 Nov 2014 11:54:40 +0100 Subject: [PATCH 62/72] add equitemporal markers to paths from simulation; --- .../sailing/gwt/ui/server/SailingServiceImpl.java | 4 ++-- .../gwt/ui/server/SimulatorServiceImpl.java | 2 +- .../gwt/ui/shared/SimulatorResultsDTO.java | 13 ++++++++++++- .../ui/shared/racemap/RaceSimulationOverlay.java | 15 ++++++++++++++- 4 files changed, 29 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 2d0223ddbab..40629f401cf 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -1514,7 +1514,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Position endPosition = trackedRace.getApproximatePosition(toWaypoint, endTimePoint); if (startTimePoint.asDate().equals(prevStartTime)) { - return new SimulatorResultsDTO(startTimePoint.asDate(), 0, null, null, null, null); + return new SimulatorResultsDTO(startTimePoint.asDate(), 0, 0, null, null, null, null); } // determine legtype upwind/downwind/reaching @@ -1573,7 +1573,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S rcDTO.coursePositions.waypointPositions.add(posDTO); posDTO = SimulatorServiceUtils.toPositionDTO(endPosition); rcDTO.coursePositions.waypointPositions.add(posDTO); - result = new SimulatorResultsDTO(startTimePoint.asDate(), legDuration, rcDTO, pathDTOs, null, null); + result = new SimulatorResultsDTO(startTimePoint.asDate(), timeStep.asMillis(), legDuration, rcDTO, pathDTOs, null, null); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java index 3c1d2c6895e..0ee6342f23d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SimulatorServiceImpl.java @@ -301,7 +301,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula windFieldDTO = this.createWindFieldDTO(wf, startTime, endTime, timeStep, params); // params.isShowStreamlets2(), params.isShowLines(), params.getSeedLines()); } - return new SimulatorResultsDTO(null, 0, rcDTO, pathDTOs, windFieldDTO, simulatedPaths.notificationMessage); + return new SimulatorResultsDTO(null, timeStep.asMillis(), 0, rcDTO, pathDTOs, windFieldDTO, simulatedPaths.notificationMessage); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SimulatorResultsDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SimulatorResultsDTO.java index 27b0a25fef1..129a2249ae4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SimulatorResultsDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/SimulatorResultsDTO.java @@ -7,6 +7,7 @@ import com.google.gwt.user.client.rpc.IsSerializable; public class SimulatorResultsDTO implements IsSerializable { private Date startTime; + private long timeStep; private long legDuration; private RaceMapDataDTO raceCourse; private WindFieldDTO windField; @@ -15,6 +16,7 @@ public class SimulatorResultsDTO implements IsSerializable { public SimulatorResultsDTO(){ this.startTime = null; + this.timeStep = 0; this.legDuration = 0; this.raceCourse = null; this.windField = null; @@ -22,8 +24,9 @@ public class SimulatorResultsDTO implements IsSerializable { this.notificationMessage = ""; } - public SimulatorResultsDTO(final Date startTime, final long legDuration, final RaceMapDataDTO raceCourse, final PathDTO[] paths, final WindFieldDTO windField, final String notificationMessage) { + public SimulatorResultsDTO(final Date startTime, final long timeStep, final long legDuration, final RaceMapDataDTO raceCourse, final PathDTO[] paths, final WindFieldDTO windField, final String notificationMessage) { this.startTime = startTime; + this.timeStep = timeStep; this.legDuration = legDuration; this.raceCourse = raceCourse; this.paths = paths; @@ -39,6 +42,14 @@ public class SimulatorResultsDTO implements IsSerializable { this.startTime = startTime; } + public long getTimeStep() { + return this.timeStep; + } + + public void setTimeStep(long timeStep) { + this.timeStep = timeStep; + } + public long getLegDuration() { return this.legDuration; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java index 5dce2d3f60b..6f84ae0212c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java @@ -164,7 +164,20 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { } } ctxt.stroke(); - ctxt.setGlobalAlpha(1.0); + SimulatorWindDTO start = points.get(0); + long timeStep = simulationResult.getTimeStep(); + for(SimulatorWindDTO point : points) { + if ((point.timepoint - start.timepoint) % (timeStep) != 0) { + continue; + } + + + Point px = mapProjection.fromLatLngToContainerPixel(LatLng.newInstance(point.position.latDeg, point.position.lngDeg)); + ctxt.beginPath(); + ctxt.arc(px.getX(), px.getY(), 1.5, 0, 2*Math.PI); + ctxt.closePath(); + ctxt.stroke(); + } colorIdx--; } From a1c211b3991f5d571c0bf93dbe1a0cb4541c5e46 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 11 Nov 2014 17:23:10 +0100 Subject: [PATCH 63/72] first integration of simulation and gps-polars; --- .../gwt/ui/server/SailingServiceImpl.java | 7 +- .../META-INF/MANIFEST.MF | 3 +- .../simulator/impl/PolarDiagramGPS.java | 232 ++++++++++++++++++ 3 files changed, 237 insertions(+), 5 deletions(-) create mode 100644 java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index a1490ba40b2..9dc4c241377 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -396,6 +396,7 @@ import com.sap.sailing.simulator.PolarDiagramFactory; import com.sap.sailing.simulator.SailingSimulator; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.TimedPositionWithSpeed; +import com.sap.sailing.simulator.impl.PolarDiagramGPS; import com.sap.sailing.simulator.impl.SailingSimulatorImpl; import com.sap.sailing.simulator.impl.SimulationParametersImpl; import com.sap.sailing.simulator.util.SailingSimulatorConstants; @@ -1535,10 +1536,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S course.add(startPosition); course.add(endPosition); BoatClass boatClass = trackedRace.getRace().getBoatClass(); - PolarDiagram polarDiagram = polarDiagramService.getService().getPolarDiagram(boatClass); - if (polarDiagram == null) { - return null; - } + PolarDataService polarData = getService().getPolarDataService(); + PolarDiagram polarDiagram = new PolarDiagramGPS(boatClass, polarData); SimulationParameters simulationPars = new SimulationParametersImpl(course, polarDiagram, windField, SailingSimulatorConstants.ModeEvent, true, true); // for upwind/downwind, run simulation with start-time, start-position, end-position and windfield diff --git a/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF b/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF index 3137d7d7316..5cace686e28 100644 --- a/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.simulator/META-INF/MANIFEST.MF @@ -11,7 +11,8 @@ Require-Bundle: com.sap.sailing.domain.common;bundle-version="1.0.0", com.sap.sailing.domain.tractracadapter, com.sap.sse.common, org.apache.httpcomponents.httpclient;bundle-version="4.2.6", - org.json.simple;bundle-version="1.1.0" + org.json.simple;bundle-version="1.1.0", + com.sap.sailing.polars;bundle-version="1.0.0" Import-Package: org.osgi.framework;version="1.7.0" Bundle-ClassPath: ., lib/ssj.jar diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java new file mode 100644 index 00000000000..9d3f5ccdc82 --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java @@ -0,0 +1,232 @@ +package com.sap.sailing.simulator.impl; + +import java.io.Serializable; +import java.util.NavigableMap; +import java.util.Set; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.LegType; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; +import com.sap.sailing.simulator.PolarDiagram; + +public class PolarDiagramGPS implements PolarDiagram, Serializable { + + private static final long serialVersionUID = -9219705955440602679L; + private final BoatClass boatClass; + private final PolarDataService polarData; + private SpeedWithBearing wind; + private SpeedWithBearing beatPort = null; + private SpeedWithBearing beatStar = null; + private SpeedWithBearing jibePort = null; + private SpeedWithBearing jibeStar = null; + + public PolarDiagramGPS(BoatClass boatClass, PolarDataService polarData) { + this.boatClass = boatClass; + this.polarData = polarData; + } + + @Override + public void setSpeedScale(double scaleSpeed) { + // TODO Auto-generated method stub + + } + + @Override + public double getSpeedScale() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setBearingScale(double scaleBearing) { + // TODO Auto-generated method stub + + } + + @Override + public double getBearingScale() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public SpeedWithBearing getWind() { + return this.wind; + } + + @Override + public void setWind(SpeedWithBearing newWind) { + if ((this.wind == null)||(!this.wind.equals(newWind))) { + this.wind = newWind; + this.beatPort = null; + this.beatStar = null; + this.jibePort = null; + this.jibeStar = null; + } + } + + @Override + public void setCurrent(SpeedWithBearing newCurrent) { + // TODO Auto-generated method stub + + } + + @Override + public SpeedWithBearing getCurrent() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Bearing getTargetDirection() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setTargetDirection(Bearing newTargetDirection) { + // TODO Auto-generated method stub + + } + + @Override + public SpeedWithBearing getSpeedAtBearing(Bearing bearing) { + if ((beatPort != null)&&(bearing.equals(beatPort.getBearing()))) { + return beatPort; + } else if ((beatStar != null)&&(bearing.equals(beatStar.getBearing()))) { + return beatStar; + } else if ((jibePort != null)&&(bearing.equals(jibePort.getBearing()))) { + return jibePort; + } else if ((jibeStar != null)&&(bearing.equals(jibeStar.getBearing()))) { + return jibeStar; + } + Speed speed = null; + try { + speed = this.polarData.getSpeed(this.boatClass, this.wind, bearing.getDifferenceTo(wind.getBearing())).getObject(); + } catch (NotEnoughDataHasBeenAddedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return new KnotSpeedWithBearingImpl(speed.getKnots(), bearing); + } + + @Override + public SpeedWithBearing getSpeedAtBearingOverGround(Bearing bearing) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SpeedWithBearing[] optimalVMGUpwind() { + return null; + } + + @Override + public Bearing[] optimalDirectionsUpwind() { + try { + if (beatPort == null) { + beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.PORT).getObject(); + } + if (beatStar == null) { + beatStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.STARBOARD).getObject(); + } + } catch (NotEnoughDataHasBeenAddedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Bearing[] result = new Bearing[2]; + if (beatPort != null) { + result[0] = this.beatPort.getBearing(); + } + if (beatStar != null) { + result[1] = this.beatStar.getBearing(); + } + return result; + } + + @Override + public Bearing[] optimalDirectionsDownwind() { + try { + if (jibePort ==null) { + jibePort = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.PORT).getObject(); + } + if (jibeStar == null) { + jibeStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.STARBOARD).getObject(); + } + } catch (NotEnoughDataHasBeenAddedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Bearing[] result = new Bearing[2]; + if (jibePort != null) { + result[0] = this.jibePort.getBearing(); + } + if (jibeStar != null) { + result[1] = this.jibeStar.getBearing(); + } + return result; + } + + @Override + public long getTurnLoss() { + // TODO Auto-generated method stub + return 4000; + } + + @Override + public WindSide getWindSide(Bearing bearing) { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap> polarDiagramPlot( + Double bearingStep, Set extraSpeeds) { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap> polarDiagramPlot( + Double bearingStep) { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap> getSpeedTable() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap getBeatAngles() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap getGybeAngles() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap getBeatSOG() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap getGybeSOG() { + // TODO Auto-generated method stub + return null; + } + +} From 0d516fb51d0f1e76bc65fd8c0419f2246aa811d1 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 11 Nov 2014 22:38:38 +0100 Subject: [PATCH 64/72] integrate with polar-diagram-base; rename implementation that directly accesses polar-api to polar-diagram-gps-raw; --- .../simulator/impl/PolarDiagramBase.java | 91 +++-- .../simulator/impl/PolarDiagramGPS.java | 340 +++++++----------- .../simulator/impl/PolarDiagramGPSRaw.java | 232 ++++++++++++ 3 files changed, 431 insertions(+), 232 deletions(-) create mode 100644 java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSRaw.java diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java index 6d080de9a8f..311866fb6c7 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java @@ -5,6 +5,7 @@ import java.util.Arrays; import java.util.Comparator; import java.util.HashSet; import java.util.Map; +import java.util.Map.Entry; import java.util.NavigableMap; import java.util.Set; import java.util.TreeMap; @@ -406,31 +407,75 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { NavigableMap floorSpeeds = speedTable.get(floorWind); NavigableMap ceilingSpeeds = speedTable.get(ceilingWind); - // Taylor estimations of order 1 - Speed floorSpeed1 = floorSpeeds.floorEntry(relativeBearing).getValue(); - Speed floorSpeed2 = floorSpeeds.ceilingEntry(relativeBearing).getValue(); - Bearing floorBearing1 = floorSpeeds.floorKey(relativeBearing); - Bearing floorBearing2 = floorSpeeds.ceilingKey(relativeBearing); - double floorSpeed; - if (floorSpeed1.equals(floorSpeed2)) { - floorSpeed = floorSpeed1.getKnots(); - } else { - floorSpeed = floorSpeed1.getKnots() + (relativeBearing.getRadians() - floorBearing1.getRadians()) - * (floorSpeed2.getKnots() - floorSpeed1.getKnots()) - / (floorBearing2.getRadians() - floorBearing1.getRadians()); - } + double floorSpeed; + if (floorSpeeds.size() == 0) { + floorSpeed = 0.0; + } else { + // Taylor estimations of order 1 + Speed floorSpeed1 = null; + Entry floorEntry = floorSpeeds.floorEntry(relativeBearing); + if (floorEntry != null) { + floorSpeed1 = floorEntry.getValue(); + } + Speed floorSpeed2 = null; + floorEntry = floorSpeeds.ceilingEntry(relativeBearing); + if (floorEntry != null) { + floorSpeed2 = floorEntry.getValue(); + } + Bearing floorBearing1 = floorSpeeds.floorKey(relativeBearing); + Bearing floorBearing2 = floorSpeeds.ceilingKey(relativeBearing); + if (floorBearing1 == null) { + floorSpeed = floorSpeed2.getKnots(); + } else if (floorBearing2 == null) { + floorSpeed = floorSpeed1.getKnots(); + } else if (floorSpeed1.equals(floorSpeed2)) { + floorSpeed = floorSpeed1.getKnots(); + } else { + floorSpeed = floorSpeed1.getKnots() + + (relativeBearing.getRadians() - floorBearing1 + .getRadians()) + * (floorSpeed2.getKnots() - floorSpeed1.getKnots()) + / (floorBearing2.getRadians() - floorBearing1 + .getRadians()); + } + } - Speed ceilingSpeed1 = ceilingSpeeds.floorEntry(relativeBearing).getValue(); - Speed ceilingSpeed2 = ceilingSpeeds.ceilingEntry(relativeBearing).getValue(); - Bearing ceilingBearing1 = ceilingSpeeds.floorKey(relativeBearing); - Bearing ceilingBearing2 = ceilingSpeeds.ceilingKey(relativeBearing); double ceilingSpeed; - if (ceilingSpeed1.equals(ceilingSpeed2)) { - ceilingSpeed = ceilingSpeed1.getKnots(); - } else { - ceilingSpeed = ceilingSpeed1.getKnots() + (relativeBearing.getRadians() - ceilingBearing1.getRadians()) - * (ceilingSpeed2.getKnots() - ceilingSpeed1.getKnots()) - / (ceilingBearing2.getRadians() - ceilingBearing1.getRadians()); + if (ceilingSpeeds.size() == 0) { + ceilingSpeed = 0.0; + } else { + Speed ceilingSpeed1 = null; + Entry entry = ceilingSpeeds.floorEntry(relativeBearing); + if (entry != null) { + ceilingSpeed1 = entry.getValue(); + } + Speed ceilingSpeed2 = null; + entry = ceilingSpeeds.ceilingEntry(relativeBearing); + if (entry != null) { + ceilingSpeed2 = entry.getValue(); + } + Bearing ceilingBearing1 = ceilingSpeeds.floorKey(relativeBearing); + Bearing ceilingBearing2 = ceilingSpeeds.ceilingKey(relativeBearing); + if (ceilingBearing1 == null) { + ceilingSpeed = ceilingSpeed2.getKnots(); + } else if (ceilingBearing2 == null) { + ceilingSpeed = ceilingSpeed1.getKnots(); + } else if (ceilingSpeed1.equals(ceilingSpeed2)) { + ceilingSpeed = ceilingSpeed1.getKnots(); + } else { + ceilingSpeed = ceilingSpeed1.getKnots() + + (relativeBearing.getRadians() - ceilingBearing1 + .getRadians()) + * (ceilingSpeed2.getKnots() - ceilingSpeed1.getKnots()) + / (ceilingBearing2.getRadians() - ceilingBearing1 + .getRadians()); + } + } + if (floorSpeeds.size() == 0) { + floorSpeed = ceilingSpeed * floorWind.getKnots() / ceilingWind.getKnots(); + } + if (ceilingSpeeds.size() == 0) { + ceilingSpeed = floorSpeed * ceilingWind.getKnots() / floorWind.getKnots(); } double speed; diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java index 9d3f5ccdc82..aa235ba117f 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java @@ -1,8 +1,12 @@ package com.sap.sailing.simulator.impl; -import java.io.Serializable; +import java.util.ArrayList; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.Map.Entry; import java.util.NavigableMap; -import java.util.Set; +import java.util.TreeMap; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.Bearing; @@ -10,223 +14,141 @@ import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; -import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; -import com.sap.sailing.simulator.PolarDiagram; -public class PolarDiagramGPS implements PolarDiagram, Serializable { +public class PolarDiagramGPS extends PolarDiagramBase { private static final long serialVersionUID = -9219705955440602679L; private final BoatClass boatClass; private final PolarDataService polarData; - private SpeedWithBearing wind; - private SpeedWithBearing beatPort = null; - private SpeedWithBearing beatStar = null; - private SpeedWithBearing jibePort = null; - private SpeedWithBearing jibeStar = null; - + public PolarDiagramGPS(BoatClass boatClass, PolarDataService polarData) { this.boatClass = boatClass; this.polarData = polarData; + + List velocities = new ArrayList(); + List beatAngles = new ArrayList(); + List beatVMG = new ArrayList(); + Map> speeds = new HashMap>(); + List runVMG = new ArrayList(); + List gybeAngles = new ArrayList(); + + // initialize wind speeds + velocities.add(new KnotSpeedImpl(0.0)); + velocities.add(new KnotSpeedImpl(6.0)); + velocities.add(new KnotSpeedImpl(8.0)); + velocities.add(new KnotSpeedImpl(10.0)); + velocities.add(new KnotSpeedImpl(12.0)); + velocities.add(new KnotSpeedImpl(14.0)); + velocities.add(new KnotSpeedImpl(16.0)); + velocities.add(new KnotSpeedImpl(20.0)); + + // initialize beat-angles and -speeds + //SpeedWithBearing beatPort; + SpeedWithBearing beatStar; + for (int i = 0; i < velocities.size(); i++) { + try { + //beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, velocities.get(i), LegType.UPWIND, Tack.PORT).getObject(); + beatStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, velocities.get(i), LegType.UPWIND, Tack.STARBOARD).getObject(); + } catch (NotEnoughDataHasBeenAddedException e) { + //beatPort = null; + beatStar = null; + } + if (beatStar != null) { + beatAngles.add(beatStar.getBearing()); + beatVMG.add(beatStar); + } else { + beatAngles.add(null); + beatVMG.add(null); + } + } + + // initialize jibe-angles and -speeds + //SpeedWithBearing jibePort; + SpeedWithBearing jibeStar; + for (int i = 0; i < velocities.size(); i++) { + try { + //jibePort = this.polarData.getAverageSpeedWithBearing(this.boatClass, velocities.get(i), LegType.DOWNWIND, Tack.PORT).getObject(); + jibeStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, velocities.get(i), LegType.DOWNWIND, Tack.STARBOARD).getObject(); + } catch (NotEnoughDataHasBeenAddedException e) { + // TODO Auto-generated catch block + //e.printStackTrace(); + //jibePort = null; + jibeStar = null; + } + if (jibeStar != null) { + gybeAngles.add(jibeStar.getBearing()); + runVMG.add(jibeStar); + } else { + gybeAngles.add(null); + runVMG.add(null); + } + } + + NavigableMap> mapSpeedTable = new TreeMap>(); + NavigableMap mapBeatAngles = new TreeMap(); + NavigableMap mapGybeAngles = new TreeMap(); + NavigableMap mapBeatSOG = new TreeMap(); + NavigableMap mapGybeSOG = new TreeMap(); + + Speed velocity = null; + Speed speed = null; + NavigableMap speedTableLine = null; + + for (int index = 0; index < velocities.size(); index++) { + velocity = velocities.get(index); + if (velocity.getKnots() == 0.0) { + mapBeatSOG.put(new KnotSpeedImpl(0.0), new KnotSpeedImpl(0.0)); + mapGybeSOG.put(new KnotSpeedImpl(0.0), new KnotSpeedImpl(0.0)); + } + speedTableLine = new TreeMap(bearingComparator); + for (Entry> entry : speeds.entrySet()) { + if (index >= entry.getValue().size()) { + continue; + } + + speed = entry.getValue().get(index); + + if (speed != Speed.NULL) { + speedTableLine.put(entry.getKey(), speed); + } + } + + mapSpeedTable.put(velocity, speedTableLine); + if (beatAngles.get(index) != null) { + mapBeatAngles.put(velocity, beatAngles.get(index)); + } + if (gybeAngles.get(index) != null) { + mapGybeAngles.put(velocity, gybeAngles.get(index)); + } + if (beatVMG.get(index) != null) { + mapBeatSOG.put(velocity, beatVMG.get(index)); + } + if (runVMG.get(index) != null) { + mapGybeSOG.put(velocity, runVMG.get(index)); + } + } + + super.speedTable = mapSpeedTable; + super.beatAngles = mapBeatAngles; + super.gybeAngles = mapGybeAngles; + super.beatSOG = mapBeatSOG; + super.gybeSOG = mapGybeSOG; + + for (Speed s : super.speedTable.keySet()) { + + if (super.beatAngles.containsKey(s) && !super.speedTable.get(s).containsKey(super.beatAngles.get(s))) { + super.speedTable.get(s).put(super.beatAngles.get(s), super.beatSOG.get(s)); + } + + if (super.gybeAngles.containsKey(s) && !super.speedTable.get(s).containsKey(super.gybeAngles.get(s))) { + super.speedTable.get(s).put(super.gybeAngles.get(s), super.gybeSOG.get(s)); + } + + } + } - @Override - public void setSpeedScale(double scaleSpeed) { - // TODO Auto-generated method stub - - } - - @Override - public double getSpeedScale() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public void setBearingScale(double scaleBearing) { - // TODO Auto-generated method stub - - } - - @Override - public double getBearingScale() { - // TODO Auto-generated method stub - return 0; - } - - @Override - public SpeedWithBearing getWind() { - return this.wind; - } - - @Override - public void setWind(SpeedWithBearing newWind) { - if ((this.wind == null)||(!this.wind.equals(newWind))) { - this.wind = newWind; - this.beatPort = null; - this.beatStar = null; - this.jibePort = null; - this.jibeStar = null; - } - } - - @Override - public void setCurrent(SpeedWithBearing newCurrent) { - // TODO Auto-generated method stub - - } - - @Override - public SpeedWithBearing getCurrent() { - // TODO Auto-generated method stub - return null; - } - - @Override - public Bearing getTargetDirection() { - // TODO Auto-generated method stub - return null; - } - - @Override - public void setTargetDirection(Bearing newTargetDirection) { - // TODO Auto-generated method stub - - } - - @Override - public SpeedWithBearing getSpeedAtBearing(Bearing bearing) { - if ((beatPort != null)&&(bearing.equals(beatPort.getBearing()))) { - return beatPort; - } else if ((beatStar != null)&&(bearing.equals(beatStar.getBearing()))) { - return beatStar; - } else if ((jibePort != null)&&(bearing.equals(jibePort.getBearing()))) { - return jibePort; - } else if ((jibeStar != null)&&(bearing.equals(jibeStar.getBearing()))) { - return jibeStar; - } - Speed speed = null; - try { - speed = this.polarData.getSpeed(this.boatClass, this.wind, bearing.getDifferenceTo(wind.getBearing())).getObject(); - } catch (NotEnoughDataHasBeenAddedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - return new KnotSpeedWithBearingImpl(speed.getKnots(), bearing); - } - - @Override - public SpeedWithBearing getSpeedAtBearingOverGround(Bearing bearing) { - // TODO Auto-generated method stub - return null; - } - - @Override - public SpeedWithBearing[] optimalVMGUpwind() { - return null; - } - - @Override - public Bearing[] optimalDirectionsUpwind() { - try { - if (beatPort == null) { - beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.PORT).getObject(); - } - if (beatStar == null) { - beatStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.STARBOARD).getObject(); - } - } catch (NotEnoughDataHasBeenAddedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - Bearing[] result = new Bearing[2]; - if (beatPort != null) { - result[0] = this.beatPort.getBearing(); - } - if (beatStar != null) { - result[1] = this.beatStar.getBearing(); - } - return result; - } - - @Override - public Bearing[] optimalDirectionsDownwind() { - try { - if (jibePort ==null) { - jibePort = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.PORT).getObject(); - } - if (jibeStar == null) { - jibeStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.STARBOARD).getObject(); - } - } catch (NotEnoughDataHasBeenAddedException e) { - // TODO Auto-generated catch block - e.printStackTrace(); - } - Bearing[] result = new Bearing[2]; - if (jibePort != null) { - result[0] = this.jibePort.getBearing(); - } - if (jibeStar != null) { - result[1] = this.jibeStar.getBearing(); - } - return result; - } - - @Override - public long getTurnLoss() { - // TODO Auto-generated method stub - return 4000; - } - - @Override - public WindSide getWindSide(Bearing bearing) { - // TODO Auto-generated method stub - return null; - } - - @Override - public NavigableMap> polarDiagramPlot( - Double bearingStep, Set extraSpeeds) { - // TODO Auto-generated method stub - return null; - } - - @Override - public NavigableMap> polarDiagramPlot( - Double bearingStep) { - // TODO Auto-generated method stub - return null; - } - - @Override - public NavigableMap> getSpeedTable() { - // TODO Auto-generated method stub - return null; - } - - @Override - public NavigableMap getBeatAngles() { - // TODO Auto-generated method stub - return null; - } - - @Override - public NavigableMap getGybeAngles() { - // TODO Auto-generated method stub - return null; - } - - @Override - public NavigableMap getBeatSOG() { - // TODO Auto-generated method stub - return null; - } - - @Override - public NavigableMap getGybeSOG() { - // TODO Auto-generated method stub - return null; - } - } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSRaw.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSRaw.java new file mode 100644 index 00000000000..ddae39a018e --- /dev/null +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSRaw.java @@ -0,0 +1,232 @@ +package com.sap.sailing.simulator.impl; + +import java.io.Serializable; +import java.util.NavigableMap; +import java.util.Set; + +import com.sap.sailing.domain.base.BoatClass; +import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.LegType; +import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; +import com.sap.sailing.simulator.PolarDiagram; + +public class PolarDiagramGPSRaw implements PolarDiagram, Serializable { + + private static final long serialVersionUID = -9219705955440602679L; + private final BoatClass boatClass; + private final PolarDataService polarData; + private SpeedWithBearing wind; + private SpeedWithBearing beatPort = null; + private SpeedWithBearing beatStar = null; + private SpeedWithBearing jibePort = null; + private SpeedWithBearing jibeStar = null; + + public PolarDiagramGPSRaw(BoatClass boatClass, PolarDataService polarData) { + this.boatClass = boatClass; + this.polarData = polarData; + } + + @Override + public void setSpeedScale(double scaleSpeed) { + // TODO Auto-generated method stub + + } + + @Override + public double getSpeedScale() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public void setBearingScale(double scaleBearing) { + // TODO Auto-generated method stub + + } + + @Override + public double getBearingScale() { + // TODO Auto-generated method stub + return 0; + } + + @Override + public SpeedWithBearing getWind() { + return this.wind; + } + + @Override + public void setWind(SpeedWithBearing newWind) { + if ((this.wind == null)||(!this.wind.equals(newWind))) { + this.wind = newWind; + this.beatPort = null; + this.beatStar = null; + this.jibePort = null; + this.jibeStar = null; + } + } + + @Override + public void setCurrent(SpeedWithBearing newCurrent) { + // TODO Auto-generated method stub + + } + + @Override + public SpeedWithBearing getCurrent() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Bearing getTargetDirection() { + // TODO Auto-generated method stub + return null; + } + + @Override + public void setTargetDirection(Bearing newTargetDirection) { + // TODO Auto-generated method stub + + } + + @Override + public SpeedWithBearing getSpeedAtBearing(Bearing bearing) { + if ((beatPort != null)&&(bearing.equals(beatPort.getBearing()))) { + return beatPort; + } else if ((beatStar != null)&&(bearing.equals(beatStar.getBearing()))) { + return beatStar; + } else if ((jibePort != null)&&(bearing.equals(jibePort.getBearing()))) { + return jibePort; + } else if ((jibeStar != null)&&(bearing.equals(jibeStar.getBearing()))) { + return jibeStar; + } + Speed speed = null; + try { + speed = this.polarData.getSpeed(this.boatClass, this.wind, bearing.getDifferenceTo(wind.getBearing())).getObject(); + } catch (NotEnoughDataHasBeenAddedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + return new KnotSpeedWithBearingImpl(speed.getKnots(), bearing); + } + + @Override + public SpeedWithBearing getSpeedAtBearingOverGround(Bearing bearing) { + // TODO Auto-generated method stub + return null; + } + + @Override + public SpeedWithBearing[] optimalVMGUpwind() { + return null; + } + + @Override + public Bearing[] optimalDirectionsUpwind() { + try { + if (beatPort == null) { + beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.PORT).getObject(); + } + if (beatStar == null) { + beatStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.STARBOARD).getObject(); + } + } catch (NotEnoughDataHasBeenAddedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Bearing[] result = new Bearing[2]; + if (beatPort != null) { + result[0] = this.beatPort.getBearing(); + } + if (beatStar != null) { + result[1] = this.beatStar.getBearing(); + } + return result; + } + + @Override + public Bearing[] optimalDirectionsDownwind() { + try { + if (jibePort ==null) { + jibePort = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.PORT).getObject(); + } + if (jibeStar == null) { + jibeStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.STARBOARD).getObject(); + } + } catch (NotEnoughDataHasBeenAddedException e) { + // TODO Auto-generated catch block + e.printStackTrace(); + } + Bearing[] result = new Bearing[2]; + if (jibePort != null) { + result[0] = this.jibePort.getBearing(); + } + if (jibeStar != null) { + result[1] = this.jibeStar.getBearing(); + } + return result; + } + + @Override + public long getTurnLoss() { + // TODO Auto-generated method stub + return 4000; + } + + @Override + public WindSide getWindSide(Bearing bearing) { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap> polarDiagramPlot( + Double bearingStep, Set extraSpeeds) { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap> polarDiagramPlot( + Double bearingStep) { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap> getSpeedTable() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap getBeatAngles() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap getGybeAngles() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap getBeatSOG() { + // TODO Auto-generated method stub + return null; + } + + @Override + public NavigableMap getGybeSOG() { + // TODO Auto-generated method stub + return null; + } + +} From 7f628c0e053ad1cbcdeb5d33d429fca79314f9c5 Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Tue, 11 Nov 2014 22:56:43 +0100 Subject: [PATCH 65/72] renaming & formatting; --- .../simulator/test/PolarDiagramCSVTest.java | 4 +- .../resources/PolarDiagram49.csv | 6 +- .../resources/PolarDiagram49Bethwaite.csv | 6 +- .../resources/PolarDiagram49ORC.csv | 6 +- .../resources/PolarDiagram49STG.csv | 6 +- .../resources/PolarDiagram505STG.csv | 6 +- .../sap/sailing/simulator/PolarDiagram.java | 4 +- .../simulator/impl/PolarDiagram49.java | 40 ++++---- .../impl/PolarDiagram49Bethwaite.java | 28 +++--- .../simulator/impl/PolarDiagram49ORC.java | 40 ++++---- .../simulator/impl/PolarDiagram49STG.java | 40 ++++---- .../simulator/impl/PolarDiagram505STG.java | 40 ++++---- .../simulator/impl/PolarDiagramBase.java | 93 +++++++++--------- .../simulator/impl/PolarDiagramCSV.java | 58 +++++------ .../simulator/impl/PolarDiagramGPS.java | 96 +++++++++---------- ...GPSRaw.java => PolarDiagramGPSDirect.java} | 8 +- 16 files changed, 241 insertions(+), 240 deletions(-) rename java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/{PolarDiagramGPSRaw.java => PolarDiagramGPSDirect.java} (95%) diff --git a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/PolarDiagramCSVTest.java b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/PolarDiagramCSVTest.java index bc2f1eaa234..5f87f146f50 100644 --- a/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/PolarDiagramCSVTest.java +++ b/java/com.sap.sailing.simulator.test/src/com/sap/sailing/simulator/test/PolarDiagramCSVTest.java @@ -160,11 +160,11 @@ public class PolarDiagramCSVTest { testMapOfSpeedsAndBearings(expectedPD.getBeatAngles(), actualPD.getBeatAngles(), "beatAngles", typeName); - testMapOfSpeedsAndBearings(expectedPD.getGybeAngles(), actualPD.getGybeAngles(), "gypeAngles", typeName); + testMapOfSpeedsAndBearings(expectedPD.getJibeAngles(), actualPD.getJibeAngles(), "jibeAngles", typeName); testMapOfSpeedsAndSpeeds(expectedPD.getBeatSOG(), actualPD.getBeatSOG(), "beatSOG", typeName); - testMapOfSpeedsAndSpeeds(expectedPD.getGybeSOG(), actualPD.getGybeSOG(), "gybeSOG", typeName); + testMapOfSpeedsAndSpeeds(expectedPD.getJibeSOG(), actualPD.getJibeSOG(), "jibeSOG", typeName); } private static void testSpeedsAtBearingsOfPolarDiagrams(PolarDiagram expectedPD, PolarDiagram actualPD, diff --git a/java/com.sap.sailing.simulator/resources/PolarDiagram49.csv b/java/com.sap.sailing.simulator/resources/PolarDiagram49.csv index b76194054de..204e2412a0f 100644 --- a/java/com.sap.sailing.simulator/resources/PolarDiagram49.csv +++ b/java/com.sap.sailing.simulator/resources/PolarDiagram49.csv @@ -1,4 +1,4 @@ -wind velocity,0,6,8,10,12,14,16,20 +wind speed,0,6,8,10,12,14,16,20 beat angles,45,43.4,41.8,38.9,37.5,36.8,36.3,36.9 beat sog,0,6.435,7.678,8.129,8.448,8.679,8.712,8.734 0,0,0,0,0,0,0,0,0 @@ -11,5 +11,5 @@ beat sog,0,6.435,7.678,8.129,8.448,8.679,8.712,8.734 135,0,5.58,7,8.02,8.71,9.29,9.88,11.47 150,0,4.62,5.85,6.97,7.86,8.57,9.13,10.26 180,0,2.5,3,4,5,6,7,7.5 -run sog,0,5.22,6.38,7.09,7.6,7.97,8.39,9.38 -gybe angles,120,140,142.6,148.4,154.4,162.5,169.6,172.3 +jibe sog,0,5.22,6.38,7.09,7.6,7.97,8.39,9.38 +jibe angles,120,140,142.6,148.4,154.4,162.5,169.6,172.3 diff --git a/java/com.sap.sailing.simulator/resources/PolarDiagram49Bethwaite.csv b/java/com.sap.sailing.simulator/resources/PolarDiagram49Bethwaite.csv index b0cedb8db6f..01a5b59b40a 100644 --- a/java/com.sap.sailing.simulator/resources/PolarDiagram49Bethwaite.csv +++ b/java/com.sap.sailing.simulator/resources/PolarDiagram49Bethwaite.csv @@ -1,4 +1,4 @@ -wind velocity,0,6,9,12,25 +wind speed,0,6,9,12,25 beat angles,43,50.24,49.4,47.35,48.01 beat sog,0,7.53,9.39,11.96,13.08 0,0,0,0,0,0 @@ -42,5 +42,5 @@ beat sog,0,7.53,9.39,11.96,13.08 159.51,,,,,22.09 163.83,,,,,16.29 180,0,5.56,6.48,7.55,14.72 -run sog,0,12.63,14.35,17.82,23.25 -gybe angles,135,141.7,144.7,150.8,155.8 +jibe sog,0,12.63,14.35,17.82,23.25 +jibe angles,135,141.7,144.7,150.8,155.8 diff --git a/java/com.sap.sailing.simulator/resources/PolarDiagram49ORC.csv b/java/com.sap.sailing.simulator/resources/PolarDiagram49ORC.csv index b76194054de..204e2412a0f 100644 --- a/java/com.sap.sailing.simulator/resources/PolarDiagram49ORC.csv +++ b/java/com.sap.sailing.simulator/resources/PolarDiagram49ORC.csv @@ -1,4 +1,4 @@ -wind velocity,0,6,8,10,12,14,16,20 +wind speed,0,6,8,10,12,14,16,20 beat angles,45,43.4,41.8,38.9,37.5,36.8,36.3,36.9 beat sog,0,6.435,7.678,8.129,8.448,8.679,8.712,8.734 0,0,0,0,0,0,0,0,0 @@ -11,5 +11,5 @@ beat sog,0,6.435,7.678,8.129,8.448,8.679,8.712,8.734 135,0,5.58,7,8.02,8.71,9.29,9.88,11.47 150,0,4.62,5.85,6.97,7.86,8.57,9.13,10.26 180,0,2.5,3,4,5,6,7,7.5 -run sog,0,5.22,6.38,7.09,7.6,7.97,8.39,9.38 -gybe angles,120,140,142.6,148.4,154.4,162.5,169.6,172.3 +jibe sog,0,5.22,6.38,7.09,7.6,7.97,8.39,9.38 +jibe angles,120,140,142.6,148.4,154.4,162.5,169.6,172.3 diff --git a/java/com.sap.sailing.simulator/resources/PolarDiagram49STG.csv b/java/com.sap.sailing.simulator/resources/PolarDiagram49STG.csv index 05fa83a67be..18db9984875 100644 --- a/java/com.sap.sailing.simulator/resources/PolarDiagram49STG.csv +++ b/java/com.sap.sailing.simulator/resources/PolarDiagram49STG.csv @@ -1,9 +1,9 @@ -wind velocity,0,6,8,10,12,14,16,20 +wind speed,0,6,8,10,12,14,16,20 beat angles,43,43,43,43,44,45,45.6,47 beat sog,0,4.5,6.25,8,8.5,9,9.3,10 0,0,0,0,0,0,0,0,0 38,0,0,0,0,0,0,0,0 90,0,4.73,6.56,8.4,8.93,9.45,9.77,10.5 180,0,2.84,3.94,5.04,5.36,5.67,5.86,6.3 -run sog,0,8,11,14,15,16,17,19 -gybe angles,135,135,140,145,150,155,157,160 +jibe sog,0,8,11,14,15,16,17,19 +jibe angles,135,135,140,145,150,155,157,160 diff --git a/java/com.sap.sailing.simulator/resources/PolarDiagram505STG.csv b/java/com.sap.sailing.simulator/resources/PolarDiagram505STG.csv index fe5deef200c..4c97454a189 100644 --- a/java/com.sap.sailing.simulator/resources/PolarDiagram505STG.csv +++ b/java/com.sap.sailing.simulator/resources/PolarDiagram505STG.csv @@ -1,9 +1,9 @@ -wind velocity,0,6,8,10,12,14,16,20 +wind speed,0,6,8,10,12,14,16,20 beat angles,44,44,46.5,50,52,54,55,56 beat sog,0,5,5.3,5.6,6,6.5,7,7 0,0,0,0,0,0,0,0,0 35,1,1,1,1,1,1,1,1 90,0,5.25,5.57,5.88,6.3,6.83,7.35,7.35 180,0,2.5,3.35,4.25,5,5.75,6.5,7.5 -run sog,0,5,5.3,5.6,6,6.5,7,7 -gybe angles,141,141,141,139.5,137.5,139.5,142.5,150 +jibe sog,0,5,5.3,5.6,6,6.5,7,7 +jibe angles,141,141,141,139.5,137.5,139.5,142.5,150 diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagram.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagram.java index 70b8ccf5bfc..c53d220463a 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagram.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/PolarDiagram.java @@ -48,8 +48,8 @@ public interface PolarDiagram extends Serializable { NavigableMap> polarDiagramPlot(Double bearingStep); NavigableMap> getSpeedTable(); NavigableMap getBeatAngles(); - NavigableMap getGybeAngles(); + NavigableMap getJibeAngles(); NavigableMap getBeatSOG(); - NavigableMap getGybeSOG(); + NavigableMap getJibeSOG(); } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49.java index 40f28deda6b..0dab0c476ca 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49.java @@ -142,25 +142,25 @@ public class PolarDiagram49 extends PolarDiagramBase { beatSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(7.92 * beatScale)); beatSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(7.94 * beatScale)); - gybeAngles = new TreeMap(); - gybeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(120)); - gybeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(140)); - gybeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(142.6)); - gybeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(148.4)); - gybeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(154.4)); - gybeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(162.5)); - gybeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(169.6)); - gybeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(172.3)); + jibeAngles = new TreeMap(); + jibeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(120)); + jibeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(140)); + jibeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(142.6)); + jibeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(148.4)); + jibeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(154.4)); + jibeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(162.5)); + jibeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(169.6)); + jibeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(172.3)); - gybeSOG = new TreeMap(); - gybeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); - gybeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.22)); - gybeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(6.38)); - gybeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(7.09)); - gybeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(7.6)); - gybeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(7.97)); - gybeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(8.39)); - gybeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(9.38)); + jibeSOG = new TreeMap(); + jibeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); + jibeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.22)); + jibeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(6.38)); + jibeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(7.09)); + jibeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(7.6)); + jibeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(7.97)); + jibeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(8.39)); + jibeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(9.38)); for (Speed s : speedTable.keySet()) { @@ -168,8 +168,8 @@ public class PolarDiagram49 extends PolarDiagramBase { speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); } - if (gybeAngles.containsKey(s) && !speedTable.get(s).containsKey(gybeAngles.get(s))) { - speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); + if (jibeAngles.containsKey(s) && !speedTable.get(s).containsKey(jibeAngles.get(s))) { + speedTable.get(s).put(jibeAngles.get(s), jibeSOG.get(s)); } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49Bethwaite.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49Bethwaite.java index 131468b7120..5f45b9917fa 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49Bethwaite.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49Bethwaite.java @@ -30,19 +30,19 @@ public class PolarDiagram49Bethwaite extends PolarDiagramBase { beatSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(11.96 * beatScale)); beatSOG.put(new KnotSpeedImpl(25), new KnotSpeedImpl(13.08 * beatScale)); - gybeAngles = new TreeMap(); - gybeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(135.0)); - gybeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(141.7)); - gybeAngles.put(new KnotSpeedImpl(9), new DegreeBearingImpl(144.7)); - gybeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(150.8)); - gybeAngles.put(new KnotSpeedImpl(25), new DegreeBearingImpl(155.8)); + jibeAngles = new TreeMap(); + jibeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(135.0)); + jibeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(141.7)); + jibeAngles.put(new KnotSpeedImpl(9), new DegreeBearingImpl(144.7)); + jibeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(150.8)); + jibeAngles.put(new KnotSpeedImpl(25), new DegreeBearingImpl(155.8)); - gybeSOG = new TreeMap(); - gybeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); - gybeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(12.63)); - gybeSOG.put(new KnotSpeedImpl(9), new KnotSpeedImpl(14.35)); - gybeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(17.82)); - gybeSOG.put(new KnotSpeedImpl(25), new KnotSpeedImpl(23.25)); + jibeSOG = new TreeMap(); + jibeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); + jibeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(12.63)); + jibeSOG.put(new KnotSpeedImpl(9), new KnotSpeedImpl(14.35)); + jibeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(17.82)); + jibeSOG.put(new KnotSpeedImpl(25), new KnotSpeedImpl(23.25)); speedTable = new TreeMap>(); NavigableMap tableRow; @@ -161,8 +161,8 @@ public class PolarDiagram49Bethwaite extends PolarDiagramBase { speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); } - if (gybeAngles.containsKey(s) && !speedTable.get(s).containsKey(gybeAngles.get(s))) { - speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); + if (jibeAngles.containsKey(s) && !speedTable.get(s).containsKey(jibeAngles.get(s))) { + speedTable.get(s).put(jibeAngles.get(s), jibeSOG.get(s)); } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49ORC.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49ORC.java index e5c5ebd6902..7a18c8e94db 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49ORC.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49ORC.java @@ -143,25 +143,25 @@ public class PolarDiagram49ORC extends PolarDiagramBase { beatSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(7.92 * beatScale)); beatSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(7.94 * beatScale)); - gybeAngles = new TreeMap(); - gybeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(120)); - gybeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(140)); - gybeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(142.6)); - gybeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(148.4)); - gybeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(154.4)); - gybeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(162.5)); - gybeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(169.6)); - gybeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(172.3)); + jibeAngles = new TreeMap(); + jibeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(120)); + jibeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(140)); + jibeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(142.6)); + jibeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(148.4)); + jibeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(154.4)); + jibeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(162.5)); + jibeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(169.6)); + jibeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(172.3)); - gybeSOG = new TreeMap(); - gybeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); - gybeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.22)); - gybeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(6.38)); - gybeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(7.09)); - gybeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(7.6)); - gybeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(7.97)); - gybeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(8.39)); - gybeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(9.38)); + jibeSOG = new TreeMap(); + jibeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); + jibeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.22)); + jibeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(6.38)); + jibeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(7.09)); + jibeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(7.6)); + jibeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(7.97)); + jibeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(8.39)); + jibeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(9.38)); for (Speed s : speedTable.keySet()) { @@ -169,8 +169,8 @@ public class PolarDiagram49ORC extends PolarDiagramBase { speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); } - if (gybeAngles.containsKey(s) && !speedTable.get(s).containsKey(gybeAngles.get(s))) { - speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); + if (jibeAngles.containsKey(s) && !speedTable.get(s).containsKey(jibeAngles.get(s))) { + speedTable.get(s).put(jibeAngles.get(s), jibeSOG.get(s)); } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49STG.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49STG.java index 09bb8b23b6e..44562db7b40 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49STG.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram49STG.java @@ -98,25 +98,25 @@ public class PolarDiagram49STG extends PolarDiagramBase { beatSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(9.30 * beatScale)); beatSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(10.00 * beatScale)); - gybeAngles = new TreeMap(); - gybeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(135.0)); - gybeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(135.0)); - gybeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(140.0)); - gybeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(145.0)); - gybeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(150.0)); - gybeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(155.0)); - gybeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(157.0)); - gybeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(160.0)); + jibeAngles = new TreeMap(); + jibeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(135.0)); + jibeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(135.0)); + jibeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(140.0)); + jibeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(145.0)); + jibeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(150.0)); + jibeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(155.0)); + jibeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(157.0)); + jibeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(160.0)); - gybeSOG = new TreeMap(); - gybeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); - gybeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(8.00)); - gybeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(11.00)); - gybeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(14.00)); - gybeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(15.00)); - gybeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(16.00)); - gybeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(17.00)); - gybeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(19.00)); + jibeSOG = new TreeMap(); + jibeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); + jibeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(8.00)); + jibeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(11.00)); + jibeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(14.00)); + jibeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(15.00)); + jibeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(16.00)); + jibeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(17.00)); + jibeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(19.00)); for (Speed s : speedTable.keySet()) { @@ -124,8 +124,8 @@ public class PolarDiagram49STG extends PolarDiagramBase { speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); } - if (gybeAngles.containsKey(s) && !speedTable.get(s).containsKey(gybeAngles.get(s))) { - speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); + if (jibeAngles.containsKey(s) && !speedTable.get(s).containsKey(jibeAngles.get(s))) { + speedTable.get(s).put(jibeAngles.get(s), jibeSOG.get(s)); } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram505STG.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram505STG.java index e1422d3a9f0..1e00fa2402e 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram505STG.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagram505STG.java @@ -96,25 +96,25 @@ public class PolarDiagram505STG extends PolarDiagramBase { beatSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(7.00 * beatScale)); beatSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(7.00 * beatScale)); - gybeAngles = new TreeMap(); - gybeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(141.0)); - gybeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(141.0)); - gybeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(141.0)); - gybeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(139.5)); - gybeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(137.5)); - gybeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(139.5)); - gybeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(142.5)); - gybeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(150.0)); + jibeAngles = new TreeMap(); + jibeAngles.put(new KnotSpeedImpl(0), new DegreeBearingImpl(141.0)); + jibeAngles.put(new KnotSpeedImpl(6), new DegreeBearingImpl(141.0)); + jibeAngles.put(new KnotSpeedImpl(8), new DegreeBearingImpl(141.0)); + jibeAngles.put(new KnotSpeedImpl(10), new DegreeBearingImpl(139.5)); + jibeAngles.put(new KnotSpeedImpl(12), new DegreeBearingImpl(137.5)); + jibeAngles.put(new KnotSpeedImpl(14), new DegreeBearingImpl(139.5)); + jibeAngles.put(new KnotSpeedImpl(16), new DegreeBearingImpl(142.5)); + jibeAngles.put(new KnotSpeedImpl(20), new DegreeBearingImpl(150.0)); - gybeSOG = new TreeMap(); - gybeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); - gybeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.00)); - gybeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(6.70)); - gybeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(8.50)); - gybeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(10.00)); - gybeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(11.50)); - gybeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(13.00)); - gybeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(15.00)); + jibeSOG = new TreeMap(); + jibeSOG.put(new KnotSpeedImpl(0), new KnotSpeedImpl(0)); + jibeSOG.put(new KnotSpeedImpl(6), new KnotSpeedImpl(5.00)); + jibeSOG.put(new KnotSpeedImpl(8), new KnotSpeedImpl(6.70)); + jibeSOG.put(new KnotSpeedImpl(10), new KnotSpeedImpl(8.50)); + jibeSOG.put(new KnotSpeedImpl(12), new KnotSpeedImpl(10.00)); + jibeSOG.put(new KnotSpeedImpl(14), new KnotSpeedImpl(11.50)); + jibeSOG.put(new KnotSpeedImpl(16), new KnotSpeedImpl(13.00)); + jibeSOG.put(new KnotSpeedImpl(20), new KnotSpeedImpl(15.00)); for (Speed s : speedTable.keySet()) { @@ -122,8 +122,8 @@ public class PolarDiagram505STG extends PolarDiagramBase { speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); } - if (gybeAngles.containsKey(s) && !speedTable.get(s).containsKey(gybeAngles.get(s))) { - speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); + if (jibeAngles.containsKey(s) && !speedTable.get(s).containsKey(jibeAngles.get(s))) { + speedTable.get(s).put(jibeAngles.get(s), jibeSOG.get(s)); } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java index 311866fb6c7..b31232928bd 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramBase.java @@ -34,9 +34,9 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { protected NavigableMap> speedTable; protected NavigableMap extTable = null; protected NavigableMap beatAngles; - protected NavigableMap gybeAngles; + protected NavigableMap jibeAngles; protected NavigableMap beatSOG; - protected NavigableMap gybeSOG; + protected NavigableMap jibeSOG; protected double scaleBearing = 1.0; protected double scaleSpeed = 1.0; @@ -72,8 +72,8 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { } @Override - public NavigableMap getGybeAngles() { - return this.gybeAngles; + public NavigableMap getJibeAngles() { + return this.jibeAngles; } @Override @@ -82,8 +82,8 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { } @Override - public NavigableMap getGybeSOG() { - return this.gybeSOG; + public NavigableMap getJibeSOG() { + return this.jibeSOG; } // this constructor creates an instance with a hard-coded set of values @@ -93,16 +93,16 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { // a constructor that allows a generic set of parameters public PolarDiagramBase(NavigableMap> speeds, - NavigableMap beats, NavigableMap gybes, - NavigableMap beatSOGs, NavigableMap gybeSOGs) { + NavigableMap beats, NavigableMap jibes, + NavigableMap beatSOGs, NavigableMap jibeSOGs) { wind = new KnotSpeedWithBearingImpl(0, new DegreeBearingImpl(180)); speedTable = speeds; beatAngles = beats; - gybeAngles = gybes; + jibeAngles = jibes; beatSOG = beatSOGs; - gybeSOG = gybeSOGs; + jibeSOG = jibeSOGs; for (Speed s : speedTable.keySet()) { @@ -110,8 +110,8 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { speedTable.get(s).put(beatAngles.get(s), beatSOG.get(s)); } - if (gybeAngles.containsKey(s) && !speedTable.get(s).containsKey(gybeAngles.get(s))) { - speedTable.get(s).put(gybeAngles.get(s), gybeSOG.get(s)); + if (jibeAngles.containsKey(s) && !speedTable.get(s).containsKey(jibeAngles.get(s))) { + speedTable.get(s).put(jibeAngles.get(s), jibeSOG.get(s)); } } @@ -411,7 +411,6 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { if (floorSpeeds.size() == 0) { floorSpeed = 0.0; } else { - // Taylor estimations of order 1 Speed floorSpeed1 = null; Entry floorEntry = floorSpeeds.floorEntry(relativeBearing); if (floorEntry != null) { @@ -425,8 +424,10 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { Bearing floorBearing1 = floorSpeeds.floorKey(relativeBearing); Bearing floorBearing2 = floorSpeeds.ceilingKey(relativeBearing); if (floorBearing1 == null) { + // for gps-polar: if no floor-bearing known; extrapolate boat-speed constantly with bearing floorSpeed = floorSpeed2.getKnots(); } else if (floorBearing2 == null) { + // for gps-polar: if no ceiling-bearing known; extrapolate boat-speed constantly with bearing floorSpeed = floorSpeed1.getKnots(); } else if (floorSpeed1.equals(floorSpeed2)) { floorSpeed = floorSpeed1.getKnots(); @@ -457,8 +458,10 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { Bearing ceilingBearing1 = ceilingSpeeds.floorKey(relativeBearing); Bearing ceilingBearing2 = ceilingSpeeds.ceilingKey(relativeBearing); if (ceilingBearing1 == null) { + // for gps-polar: if no floor-bearing known; extrapolate boat-speed constantly with bearing ceilingSpeed = ceilingSpeed2.getKnots(); } else if (ceilingBearing2 == null) { + // for gps-polar: if no ceiling-bearing known; extrapolate boat-speed constantly with bearing ceilingSpeed = ceilingSpeed1.getKnots(); } else if (ceilingSpeed1.equals(ceilingSpeed2)) { ceilingSpeed = ceilingSpeed1.getKnots(); @@ -472,9 +475,11 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { } } if (floorSpeeds.size() == 0) { + // for gps-polar: if no boat-speeds known for floor-wind-speed, extrapolate linearly floorSpeed = ceilingSpeed * floorWind.getKnots() / ceilingWind.getKnots(); } if (ceilingSpeeds.size() == 0) { + // for gps-polar: if no boat-speeds known for ceiling-wind-speed, extrapolate linearly ceilingSpeed = floorSpeed * ceilingWind.getKnots() / floorWind.getKnots(); } @@ -689,33 +694,33 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { public Bearing[] optimalDirectionsDownwind() { Bearing windBearing = wind.getBearing().reverse(); - Bearing estGybeAngleRight = null; - Bearing estGybeAngleLeft = null; + Bearing estJibeAngleRight = null; + Bearing estJibeAngleLeft = null; if (getTargetDirection().equals(new DegreeBearingImpl(0))) { windBearing = wind.getBearing().reverse(); - estGybeAngleRight = null; - estGybeAngleLeft = null; - // lookup gybe-angles based on wind-values - Map.Entry floorEntry = gybeAngles.floorEntry(wind); - Bearing floorGybeAngle = null; + estJibeAngleRight = null; + estJibeAngleLeft = null; + // lookup jibe-angles based on wind-values + Map.Entry floorEntry = jibeAngles.floorEntry(wind); + Bearing floorJibeAngle = null; if (floorEntry != null) { - floorGybeAngle = floorEntry.getValue(); + floorJibeAngle = floorEntry.getValue(); } - Map.Entry ceilingEntry = gybeAngles.ceilingEntry(wind); - Bearing ceilingGybeAngle = null; + Map.Entry ceilingEntry = jibeAngles.ceilingEntry(wind); + Bearing ceilingJibeAngle = null; if (ceilingEntry != null) { - ceilingGybeAngle = ceilingEntry.getValue(); + ceilingJibeAngle = ceilingEntry.getValue(); } - // handle gybe-angles for out-of-definition-range wind-values - if (floorGybeAngle == null) { - floorGybeAngle = ceilingGybeAngle; + // handle jibe-angles for out-of-definition-range wind-values + if (floorJibeAngle == null) { + floorJibeAngle = ceilingJibeAngle; } - if (ceilingGybeAngle == null) { - ceilingGybeAngle = floorGybeAngle; + if (ceilingJibeAngle == null) { + ceilingJibeAngle = floorJibeAngle; } // lookup wind-support-values based on wind-values - Speed floorSpeed = gybeAngles.floorKey(wind); - Speed ceilingSpeed = gybeAngles.ceilingKey(wind); + Speed floorSpeed = jibeAngles.floorKey(wind); + Speed ceilingSpeed = jibeAngles.ceilingKey(wind); // handle out-of-definition-range wind-values if (floorSpeed == null) { floorSpeed = ceilingSpeed; @@ -723,21 +728,21 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { if (ceilingSpeed == null) { ceilingSpeed = floorSpeed; } - double gybeAngle; + double jibeAngle; if (floorSpeed.equals(ceilingSpeed)) { - gybeAngle = floorGybeAngle.getRadians(); + jibeAngle = floorJibeAngle.getRadians(); } else { - gybeAngle = floorGybeAngle.getRadians() + (wind.getKnots() - floorSpeed.getKnots()) - * (ceilingGybeAngle.getRadians() - floorGybeAngle.getRadians()) + jibeAngle = floorJibeAngle.getRadians() + (wind.getKnots() - floorSpeed.getKnots()) + * (ceilingJibeAngle.getRadians() - floorJibeAngle.getRadians()) / (ceilingSpeed.getKnots() - floorSpeed.getKnots()); } - // Bearing estGybeAngle = new RadianBearingImpl(gybeAngle); + // Bearing estJibeAngle = new RadianBearingImpl(jibeAngle); - double scaledGybeAngle = Math.PI-(Math.PI-gybeAngle)*this.scaleBearing; - estGybeAngleRight = windBearing.add(new RadianBearingImpl(+scaledGybeAngle)); - estGybeAngleLeft = windBearing.add(new RadianBearingImpl(-scaledGybeAngle)); + double scaledJibeAngle = Math.PI-(Math.PI-jibeAngle)*this.scaleBearing; + estJibeAngleRight = windBearing.add(new RadianBearingImpl(+scaledJibeAngle)); + estJibeAngleLeft = windBearing.add(new RadianBearingImpl(-scaledJibeAngle)); - return new Bearing[] { estGybeAngleRight, estGybeAngleLeft }; + return new Bearing[] { estJibeAngleRight, estJibeAngleLeft }; } else { @@ -758,17 +763,17 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { * Math.cos(b.getDifferenceTo(getTargetDirection().reverse()).getRadians()) > maxSpeedRight) { maxSpeedRight = getSpeedAtBearing(b).getKnots() * Math.cos(b.getDifferenceTo(getTargetDirection().reverse()).getRadians()); - estGybeAngleRight = b; + estJibeAngleRight = b; } } else if (getSpeedAtBearing(b).getKnots() * Math.cos(b.getDifferenceTo(getTargetDirection().reverse()).getRadians()) > maxSpeedLeft) { maxSpeedLeft = getSpeedAtBearing(b).getKnots() * Math.cos(b.getDifferenceTo(getTargetDirection().reverse()).getRadians()); - estGybeAngleLeft = b; + estJibeAngleLeft = b; } } - return new Bearing[] { estGybeAngleLeft, estGybeAngleRight }; + return new Bearing[] { estJibeAngleLeft, estJibeAngleRight }; } } @@ -796,8 +801,6 @@ public class PolarDiagramBase implements PolarDiagram, Serializable { return 4000; } - // TO BE REVIEWED - // not sure I use the right terms and conventions @Override public WindSide getWindSide(Bearing bearing) { WindSide windSide = null; diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramCSV.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramCSV.java index 8acd9fdb307..0becc2dee38 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramCSV.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramCSV.java @@ -27,12 +27,12 @@ public class PolarDiagramCSV extends PolarDiagramBase { InputStreamReader isr = new InputStreamReader(csvFile); BufferedReader bfr = new BufferedReader(isr); - List velocities = new ArrayList(); + List windSpeeds = new ArrayList(); List beatAngles = new ArrayList(); - List beatVMG = new ArrayList(); + List beatSpeed = new ArrayList(); Map> speeds = new HashMap>(); - List runVMG = new ArrayList(); - List gybeAngles = new ArrayList(); + List jibeSpeed = new ArrayList(); + List jibeAngles = new ArrayList(); String line = ""; String[] elements = null; @@ -47,9 +47,9 @@ public class PolarDiagramCSV extends PolarDiagramBase { elements[0] = elements[0].toLowerCase(); switch (elements[0]) { - case "windvelocity": + case "windspeed": for (int i = 1; i < elements.length; i++) { - velocities.add(new KnotSpeedImpl(new Double(elements[i]))); + windSpeeds.add(new KnotSpeedImpl(new Double(elements[i]))); } break; case "beatangles": @@ -59,17 +59,17 @@ public class PolarDiagramCSV extends PolarDiagramBase { break; case "beatsog": for (int i = 1; i < elements.length; i++) { - beatVMG.add(new KnotSpeedImpl(new Double(elements[i]))); + beatSpeed.add(new KnotSpeedImpl(new Double(elements[i]))); } break; - case "runsog": + case "jibesog": for (int i = 1; i < elements.length; i++) { - runVMG.add(new KnotSpeedImpl(new Double(elements[i]))); + jibeSpeed.add(new KnotSpeedImpl(new Double(elements[i]))); } break; - case "gybeangles": + case "jibeangles": for (int i = 1; i < elements.length; i++) { - gybeAngles.add(new DegreeBearingImpl(new Double(elements[i]))); + jibeAngles.add(new DegreeBearingImpl(new Double(elements[i]))); } break; default: @@ -92,16 +92,16 @@ public class PolarDiagramCSV extends PolarDiagramBase { NavigableMap> mapSpeedTable = new TreeMap>(); NavigableMap mapBeatAngles = new TreeMap(); - NavigableMap mapGybeAngles = new TreeMap(); + NavigableMap mapJibeAngles = new TreeMap(); NavigableMap mapBeatSOG = new TreeMap(); - NavigableMap mapGybeSOG = new TreeMap(); + NavigableMap mapJibeSOG = new TreeMap(); - Speed velocity = null; - Speed speed = null; + Speed windSpeed = null; + Speed boatSpeed = null; NavigableMap speedTableLine = null; - for (int index = 0; index < velocities.size(); index++) { - velocity = velocities.get(index); + for (int index = 0; index < windSpeeds.size(); index++) { + windSpeed = windSpeeds.get(index); speedTableLine = new TreeMap(bearingComparator); for (Entry> entry : speeds.entrySet()) { @@ -109,27 +109,27 @@ public class PolarDiagramCSV extends PolarDiagramBase { continue; } - speed = entry.getValue().get(index); + boatSpeed = entry.getValue().get(index); - if (speed != Speed.NULL) { - speedTableLine.put(entry.getKey(), speed); + if (boatSpeed != Speed.NULL) { + speedTableLine.put(entry.getKey(), boatSpeed); } } - mapSpeedTable.put(velocity, speedTableLine); - mapBeatAngles.put(velocity, beatAngles.get(index)); - mapGybeAngles.put(velocity, gybeAngles.get(index)); - mapBeatSOG.put(velocity, beatVMG.get(index)); - mapGybeSOG.put(velocity, runVMG.get(index)); + mapSpeedTable.put(windSpeed, speedTableLine); + mapBeatAngles.put(windSpeed, beatAngles.get(index)); + mapJibeAngles.put(windSpeed, jibeAngles.get(index)); + mapBeatSOG.put(windSpeed, beatSpeed.get(index)); + mapJibeSOG.put(windSpeed, jibeSpeed.get(index)); } //setWind(new KnotSpeedWithBearingImpl(0, new DegreeBearingImpl(180))); super.speedTable = mapSpeedTable; super.beatAngles = mapBeatAngles; - super.gybeAngles = mapGybeAngles; + super.jibeAngles = mapJibeAngles; super.beatSOG = mapBeatSOG; - super.gybeSOG = mapGybeSOG; + super.jibeSOG = mapJibeSOG; for (Speed s : super.speedTable.keySet()) { @@ -137,8 +137,8 @@ public class PolarDiagramCSV extends PolarDiagramBase { super.speedTable.get(s).put(super.beatAngles.get(s), super.beatSOG.get(s)); } - if (super.gybeAngles.containsKey(s) && !super.speedTable.get(s).containsKey(super.gybeAngles.get(s))) { - super.speedTable.get(s).put(super.gybeAngles.get(s), super.gybeSOG.get(s)); + if (super.jibeAngles.containsKey(s) && !super.speedTable.get(s).containsKey(super.jibeAngles.get(s))) { + super.speedTable.get(s).put(super.jibeAngles.get(s), super.jibeSOG.get(s)); } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java index aa235ba117f..45b504952ce 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java @@ -28,80 +28,78 @@ public class PolarDiagramGPS extends PolarDiagramBase { this.boatClass = boatClass; this.polarData = polarData; - List velocities = new ArrayList(); + List windSpeeds = new ArrayList(); List beatAngles = new ArrayList(); - List beatVMG = new ArrayList(); + List beatSpeed = new ArrayList(); Map> speeds = new HashMap>(); - List runVMG = new ArrayList(); - List gybeAngles = new ArrayList(); + List jibeSpeed = new ArrayList(); + List jibeAngles = new ArrayList(); // initialize wind speeds - velocities.add(new KnotSpeedImpl(0.0)); - velocities.add(new KnotSpeedImpl(6.0)); - velocities.add(new KnotSpeedImpl(8.0)); - velocities.add(new KnotSpeedImpl(10.0)); - velocities.add(new KnotSpeedImpl(12.0)); - velocities.add(new KnotSpeedImpl(14.0)); - velocities.add(new KnotSpeedImpl(16.0)); - velocities.add(new KnotSpeedImpl(20.0)); + windSpeeds.add(new KnotSpeedImpl(0.0)); + windSpeeds.add(new KnotSpeedImpl(6.0)); + windSpeeds.add(new KnotSpeedImpl(8.0)); + windSpeeds.add(new KnotSpeedImpl(10.0)); + windSpeeds.add(new KnotSpeedImpl(12.0)); + windSpeeds.add(new KnotSpeedImpl(14.0)); + windSpeeds.add(new KnotSpeedImpl(16.0)); + windSpeeds.add(new KnotSpeedImpl(20.0)); // initialize beat-angles and -speeds //SpeedWithBearing beatPort; SpeedWithBearing beatStar; - for (int i = 0; i < velocities.size(); i++) { + for (int i = 0; i < windSpeeds.size(); i++) { try { - //beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, velocities.get(i), LegType.UPWIND, Tack.PORT).getObject(); - beatStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, velocities.get(i), LegType.UPWIND, Tack.STARBOARD).getObject(); + //beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.UPWIND, Tack.PORT).getObject(); + beatStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.UPWIND, Tack.STARBOARD).getObject(); } catch (NotEnoughDataHasBeenAddedException e) { //beatPort = null; beatStar = null; } if (beatStar != null) { beatAngles.add(beatStar.getBearing()); - beatVMG.add(beatStar); + beatSpeed.add(beatStar); } else { beatAngles.add(null); - beatVMG.add(null); + beatSpeed.add(null); } } // initialize jibe-angles and -speeds //SpeedWithBearing jibePort; SpeedWithBearing jibeStar; - for (int i = 0; i < velocities.size(); i++) { + for (int i = 0; i < windSpeeds.size(); i++) { try { - //jibePort = this.polarData.getAverageSpeedWithBearing(this.boatClass, velocities.get(i), LegType.DOWNWIND, Tack.PORT).getObject(); - jibeStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, velocities.get(i), LegType.DOWNWIND, Tack.STARBOARD).getObject(); + //jibePort = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.DOWNWIND, Tack.PORT).getObject(); + jibeStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.DOWNWIND, Tack.STARBOARD).getObject(); } catch (NotEnoughDataHasBeenAddedException e) { - // TODO Auto-generated catch block - //e.printStackTrace(); //jibePort = null; jibeStar = null; } if (jibeStar != null) { - gybeAngles.add(jibeStar.getBearing()); - runVMG.add(jibeStar); + jibeAngles.add(jibeStar.getBearing()); + jibeSpeed.add(jibeStar); } else { - gybeAngles.add(null); - runVMG.add(null); + jibeAngles.add(null); + jibeSpeed.add(null); } } NavigableMap> mapSpeedTable = new TreeMap>(); NavigableMap mapBeatAngles = new TreeMap(); - NavigableMap mapGybeAngles = new TreeMap(); + NavigableMap mapJibeAngles = new TreeMap(); NavigableMap mapBeatSOG = new TreeMap(); - NavigableMap mapGybeSOG = new TreeMap(); + NavigableMap mapJibeSOG = new TreeMap(); - Speed velocity = null; - Speed speed = null; + Speed windSpeed = null; + Speed boatSpeed = null; NavigableMap speedTableLine = null; - for (int index = 0; index < velocities.size(); index++) { - velocity = velocities.get(index); - if (velocity.getKnots() == 0.0) { + for (int index = 0; index < windSpeeds.size(); index++) { + windSpeed = windSpeeds.get(index); + if (windSpeed.getKnots() == 0.0) { mapBeatSOG.put(new KnotSpeedImpl(0.0), new KnotSpeedImpl(0.0)); - mapGybeSOG.put(new KnotSpeedImpl(0.0), new KnotSpeedImpl(0.0)); + mapJibeSOG.put(new KnotSpeedImpl(0.0), new KnotSpeedImpl(0.0)); } speedTableLine = new TreeMap(bearingComparator); for (Entry> entry : speeds.entrySet()) { @@ -109,33 +107,33 @@ public class PolarDiagramGPS extends PolarDiagramBase { continue; } - speed = entry.getValue().get(index); + boatSpeed = entry.getValue().get(index); - if (speed != Speed.NULL) { - speedTableLine.put(entry.getKey(), speed); + if (boatSpeed != Speed.NULL) { + speedTableLine.put(entry.getKey(), boatSpeed); } } - mapSpeedTable.put(velocity, speedTableLine); + mapSpeedTable.put(windSpeed, speedTableLine); if (beatAngles.get(index) != null) { - mapBeatAngles.put(velocity, beatAngles.get(index)); + mapBeatAngles.put(windSpeed, beatAngles.get(index)); } - if (gybeAngles.get(index) != null) { - mapGybeAngles.put(velocity, gybeAngles.get(index)); + if (jibeAngles.get(index) != null) { + mapJibeAngles.put(windSpeed, jibeAngles.get(index)); } - if (beatVMG.get(index) != null) { - mapBeatSOG.put(velocity, beatVMG.get(index)); + if (beatSpeed.get(index) != null) { + mapBeatSOG.put(windSpeed, beatSpeed.get(index)); } - if (runVMG.get(index) != null) { - mapGybeSOG.put(velocity, runVMG.get(index)); + if (jibeSpeed.get(index) != null) { + mapJibeSOG.put(windSpeed, jibeSpeed.get(index)); } } super.speedTable = mapSpeedTable; super.beatAngles = mapBeatAngles; - super.gybeAngles = mapGybeAngles; + super.jibeAngles = mapJibeAngles; super.beatSOG = mapBeatSOG; - super.gybeSOG = mapGybeSOG; + super.jibeSOG = mapJibeSOG; for (Speed s : super.speedTable.keySet()) { @@ -143,8 +141,8 @@ public class PolarDiagramGPS extends PolarDiagramBase { super.speedTable.get(s).put(super.beatAngles.get(s), super.beatSOG.get(s)); } - if (super.gybeAngles.containsKey(s) && !super.speedTable.get(s).containsKey(super.gybeAngles.get(s))) { - super.speedTable.get(s).put(super.gybeAngles.get(s), super.gybeSOG.get(s)); + if (super.jibeAngles.containsKey(s) && !super.speedTable.get(s).containsKey(super.jibeAngles.get(s))) { + super.speedTable.get(s).put(super.jibeAngles.get(s), super.jibeSOG.get(s)); } } diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSRaw.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSDirect.java similarity index 95% rename from java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSRaw.java rename to java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSDirect.java index ddae39a018e..0b3d5b13806 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSRaw.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSDirect.java @@ -15,7 +15,7 @@ import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sailing.simulator.PolarDiagram; -public class PolarDiagramGPSRaw implements PolarDiagram, Serializable { +public class PolarDiagramGPSDirect implements PolarDiagram, Serializable { private static final long serialVersionUID = -9219705955440602679L; private final BoatClass boatClass; @@ -26,7 +26,7 @@ public class PolarDiagramGPSRaw implements PolarDiagram, Serializable { private SpeedWithBearing jibePort = null; private SpeedWithBearing jibeStar = null; - public PolarDiagramGPSRaw(BoatClass boatClass, PolarDataService polarData) { + public PolarDiagramGPSDirect(BoatClass boatClass, PolarDataService polarData) { this.boatClass = boatClass; this.polarData = polarData; } @@ -212,7 +212,7 @@ public class PolarDiagramGPSRaw implements PolarDiagram, Serializable { } @Override - public NavigableMap getGybeAngles() { + public NavigableMap getJibeAngles() { // TODO Auto-generated method stub return null; } @@ -224,7 +224,7 @@ public class PolarDiagramGPSRaw implements PolarDiagram, Serializable { } @Override - public NavigableMap getGybeSOG() { + public NavigableMap getJibeSOG() { // TODO Auto-generated method stub return null; } From b1d0ee55199c215f1b2b0be8bb6d941209acb103 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 12 Nov 2014 16:08:18 +0100 Subject: [PATCH 66/72] Optimized polar data service average methods --- .../com/sap/sailing/polars/PolarDataService.java | 2 ++ .../analysis/impl/PolarSheetAnalyzerImpl.java | 6 +----- .../sailing/polars/impl/PolarDataServiceImpl.java | 5 +++++ .../sap/sailing/polars/mining/PolarDataMiner.java | 15 +++++++++++++++ 4 files changed, 23 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index cc9251f78cd..07311967904 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -115,6 +115,8 @@ public interface PolarDataService { Set getAllBoatClassesWithPolarSheetsAvailable(); void competitorPositionChanged(GPSFixMoving fix, Competitor competitor, TrackedRace createdTrackedRace); + + Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java index a12273fd7ab..a253f90fe7c 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java @@ -9,7 +9,6 @@ import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; -import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; @@ -111,10 +110,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } private Integer[] getDataCountArray(BoatClass boatClass, Speed windSpeed) { - PolarSheetsData sheet = polarDataService.getPolarSheetForBoatClass(boatClass); - - int windIndex = sheet.getStepping().getLevelIndexForValue(windSpeed.getKnots()); - Integer[] dataCountPerAngle = sheet.getDataCountPerAngleForWindspeed(windIndex); + Integer[] dataCountPerAngle = polarDataService.getDataCountsForWindSpeed(boatClass, windSpeed); return dataCountPerAngle; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 198b648345c..19225a1ec44 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -154,6 +154,11 @@ public class PolarDataServiceImpl implements PolarDataService { } + @Override + public Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed) { + return polarDataMiner.getDataCountsForWindSpeed(boatClass, windSpeed); + } + // private Map>> createIntervalSpecification( // TrackedRace createdTrackedRace, Competitor competitor, TimePoint timePoint) { // HashMap>> result = new HashMap Date: Wed, 12 Nov 2014 17:00:51 +0100 Subject: [PATCH 67/72] Further performace fixes for average polar service methods --- .../sap/sailing/polars/PolarDataService.java | 10 +++++- .../analysis/impl/PolarSheetAnalyzerImpl.java | 6 ++-- .../polars/impl/PolarDataServiceImpl.java | 4 +-- .../sailing/polars/mining/PolarDataMiner.java | 31 +++++++++++++------ 4 files changed, 35 insertions(+), 16 deletions(-) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java index 07311967904..aca9c3a0271 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/PolarDataService.java @@ -116,7 +116,15 @@ public interface PolarDataService { void competitorPositionChanged(GPSFixMoving fix, Competitor competitor, TrackedRace createdTrackedRace); - Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed); + /** + * Returns underlying datacount for a given boat class and windspeed. + * @param boatClass + * @param windSpeed + * @param startAngleInclusive between 0 and 359; smaller than (or equal to) endAngleExclusive + * @param endAngleExclusive between 0 and 359; bigger than startAngleInclusive + * @return array with datacount for all angles in the given area, else null + */ + Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java index a253f90fe7c..d0b8f6c5fc7 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/analysis/impl/PolarSheetAnalyzerImpl.java @@ -75,7 +75,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { private SpeedWithBearingWithConfidence estimateAnglePeakAndAverageSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) throws NotEnoughDataHasBeenAddedException { - Integer[] dataCountPerAngle = getDataCountArray(boatClass, windSpeed); + Integer[] dataCountPerAngle = getDataCountArray(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); @@ -109,8 +109,8 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { return confidence; } - private Integer[] getDataCountArray(BoatClass boatClass, Speed windSpeed) { - Integer[] dataCountPerAngle = polarDataService.getDataCountsForWindSpeed(boatClass, windSpeed); + private Integer[] getDataCountArray(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) { + Integer[] dataCountPerAngle = polarDataService.getDataCountsForWindSpeed(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); return dataCountPerAngle; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 19225a1ec44..2f7544b4944 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -155,8 +155,8 @@ public class PolarDataServiceImpl implements PolarDataService { } @Override - public Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed) { - return polarDataMiner.getDataCountsForWindSpeed(boatClass, windSpeed); + public Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) { + return polarDataMiner.getDataCountsForWindSpeed(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); } // private Map>> createIntervalSpecification( diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index cce8c112731..49bceae8cf5 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -132,10 +132,7 @@ public class PolarDataMiner { for (int angle = 0; angle < 360; angle++) { Pair, Integer> speedWithConfidenceAndDataCount; try { - int convertedAngle = angle; - if (angle > 180) { - convertedAngle = angle - 360; - } + int convertedAngle = convertAngleIfNecessary(angle); speedWithConfidenceAndDataCount = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); } catch (NotEnoughDataHasBeenAddedException e) { @@ -168,6 +165,16 @@ public class PolarDataMiner { + private int convertAngleIfNecessary(int angle) { + int convertedAngle = angle; + if (angle > 180) { + convertedAngle = angle - 360; + } + return convertedAngle; + } + + + private PolarSheetsHistogramDataImpl createEmptyHistogramData(Integer[] perAngle, int angle, int dataCount, double coefficiantOfVariation, double confidenceMeasure) { perAngle[angle] = dataCount; @@ -193,14 +200,18 @@ public class PolarDataMiner { - public Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed) { + public Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) { Integer[] dataCounts = new Integer[360]; for (int angle = 0; angle < 360; angle++) { - try { - dataCounts[angle] = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, - new DegreeBearingImpl(angle)).getB(); - } catch (NotEnoughDataHasBeenAddedException e) { - dataCounts[angle] = 0; + if (angle >= startAngleInclusive && angle < endAngleExclusive) { + try { + dataCounts[angle] = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, + new DegreeBearingImpl(convertAngleIfNecessary(angle))).getB(); + } catch (NotEnoughDataHasBeenAddedException e) { + dataCounts[angle] = 0; + } + } else { + dataCounts[angle] = null; } } return dataCounts; From d09675bc9a83f921fd1c7e0008c88e4fbf9f0473 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 12 Nov 2014 17:19:41 +0100 Subject: [PATCH 68/72] Fixed test to comply with performance fixes --- .../polars/test/PolarSheetAnalyzerTest.java | 107 +----------------- 1 file changed, 6 insertions(+), 101 deletions(-) diff --git a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java index d9be2309eff..37a526f0fd3 100644 --- a/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java +++ b/java/com.sap.sailing.polars.test/src/com/sap/sailing/polars/test/PolarSheetAnalyzerTest.java @@ -7,9 +7,6 @@ import static org.mockito.Matchers.argThat; import static org.mockito.Mockito.mock; import static org.mockito.Mockito.when; -import java.util.HashMap; -import java.util.Map; - import org.junit.Test; import org.mockito.ArgumentMatcher; @@ -17,12 +14,8 @@ import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; -import com.sap.sailing.domain.common.PolarSheetGenerationSettings; -import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.impl.KnotSpeedImpl; -import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; -import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.analysis.PolarSheetAnalyzer; import com.sap.sailing.polars.analysis.impl.PolarSheetAnalyzerImpl; @@ -64,9 +57,10 @@ public class PolarSheetAnalyzerTest { private PolarDataService createMockedPolarDataService() throws NotEnoughDataHasBeenAddedException { PolarDataService mockedPolarDataService = mock(PolarDataService.class); - PolarSheetsData data = createPolarSheetsData(); - when(mockedPolarDataService.getPolarSheetForBoatClass(any(BoatClass.class))).thenReturn(data); - + Integer[] data = createCounts(); + when(mockedPolarDataService.getDataCountsForWindSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), + any(Integer.class), any(Integer.class))).thenReturn(data); + //Starboard Upwind when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), argThat(new BearingMatcher(49)))).thenReturn( @@ -122,93 +116,7 @@ public class PolarSheetAnalyzerTest { * * @return */ - private PolarSheetsData createPolarSheetsData() { - PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); - Number[][] averagedPolarDataByWindSpeed = { - {}, - {}, - {}, - {}, - {}, - { 0.0, 0.0, 0.35653425045212994, 0.0, 0.20934280998529808, 0.0, 0.0, 0.0, 0.3145227273331494, 0.0, - 0.5123377765857685, 0.0, 0.0, 0.0, 0.3910305177324864, 0.01848543712702339, 74.14368617329595, - 0.19873208170945428, 1.7877762410964606, 13.813130768721209, 10.642219209509047, - 13.401166322502394, 8.369967315755705, 8.612484953161221, 6.830752063578309, 6.731226410756168, - 7.534989526402173, 7.417304789344726, 7.689655016976699, 7.856820838665987, 7.877814207628512, - 7.686942654891556, 7.94739600490786, 7.250053609589324, 7.658903932314423, 8.264104048379547, - 7.97995749658058, 8.328973177890093, 8.445018706583248, 8.391458379377287, 8.316439838911991, - 8.308509782296186, 8.41538252133265, 8.485027362920015, 8.301702107250204, 8.419623807296604, - 8.624320785824453, 8.459533164762528, 8.563247204808349, 8.478048671702888, 8.466303997538812, - 8.583383077026435, 8.459935549408716, 8.536070928645852, 8.484085598981617, 8.354759109745252, - 8.273057288860901, 8.234006491204791, 7.8843340092591445, 7.832604832601752, 7.752662035724477, - 7.383456897289133, 6.821527524231923, 6.316822983662531, 5.94010223470201, 5.778283414530163, - 5.288261984314158, 5.0632678301953415, 4.575991234971232, 4.4246483551331615, - 3.754104138232388, 3.574202962043522, 2.809782370429744, 2.3756561838866554, - 1.4733065799328493, 1.4254216934512096, 2.3480707715085587, 2.2261678547163744, - 1.3404842722592116, 1.3284863115290033, 1.2125511969957223, 1.6516010763946074, - 1.3858564062376648, 1.206826186297077, 1.3739210408131197, 1.484483916721746, 1.16345069673396, - 1.030671321957646, 1.0099211183312304, 1.128789637547173, 1.3136200715041277, - 1.0889100793268662, 1.619317841041691, 1.3193441080791892, 1.0031422557092482, - 0.7919668067046017, 0.9306316555528391, 0.8006819358007504, 1.4692741310417397, - 1.0078551872416406, 0.18256090691178703, 1.0285132794809515, 0.6438471218131188, - 0.6380303949260124, 0.664014588165121, 0.7808063306582489, 0.7898512748200681, - 0.6511549225287516, 0.6376995913182613, 0.7570886726617079, 0.6619740768653626, - 0.7632028965886186, 0.6338755107017753, 0.6640484201429284, 2.127282972819131, - 2.8987479648532997, 6.683424580288278, 8.507014641722314, 7.703364841210107, 4.71357814053702, - 8.000825810341617, 7.671441013221641, 9.115713955723916, 9.023412736773132, 7.998471846760262, - 10.3966884832781, 9.795531845297319, 8.84626879400292, 10.7522343414125, 12.416303781075975, - 11.722882328824802, 11.955629251917987, 13.323328215681695, 12.902836778810396, - 12.82088099069422, 13.274245192043882, 13.06739796258905, 13.047121734187872, - 13.442188461750433, 13.332939764570671, 13.558632900491695, 13.794354053931528, - 13.665782579628802, 14.169301888320263, 13.974157665863203, 14.222780950415583, - 14.47187020132089, 14.341301813979271, 14.194209653738831, 14.344773681878142, - 13.962146190803251, 14.11383996792033, 14.281588010608605, 14.603553724187172, - 14.762753353002006, 14.123456040381438, 15.046630203667128, 14.39273398895829, - 14.224594624139609, 15.655628777674853, 15.21593321294868, 13.558581393009002, - 11.63031430546637, 11.885139926443486, 8.090192790566025, 8.85949813118447, 9.734779186465921, - 9.822728590064322, 9.45181850091312, 12.306111307584684, 13.943542199831565, - 11.610604465928457, 12.093955433640602, 12.352243834497187, 5.509081709082437, - 9.386182259798517, 11.796573155778503, 10.752292039895153, 11.909890662822576, - 10.116169101754338, 12.694529106484943, 12.670730206147631, 10.379478057323539, - 12.672626215427158, 10.724408451389763, 10.087325562474476, 6.243751559092033, - 10.222427424506762, 10.02529936912795, 11.0353088918245, 11.096075421534366, - 11.682256641020984, 10.803765317446135, 11.21085498256219, 12.986416296517614, - 13.6093151775165, 11.851727180875798, 15.451306097090033, 12.382813246378253, - 14.86900369646574, 14.349497782030753, 13.368463669152609, 14.914498105361215, - 13.627605853093092, 14.511034781204497, 15.204877751897692, 13.56324380463669, - 13.796723457212012, 14.126664346993518, 14.083851682454043, 13.984023085926546, - 13.77727116288626, 13.924407601290493, 14.167888563062107, 13.969213312748776, - 13.78894715705271, 13.420656294986587, 13.268607457651942, 13.107087350500771, - 12.968302178632449, 12.900886466337163, 12.63727656702256, 13.324437322806876, - 12.447444273895949, 11.929720685469128, 11.866720727517666, 11.845857431417869, - 12.34750950745547, 11.82999403657738, 11.56808623971416, 11.005790647087728, - 11.199641554710265, 11.147034585879346, 9.581506313873323, 9.377505334232003, - 9.165927874941573, 9.039110322467469, 9.158860532503148, 8.335083032404867, 7.794601241888998, - 8.095394521682454, 5.288448397940189, 5.032462558577374, 6.0372815329868175, 5.550230684686165, - 5.469127647370254, 7.712573013931298, 6.905684954724151, 6.244400641700857, 4.733792287830745, - 5.703038182173567, 5.705814871750967, 3.6710816576685543, 4.290237091844638, - 3.9287689933642715, 3.8420701003482005, 4.049827829086237, 3.2238964501634513, - 3.153176378342545, 2.7186371105079665, 4.5320344683521725, 6.205007707190399, - 0.9290805079838338, 5.461334397488564, 0.11769821370235434, 4.821232524035565, - 6.535593439138594, 1.430640812116557, 1.6028097505376921, 2.444286329582734, 3.432921232002375, - 1.5524241414822946, 4.463117209069594, 9.233008385665372, 6.701689021468631, 6.091887266547403, - 6.736705820169654, 6.638753611795072, 8.315606347683866, 7.192407235278191, 7.065178905446558, - 7.340556868387216, 7.8140170631317565, 8.490879454652937, 8.869186790970112, 8.089554910312003, - 8.773712136668893, 8.48502143463395, 8.506940480879672, 8.426924203848873, 8.470754959099478, - 8.34341243311524, 8.812850540132228, 8.471103442329234, 8.95942249226152, 8.368125204092756, - 8.101176539059828, 8.536922798672386, 8.039899341914161, 8.66061693085369, 8.522478004005418, - 8.354071503807528, 8.299401288177423, 8.308870759000826, 8.382553283761265, 8.508054693539464, - 8.508341232737465, 8.416280733708618, 8.624730989938895, 8.445599410456111, 8.553274292235153, - 8.614582090896583, 8.619979792865113, 8.497641411226578, 8.59426371914717, 8.638910090156958, - 8.720583873644639, 8.582290408559516, 8.508363936388186, 8.555021199411986, 8.575018051008957, - 8.415002407687826, 8.570989437948075, 8.47499610688996, 8.153991578948714, 8.20568750487526, - 8.16792006664647, 8.035532991444567, 8.374627467796898, 8.53819686502552, 7.998112484617343, - 5.001488152415163, 4.248045472089586, 8.06284784348518, 6.586154602284839, 9.171084517442075, - 9.047519598278601, 8.290140115687464, 6.627513257025532, 9.118867780171255, 0.0, - 0.15031820068279983, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.0, 0.19624692515939246, 0.0, 0.0, 0.0, - 0.6993196184976416, 0.0, 0.0, 0.0, 0.0, 0.0, 0.37664203710504496 }, {}, {}, {}, {} }; - - Map dataCountsPerAngleAndWindSpeed = new HashMap<>(); + private Integer[] createCounts() { Integer[] dataCountsPerAngle = { 0, 0, 2, 0, 1, 0, 0, 0, 1, 0, 1, 0, 0, 0, 3, 1, 2, 1, 6, 2, 2, 2, 1, 6, 6, 17, 13, 19, 30, 43, 32, 50, 48, 57, 70, 76, 107, 129, 177, 186, 222, 237, 275, 285, 321, 322, 401, 451, 466, 413, 468, 479, 459, 429, 360, 345, 317, 288, 278, 202, 175, 130, 113, 104, 100, 73, 45, 51, 52, @@ -223,10 +131,7 @@ public class PolarSheetAnalyzerTest { 69, 68, 79, 62, 66, 86, 72, 84, 89, 75, 73, 62, 104, 110, 147, 137, 170, 204, 310, 347, 354, 377, 433, 457, 419, 436, 467, 502, 513, 525, 463, 422, 405, 363, 309, 282, 262, 186, 162, 123, 97, 81, 45, 35, 30, 24, 20, 12, 9, 8, 5, 6, 4, 6, 7, 2, 0, 1, 0, 0, 0, 0, 0, 0, 0, 1, 0, 0, 0, 2, 0, 0, 0, 0, 0, 1 }; - dataCountsPerAngleAndWindSpeed.put(5, dataCountsPerAngle); - PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 50000, - dataCountsPerAngleAndWindSpeed, settings.getWindStepping(), null); - return data; + return dataCountsPerAngle; } private class SpeedMatcher extends ArgumentMatcher { From 9ba768cbf0e2f3099342333390e5faebe1adc08d Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 12 Nov 2014 18:57:01 +0100 Subject: [PATCH 69/72] fix segmentation of paths in equitemporal steps; --- .../src/com/sap/sailing/simulator/impl/PathImpl.java | 6 +++++- 1 file changed, 5 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathImpl.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathImpl.java index a202bda9a4e..670e383fef6 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathImpl.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathImpl.java @@ -74,8 +74,12 @@ public class PathImpl implements Path, Serializable { int idx = 1; while (idx < this.pathPoints.size()) { - if (this.pathPoints.get(idx).getTimePoint().asMillis() >= nextTimePoint.asMillis()) { + if ((this.pathPoints.get(idx).getTimePoint().asMillis() >= nextTimePoint.asMillis())||(idx == this.pathPoints.size()-1)) { + if (idx == this.pathPoints.size()-1) { + nextTimePoint = endTime; + } + // reached point after next timestep TimedPositionWithSpeed p1 = this.pathPoints.get(idx - 1); TimedPositionWithSpeed p2 = this.pathPoints.get(idx); From 2fb90c85192580085baad1573b936c01a9f6ea8d Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 12 Nov 2014 19:10:52 +0100 Subject: [PATCH 70/72] fix final path-position of tree-grow; --- .../sap/sailing/simulator/impl/PathGeneratorTreeGrow.java | 8 +++++--- 1 file changed, 5 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java index 0e6fd5af919..fa822c5c335 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PathGeneratorTreeGrow.java @@ -685,6 +685,7 @@ public class PathGeneratorTreeGrow extends PathGeneratorBase { // generate intermediate steps bestCand = trgPaths.get(0); // target-path ending closest to target + long endTime = bestCand.pos.getTimePoint().asMillis(); TimedPositionWithSpeed curPosition = null; char nextDirection = '0'; char prevDirection = '0'; @@ -702,9 +703,10 @@ public class PathGeneratorTreeGrow extends PathGeneratorBase { boolean sameBaseDirection = this.isSameDirection(prevDirection, nextDirection); Wind curWind = wf.getWind(curPosition); TimedPosition newPosition = this.getStep(curPosition, curWind, usedTimeStep, turnLoss, sameBaseDirection, nextDirection); - curPosition = new TimedPositionWithSpeedImpl(newPosition.getTimePoint(), newPosition.getPosition(), null); - path.add(curPosition); - + if (newPosition.getTimePoint().asMillis() < endTime) { + curPosition = new TimedPositionWithSpeedImpl(newPosition.getTimePoint(), newPosition.getPosition(), null); + path.add(curPosition); + } } prevDirection = nextDirection; From 1ff17635759450f1f53d7c08d1fdbe421fd4972e Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Wed, 12 Nov 2014 19:34:18 +0100 Subject: [PATCH 71/72] evaluate hasPolar() on race-map based on gps-polar-data; thereby generalize raceboard-simulation to all boat-classes; --- .../sailing/gwt/ui/server/SailingServiceImpl.java | 14 ++------------ 1 file changed, 2 insertions(+), 12 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 9dc4c241377..fe7ef0be8b6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -392,7 +392,6 @@ import com.sap.sailing.server.replication.ReplicationService; import com.sap.sailing.server.replication.impl.ReplicaDescriptor; import com.sap.sailing.simulator.Path; import com.sap.sailing.simulator.PolarDiagram; -import com.sap.sailing.simulator.PolarDiagramFactory; import com.sap.sailing.simulator.SailingSimulator; import com.sap.sailing.simulator.SimulationParameters; import com.sap.sailing.simulator.TimedPositionWithSpeed; @@ -440,8 +439,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S private final ServiceTracker deviceIdentifierStringSerializationHandlerTracker; - - private final ServiceTracker polarDiagramService; private final com.sap.sailing.domain.tractracadapter.persistence.MongoObjectFactory tractracMongoObjectFactory; @@ -490,7 +487,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S raceLogTrackingAdapterTracker = createAndOpenRaceLogTrackingAdapterTracker(context); deviceIdentifierStringSerializationHandlerTracker = createAndOpenDeviceIdentifierStringSerializationHandlerTracker(context); igtimiAdapterTracker = createAndOpenIgtimiTracker(context); - polarDiagramService = createAndOpenPolarDiagramService(context); baseDomainFactory = getService().getBaseDomainFactory(); mongoObjectFactory = getService().getMongoObjectFactory(); domainObjectFactory = getService().getDomainObjectFactory(); @@ -587,13 +583,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return result; } - protected ServiceTracker createAndOpenPolarDiagramService(BundleContext context) { - ServiceTracker result = new ServiceTracker( - context, PolarDiagramFactory.class.getName(), null); - result.open(); - return result; - } - private void writeObject(ObjectOutputStream oos) throws IOException { oos.defaultWriteObject(); } @@ -1473,7 +1462,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public Boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier) { TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); BoatClass boatClass = trackedRace.getRace().getBoatClass(); - PolarDiagram polarDiagram = polarDiagramService.getService().getPolarDiagram(boatClass); + PolarDataService polarData = getService().getPolarDataService(); + PolarDiagram polarDiagram = new PolarDiagramGPS(boatClass, polarData); return new Boolean(polarDiagram != null); }; From 516f286def4055e32544446dc5b0e9b6e7c28bee Mon Sep 17 00:00:00 2001 From: "Christopher Ronnewinkel (D036654)" Date: Thu, 13 Nov 2014 16:20:58 +0100 Subject: [PATCH 72/72] fix gps-polar-diagram direct implementation; average port/star angles & speeds in gps-polar-diagram interpolated implementation; --- .../simulator/impl/PolarDiagramGPS.java | 25 +++++++++++-------- .../simulator/impl/PolarDiagramGPSDirect.java | 12 ++++++--- 2 files changed, 23 insertions(+), 14 deletions(-) diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java index 45b504952ce..3c60ef1b59c 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java @@ -14,6 +14,7 @@ import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; 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.polars.PolarDataService; import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; @@ -46,19 +47,21 @@ public class PolarDiagramGPS extends PolarDiagramBase { windSpeeds.add(new KnotSpeedImpl(20.0)); // initialize beat-angles and -speeds - //SpeedWithBearing beatPort; + SpeedWithBearing beatPort; SpeedWithBearing beatStar; for (int i = 0; i < windSpeeds.size(); i++) { try { - //beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.UPWIND, Tack.PORT).getObject(); + beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.UPWIND, Tack.PORT).getObject(); beatStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.UPWIND, Tack.STARBOARD).getObject(); } catch (NotEnoughDataHasBeenAddedException e) { - //beatPort = null; + beatPort = null; beatStar = null; } if (beatStar != null) { - beatAngles.add(beatStar.getBearing()); - beatSpeed.add(beatStar); + Bearing avgBeatAngle = new DegreeBearingImpl((beatStar.getBearing().getDegrees() + (360-beatPort.getBearing().getDegrees())%360)/2.0); + beatAngles.add(avgBeatAngle); + Speed avgBeatSpeed = new KnotSpeedImpl((beatStar.getKnots() + beatPort.getKnots())/2.0); + beatSpeed.add(avgBeatSpeed); } else { beatAngles.add(null); beatSpeed.add(null); @@ -66,19 +69,21 @@ public class PolarDiagramGPS extends PolarDiagramBase { } // initialize jibe-angles and -speeds - //SpeedWithBearing jibePort; + SpeedWithBearing jibePort; SpeedWithBearing jibeStar; for (int i = 0; i < windSpeeds.size(); i++) { try { - //jibePort = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.DOWNWIND, Tack.PORT).getObject(); + jibePort = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.DOWNWIND, Tack.PORT).getObject(); jibeStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, windSpeeds.get(i), LegType.DOWNWIND, Tack.STARBOARD).getObject(); } catch (NotEnoughDataHasBeenAddedException e) { - //jibePort = null; + jibePort = null; jibeStar = null; } if (jibeStar != null) { - jibeAngles.add(jibeStar.getBearing()); - jibeSpeed.add(jibeStar); + Bearing avgJibeAngle = new DegreeBearingImpl((jibeStar.getBearing().getDegrees() + (360-jibePort.getBearing().getDegrees())%360)/2.0); + jibeAngles.add(avgJibeAngle); + Speed avgJibeSpeed = new KnotSpeedImpl((jibeStar.getKnots() + jibePort.getKnots())/2.0); + jibeSpeed.add(avgJibeSpeed); } else { jibeAngles.add(null); jibeSpeed.add(null); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSDirect.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSDirect.java index 0b3d5b13806..03057d9a728 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSDirect.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPSDirect.java @@ -131,10 +131,12 @@ public class PolarDiagramGPSDirect implements PolarDiagram, Serializable { public Bearing[] optimalDirectionsUpwind() { try { if (beatPort == null) { - beatPort = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.PORT).getObject(); + SpeedWithBearing beatPortRelative = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.PORT).getObject(); + beatPort = new KnotSpeedWithBearingImpl(beatPortRelative.getKnots(), this.wind.getBearing().reverse().add(beatPortRelative.getBearing())); } if (beatStar == null) { - beatStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.STARBOARD).getObject(); + SpeedWithBearing beatStarRelative = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.UPWIND, Tack.STARBOARD).getObject(); + beatStar = new KnotSpeedWithBearingImpl(beatStarRelative.getKnots(), this.wind.getBearing().reverse().add(beatStarRelative.getBearing())); } } catch (NotEnoughDataHasBeenAddedException e) { // TODO Auto-generated catch block @@ -154,10 +156,12 @@ public class PolarDiagramGPSDirect implements PolarDiagram, Serializable { public Bearing[] optimalDirectionsDownwind() { try { if (jibePort ==null) { - jibePort = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.PORT).getObject(); + SpeedWithBearing jibePortRelative = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.PORT).getObject(); + jibePort = new KnotSpeedWithBearingImpl(jibePortRelative.getKnots(), this.wind.getBearing().reverse().add(jibePortRelative.getBearing())); } if (jibeStar == null) { - jibeStar = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.STARBOARD).getObject(); + SpeedWithBearing jibeStarRelative = this.polarData.getAverageSpeedWithBearing(this.boatClass, wind, LegType.DOWNWIND, Tack.STARBOARD).getObject(); + jibeStar = new KnotSpeedWithBearingImpl(jibeStarRelative.getKnots(), this.wind.getBearing().reverse().add(jibeStarRelative.getBearing())); } } catch (NotEnoughDataHasBeenAddedException e) { // TODO Auto-generated catch block