From 3534be67369bf79ea2bcc4b03b2e5b1a66a8a8de Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 4 Dec 2013 15:18:25 +0100 Subject: [PATCH 001/103] 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 002/103] 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 003/103] 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 004/103] 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 005/103] 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 006/103] 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 007/103] 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 008/103] 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 009/103] 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 010/103] 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 011/103] 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 012/103] 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 013/103] 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 014/103] 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 015/103] 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 016/103] 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 017/103] 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 018/103] 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 019/103] 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 020/103] 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 021/103] 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 022/103] 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 023/103] 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 024/103] 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 025/103] 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 026/103] 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 027/103] 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 028/103] 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 029/103] 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 030/103] 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 031/103] 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 032/103] 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 033/103] 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 034/103] 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 035/103] 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 036/103] 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 037/103] 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 4564a44eed5ca7b1e9791e64ba8e1cce5b927fa7 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 22 Oct 2014 11:33:00 +0200 Subject: [PATCH 038/103] 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 039/103] 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 040/103] 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 041/103] 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 2307f8a3a948186d80766e84cacf558b02e42321 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 29 Oct 2014 10:19:30 +0100 Subject: [PATCH 042/103] 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 0066b170c25ef581fd7ddfbf346f6649bea5dbe9 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 5 Nov 2014 13:01:03 +0100 Subject: [PATCH 043/103] 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 044/103] 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 045/103] 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 046/103] 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 7bf811ce74467e8404055060590e3e2440e33a7d Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 7 Nov 2014 17:52:48 +0100 Subject: [PATCH 047/103] 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 048/103] 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 049/103] 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 b1d0ee55199c215f1b2b0be8bb6d941209acb103 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 12 Nov 2014 16:08:18 +0100 Subject: [PATCH 050/103] 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 051/103] 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 052/103] 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 fa36c074605d0fbace76b2b1d36e8d462ee88d91 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Thu, 13 Nov 2014 18:32:07 +0100 Subject: [PATCH 053/103] Fixed possible null pointers when GPS fix not valid for polar diagram --- .../mining/IncrementalRegressionProcessor.java | 14 +++++++++----- 1 file changed, 9 insertions(+), 5 deletions(-) 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 646f1e54359..ca7c7aed577 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 @@ -58,19 +58,23 @@ public class IncrementalRegressionProcessor implements Processor angleToTheWind = fix.getAngleToTheWind(); + WindWithConfidence> windSpeed = fix.getWindSpeed(); + + SpeedWithBearingWithConfidence boatSpeedWithConfidence = fix.getBoatSpeed(); - - if (angleToTheWind != null) { - WindWithConfidence> windWithConfidenceForSpeed = fix.getWindSpeed(); - SpeedWithBearingWithConfidence boatSpeedWithConfidence = fix.getBoatSpeed(); + //Only add GPS data if speeds and angles are not null, else do nothing! + if (angleToTheWind != null && windSpeed != null && boatSpeedWithConfidence != null) { + + WindWithConfidence> windWithConfidenceForSpeed = windSpeed; + 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); - } + } } @Override From c65474dd37df7435c6ce1a9e0b007f1cfad24603 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Fri, 14 Nov 2014 22:35:15 +0100 Subject: [PATCH 054/103] Fixed PolarFixFilterCriteria for live races --- .../polars/mining/PolarFixFilterCriteria.java | 18 ++++++++++-------- 1 file changed, 10 insertions(+), 8 deletions(-) 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 9103f7e90d2..285cd43f13d 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 @@ -29,19 +29,21 @@ public class PolarFixFilterCriteria implements FilterCriterion Date: Wed, 26 Nov 2014 10:56:57 +0100 Subject: [PATCH 055/103] WindStepping now supports doubles as levels --- .../com/sap/sailing/domain/common/WindStepping.java | 4 ++-- .../impl/PolarSheetGenerationSettingsImpl.java | 2 +- .../sailing/domain/common/impl/WindSteppingImpl.java | 12 ++++++------ .../common/impl/WindSteppingWithMaxDistance.java | 4 ++-- .../sap/sailing/domain/test/WindSteppingTest.java | 12 ++++++------ .../gwt/ui/test/PolarSheetGenerationServiceTest.java | 2 +- .../gwt/ui/polarsheets/PolarSheetsChartPanel.java | 2 +- .../polarsheets/WindSteppingConfiguratorPanel.java | 10 +++++----- .../polars/test/PolarSheetGenerationTest.java | 8 ++++---- .../sap/sailing/polars/mining/PolarDataMiner.java | 4 ++-- .../SpeedClusterGroupFromWindSteppingCreator.java | 2 +- 11 files changed, 31 insertions(+), 31 deletions(-) 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 0ed1304fd4d..d2f38855204 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 @@ -6,9 +6,9 @@ public interface WindStepping extends Serializable{ public abstract int getLevelIndexForValue(double speed); - public abstract int getSteppedValueForValue(double speed); + public abstract Double getSteppedValueForValue(double speed); - Integer[] getRawStepping(); + Double[] getRawStepping(); public abstract int getLevelIndexFloorForValue(double speed); 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 031fab4aa03..4ac8f25e07f 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,7 +5,7 @@ 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 }; + Double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. }; WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.5); 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.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 ea11afd446e..4d289158fae 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 @@ -11,9 +11,9 @@ public class WindSteppingImpl implements WindStepping { protected WindSteppingImpl() {}; private static final long serialVersionUID = 2215693490331489508L; - protected Integer[] levels; + protected Double[] levels; - public WindSteppingImpl(Integer[] levels) { + public WindSteppingImpl(Double[] levels) { this.levels = levels; } @@ -24,7 +24,7 @@ public class WindSteppingImpl implements WindStepping { @Override public int getLevelIndexForValue(double speed) { for (int i = 0; i < levels.length - 1; i++) { - if (speed < levels[i] + ((levels[i+1] - levels[i]) / 2)) { + if (speed < levels[i] + ((levels[i+1] - levels[i]) / 2.)) { return i; } } @@ -32,14 +32,14 @@ public class WindSteppingImpl implements WindStepping { } @Override - public Integer[] getRawStepping() { + public Double[] getRawStepping() { return levels; } @Override - public int getSteppedValueForValue(double speed) { + public Double getSteppedValueForValue(double speed) { int levelIndex = getLevelIndexForValue(speed); - int result = - 1; + Double result = -1.0; if (levelIndex >= 0) { result = levels[levelIndex]; } 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 8fea549479e..d22d4b35091 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 @@ -11,7 +11,7 @@ public class WindSteppingWithMaxDistance extends WindSteppingImpl { super(); }; - public WindSteppingWithMaxDistance(Integer[] levels, double maxDistance) { + public WindSteppingWithMaxDistance(Double[] levels, double maxDistance) { super(levels); this.maxDistance = maxDistance; } @@ -20,7 +20,7 @@ public class WindSteppingWithMaxDistance extends WindSteppingImpl { public int getLevelIndexForValue(double speed) { int result = -1; for (int i = 0; i < levels.length - 1; i++) { - double threshold = levels[i] + ((levels[i+1] - levels[i]) / 2); + double threshold = levels[i] + ((levels[i+1] - levels[i]) / 2.); if (speed < threshold) { if (threshold - speed <= maxDistance * 2) { result = i; diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindSteppingTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindSteppingTest.java index 47dbba9300b..440ab223abd 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindSteppingTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindSteppingTest.java @@ -12,26 +12,26 @@ public class WindSteppingTest { @Test public void testStepping() { - Integer[] levels = {2,4}; + Double[] levels = {2.,4.}; WindStepping stepping = new WindSteppingImpl(levels); Assert.assertEquals(0, stepping.getLevelIndexForValue(1)); Assert.assertEquals(1, stepping.getLevelIndexForValue(5)); Assert.assertEquals(1, stepping.getLevelIndexForValue(3)); - Assert.assertEquals(2, stepping.getSteppedValueForValue(1.064)); - Assert.assertEquals(4, stepping.getSteppedValueForValue(7.8365)); + Assert.assertEquals(2., stepping.getSteppedValueForValue(1.064), 0.005); + Assert.assertEquals(4., stepping.getSteppedValueForValue(7.8365), 0.005); } @Test public void testSteppingWithMaxDistance() { - Integer[] levels = {2,4}; + Double[] levels = {2.,4.}; WindStepping stepping = new WindSteppingWithMaxDistance(levels, 1.0); Assert.assertEquals(0, stepping.getLevelIndexForValue(1)); Assert.assertEquals(1, stepping.getLevelIndexForValue(5)); Assert.assertEquals(-1, stepping.getLevelIndexForValue(5.01)); Assert.assertEquals(-1, stepping.getLevelIndexForValue(0.5)); Assert.assertEquals(-1, stepping.getLevelIndexForValue(8)); - Assert.assertEquals(2, stepping.getSteppedValueForValue(1.064)); - Assert.assertEquals(-1, stepping.getSteppedValueForValue(7.8365)); + Assert.assertEquals(2.0, stepping.getSteppedValueForValue(1.064), 0.005); + Assert.assertEquals(-1.0, stepping.getSteppedValueForValue(7.8365), 0.005); } } 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 c4ebb1f8385..2f2fff6f716 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 @@ -66,7 +66,7 @@ public class PolarSheetGenerationServiceTest { SailingService service = new MockSailingServiceForPolarSheetGeneration(); - Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 }; + Double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. }; WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0); PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 20, 0, false, true, 5, 0.05, false, 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 22aba90c31f..4fa726aa74e 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 { for (int i = 0; i < stepCount; i++) { if (hasSufficientDataForWindspeed(result.getDataCountPerAngleForWindspeed(i))) { if (seriesMap.get(seriesId)[i] == null) { - createSeriesForWindspeed(seriesId, i, result.getStepping().getRawStepping()[i]); + createSeriesForWindspeed(seriesId, i, (int) Math.round(result.getStepping().getRawStepping()[i])); } Series series = seriesMap.get(seriesId)[i]; //series.setPoints(result.getAveragedPolarDataByWindSpeed()[i], false); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/WindSteppingConfiguratorPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/WindSteppingConfiguratorPanel.java index a82b22b408b..508268c748a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/WindSteppingConfiguratorPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/WindSteppingConfiguratorPanel.java @@ -27,9 +27,9 @@ public class WindSteppingConfiguratorPanel extends HorizontalPanel { public WindSteppingConfiguratorPanel(WindSteppingWithMaxDistance windStepping) { setupPlusAndMinusButtons(); - Integer[] levels = windStepping.getRawStepping(); + Double[] levels = windStepping.getRawStepping(); for (int i = 0; i < levels.length; i++) { - TextBox textBox = createSingleBox(levels[i]); + TextBox textBox = createSingleBox((int) Math.round(levels[i])); textBoxes.add(textBox); } updateTextBoxes(); @@ -69,12 +69,12 @@ public class WindSteppingConfiguratorPanel extends HorizontalPanel { } public WindSteppingWithMaxDistance getStepping(double maxDistance) { - List levelList = new ArrayList(); + List levelList = new ArrayList(); for (TextBox box : textBoxes) { - levelList.add(Integer.parseInt(box.getValue())); + levelList.add(new Double(Integer.parseInt(box.getValue()))); } Collections.sort(levelList); - Integer[] levels = levelList.toArray(new Integer[levelList.size()]); + Double[] levels = levelList.toArray(new Double[levelList.size()]); return new WindSteppingWithMaxDistance(levels, maxDistance); } 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 a8d7cd0c6cb..ac13fb065fe 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 @@ -58,14 +58,14 @@ 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.sse.common.TimePoint; -import com.sap.sse.common.impl.MillisecondsTimePoint; 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; import com.sap.sailing.polars.generation.PolarSheetGenerator; import com.sap.sailing.polars.generation.PolarSheetHistogramBuilder; +import com.sap.sse.common.TimePoint; +import com.sap.sse.common.impl.MillisecondsTimePoint; public class PolarSheetGenerationTest { @@ -78,7 +78,7 @@ public class PolarSheetGenerationTest { MockTrackedRaceForPolarSheetGeneration race = new MockTrackedRaceForPolarSheetGeneration(); - Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 }; + Double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. }; WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0); PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 20, 0, false, true, 5, 0.05, false, windStepping, false); @@ -118,7 +118,7 @@ public class PolarSheetGenerationTest { @Test public void testHistogramBuilder() { - Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 }; + Double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. }; WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0); PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 10, 0, false, true, 5, 0.05, false, windStepping, false); 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 49bceae8cf5..fd615c5dad5 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 @@ -116,7 +116,7 @@ public class PolarDataMiner { public PolarSheetsData createFullSheetForBoatClass(BoatClass boatClass) { - Integer[] defaultWindSpeeds = defaultPolarSheetGenerationSettings.getWindStepping().getRawStepping(); + Double[] defaultWindSpeeds = defaultPolarSheetGenerationSettings.getWindStepping().getRawStepping(); Number[][] averagedPolarDataByWindSpeed = new Number[defaultWindSpeeds.length][360]; Map dataCountPerAngleForWindspeed = new HashMap<>(); @@ -125,7 +125,7 @@ public class PolarDataMiner { int totalDataCount = 0; for (int windIndex = 0; windIndex < defaultWindSpeeds.length; windIndex++) { - Integer windSpeed = defaultWindSpeeds[windIndex]; + Double windSpeed = defaultWindSpeeds[windIndex]; Integer[] perAngle = new Integer[360]; Map perWindSpeed = new HashMap<>(); 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 index 6bd0e192b20..8b6262d4231 100644 --- 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 @@ -9,7 +9,7 @@ public class SpeedClusterGroupFromWindSteppingCreator { public static ClusterGroup createSpeedClusterGroupFrom(WindSteppingWithMaxDistance windStepping) { double maxDistance = windStepping.getMaxDistance(); - Integer[] rawIntegerStepping = windStepping.getRawStepping(); + Double[] rawIntegerStepping = windStepping.getRawStepping(); double[] rawDoubleLevelMids = new double[rawIntegerStepping.length]; for (int i = 0; i < rawIntegerStepping.length; i++) { rawDoubleLevelMids[i] = rawIntegerStepping[i]; From e33258acf474497468fe41e9804facf09bbedac5 Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 26 Nov 2014 12:12:28 +0100 Subject: [PATCH 056/103] Added parameter to allow usage of mean instead of lin reg --- .../PolarSheetGenerationSettingsImpl.java | 15 ++++++++++ .../mining/test/PolarDataMinerTest.java | 6 ++-- .../polars/test/PolarSheetAnalyzerTest.java | 24 ++++++++-------- .../sap/sailing/polars/PolarDataService.java | 5 ++-- .../analysis/impl/PolarSheetAnalyzerImpl.java | 21 +++++++------- .../polars/impl/PolarDataServiceImpl.java | 6 ++-- .../IncrementalRegressionProcessor.java | 4 +-- .../sailing/polars/mining/PolarDataMiner.java | 28 +++++++++++++------ .../polars/regression/BoatSpeedEstimator.java | 10 ++++++- .../IncrementalLinearRegressionProcessor.java | 2 ++ .../IncrementalLeastSquaresProcessor.java | 8 ++++++ .../gateway/jaxrs/api/PolarResource.java | 2 +- 12 files changed, 88 insertions(+), 43 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 4ac8f25e07f..200d58c7bff 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 @@ -1,5 +1,8 @@ package com.sap.sailing.domain.common.impl; +import java.util.ArrayList; +import java.util.List; + import com.sap.sailing.domain.common.PolarSheetGenerationSettings; public class PolarSheetGenerationSettingsImpl implements PolarSheetGenerationSettings { @@ -10,6 +13,18 @@ public class PolarSheetGenerationSettingsImpl implements PolarSheetGenerationSet return new PolarSheetGenerationSettingsImpl(50, 0.1, 20, 20, 0.1, true, true, 2, 0.05, true, windStepping, false); } + + public static PolarSheetGenerationSettings createBackendPolarSettings() { + List levelList = new ArrayList(); + for (double levelValue = 0.5; levelValue < 35; levelValue = levelValue + 0.5) { + levelList.add(levelValue); + } + Double[] levels = levelList.toArray(new Double[levelList.size()]); + + WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 0.5); + return new PolarSheetGenerationSettingsImpl(50, 0.1, 20, 20, 0.1, true, true, 2, 0.05, true, windStepping, + false); + } private static final long serialVersionUID = 2731616509404813790L; private Integer minimumDataCountPerGraph; 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 ef12a4d729d..2d81c4d20b1 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 @@ -107,17 +107,17 @@ public class PolarDataMinerTest { } SpeedWithConfidence estimatedSpeed1 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), - new DegreeBearingImpl(44.9)); + new DegreeBearingImpl(44.9), true); assertThat(estimatedSpeed1, is(notNullValue())); assertThat(estimatedSpeed1.getObject().getKnots(), is(closeTo(10.55, EPSILON))); SpeedWithConfidence estimatedSpeed2 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), - new DegreeBearingImpl(42)); + new DegreeBearingImpl(42), true); assertThat(estimatedSpeed2, is(notNullValue())); assertThat(estimatedSpeed2.getObject().getKnots(), is(closeTo(10.05, EPSILON))); SpeedWithConfidence estimatedSpeed3 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), - new DegreeBearingImpl(42.8)); + new DegreeBearingImpl(42.8), true); assertThat(estimatedSpeed3, is(notNullValue())); assertThat(estimatedSpeed3.getObject().getKnots(), is(closeTo(10, EPSILON))); } 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 37a526f0fd3..25d148b0e03 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 @@ -63,46 +63,46 @@ public class PolarSheetAnalyzerTest { //Starboard Upwind when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(49)))).thenReturn( + argThat(new BearingMatcher(49)), true)).thenReturn( new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.478048671702888), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(50)))).thenReturn( + argThat(new BearingMatcher(50)), true)).thenReturn( new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.466303997538812), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(51)))).thenReturn( + argThat(new BearingMatcher(51)), true)).thenReturn( 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( + argThat(new BearingMatcher(141)), true)).thenReturn( new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.794354053931528), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(142)))).thenReturn( + argThat(new BearingMatcher(142)), true)).thenReturn( new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.665782579628802), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(143)))).thenReturn( + argThat(new BearingMatcher(143)), true)).thenReturn( new SpeedWithConfidenceImpl(new KnotSpeedImpl(14.169301888320263), 0.5, null)); //Port Upwind when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(-48)))).thenReturn( + argThat(new BearingMatcher(-48)), true)).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( + argThat(new BearingMatcher(-50)), true)).thenReturn( new SpeedWithConfidenceImpl(new KnotSpeedImpl(8.553274292235153), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(-49)))).thenReturn( + argThat(new BearingMatcher(-49)), true)).thenReturn( 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( + argThat(new BearingMatcher(-145)), true)).thenReturn( new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.78894715705271), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(-144)))).thenReturn( + argThat(new BearingMatcher(-144)), true)).thenReturn( new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.420656294986587), 0.5, null)); when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), - argThat(new BearingMatcher(-143)))).thenReturn( + argThat(new BearingMatcher(-143)), true)).thenReturn( new SpeedWithConfidenceImpl(new KnotSpeedImpl(13.268607457651942), 0.5, null)); return mockedPolarDataService; 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 aca9c3a0271..19d5aaf6e11 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 @@ -26,7 +26,7 @@ import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; * 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 + * The interesting methods for a user are {@link #getSpeed(BoatClass, Speed, Bearing, boolean)} if data for a specific angle is * needed and {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} * which also returns the average angle for the provided parameters. * @@ -41,10 +41,11 @@ public interface PolarDataService { * @param windSpeed * @param bearingToTheWind * Boat's direction relative to the wind. either in -180 -> +180 or 0 -> 359 degrees + * @param useLinearRegression if true uses lin. Regression for estimation in the wind interval, if false simple arithm. mean in interval * @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, boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException; /** 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 d0b8f6c5fc7..15fffc78e83 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 @@ -38,7 +38,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { int startAngleInclusive = 1; int endAngleExclusive = 90; SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, - endAngleExclusive); + endAngleExclusive, true); return speedWithBearing; } @@ -49,7 +49,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { int startAngleInclusive = 91; int endAngleExclusive = 180; SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, - endAngleExclusive); + endAngleExclusive, true); return speedWithBearing; } @@ -59,7 +59,7 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { int startAngleInclusive = 181; int endAngleExclusive = 270; SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, - endAngleExclusive); + endAngleExclusive, true); return speedWithBearing; } @@ -69,12 +69,12 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { int startAngleInclusive = 271; int endAngleExclusive = 360; SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, - endAngleExclusive); + endAngleExclusive, true); return speedWithBearing; } private SpeedWithBearingWithConfidence estimateAnglePeakAndAverageSpeed(BoatClass boatClass, Speed windSpeed, - int startAngleInclusive, int endAngleExclusive) throws NotEnoughDataHasBeenAddedException { + int startAngleInclusive, int endAngleExclusive, boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException { Integer[] dataCountPerAngle = getDataCountArray(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); @@ -83,8 +83,9 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { double convertedAngleIfOver180 = convertAngleIfNecessary(estimatedPeak); - SpeedWithConfidence averagedSpeedWithConfidence = estimatedSpeed(boatClass, windSpeed, convertedAngleIfOver180); - + SpeedWithConfidence averagedSpeedWithConfidence = estimateSpeed(boatClass, windSpeed, + convertedAngleIfOver180, useLinearRegression); + double originConfidence = averagedSpeedWithConfidence.getConfidence(); double overallConfidence = (originConfidence + calcDataCountConfidence(dataCountPerAngle, estimatedPeak)) / 2; @@ -122,10 +123,10 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { return convertedAngleIfOver180; } - private SpeedWithConfidence estimatedSpeed(BoatClass boatClass, Speed windSpeed, double estimatedPeak) - throws NotEnoughDataHasBeenAddedException { + private SpeedWithConfidence estimateSpeed(BoatClass boatClass, Speed windSpeed, double estimatedPeak, + boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException { SpeedWithConfidence speed = polarDataService.getSpeed(boatClass, windSpeed, new DegreeBearingImpl( - estimatedPeak)); + estimatedPeak), useLinearRegression); return speed; } 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 2f7544b4944..62653587319 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 @@ -59,9 +59,9 @@ public class PolarDataServiceImpl implements PolarDataService { } @Override - public SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) - throws NotEnoughDataHasBeenAddedException { - return polarDataMiner.estimateBoatSpeed(boatClass, windSpeed, bearingToTheWind); + public SpeedWithConfidence getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind, + boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException { + return polarDataMiner.estimateBoatSpeed(boatClass, windSpeed, bearingToTheWind, useLinearRegression); } @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 9d427aba9c8..b6945159546 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 @@ -94,7 +94,7 @@ public class IncrementalRegressionProcessor implements Processor, Integer> estimateBoatSpeed(final BoatClass boatClass, final Speed windSpeed, - final Bearing angleToTheWind) + final Bearing angleToTheWind, boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException { int dataCount = 0; PolarClusterKey key = new PolarClusterKey() { @@ -126,7 +126,7 @@ public class IncrementalRegressionProcessor implements Processor, Integer>(new SpeedWithConfidenceImpl( 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 fd615c5dad5..b5b422e9b6c 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 @@ -40,7 +40,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 final PolarSheetGenerationSettings backendPolarSheetGenerationSettings; private static ThreadPoolExecutor createExecutor() { return new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS, @@ -52,7 +52,7 @@ public class PolarDataMiner { private IncrementalRegressionProcessor incrementalRegressionProcessor; public PolarDataMiner() { - defaultPolarSheetGenerationSettings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); + backendPolarSheetGenerationSettings = PolarSheetGenerationSettingsImpl.createBackendPolarSettings(); try { setUpWorkflow(); } catch (ClassCastException | NoSuchMethodException | SecurityException e) { @@ -65,7 +65,7 @@ public class PolarDataMiner { private void setUpWorkflow() throws ClassCastException, NoSuchMethodException, SecurityException { - WindSteppingWithMaxDistance stepping = defaultPolarSheetGenerationSettings.getWindStepping(); + WindSteppingWithMaxDistance stepping = backendPolarSheetGenerationSettings.getWindStepping(); final ClusterGroup speedClusterGroup = SpeedClusterGroupFromWindSteppingCreator .createSpeedClusterGroupFrom(stepping); incrementalRegressionProcessor = new IncrementalRegressionProcessor(speedClusterGroup); @@ -108,15 +108,25 @@ public class PolarDataMiner { return isActive || hasQueue; } - public SpeedWithConfidence estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind) + /** + * + * @param boatClass + * @param windSpeed + * @param angleToTheWind + * @param useLinearRegression if true uses lin. regression in the wind interval, otherwise arithm. mean + * @return + * @throws NotEnoughDataHasBeenAddedException + */ + public SpeedWithConfidence estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind, + boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException { - return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind).getA(); + return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind, useLinearRegression).getA(); } public PolarSheetsData createFullSheetForBoatClass(BoatClass boatClass) { - Double[] defaultWindSpeeds = defaultPolarSheetGenerationSettings.getWindStepping().getRawStepping(); + Double[] defaultWindSpeeds = backendPolarSheetGenerationSettings.getWindStepping().getRawStepping(); Number[][] averagedPolarDataByWindSpeed = new Number[defaultWindSpeeds.length][360]; Map dataCountPerAngleForWindspeed = new HashMap<>(); @@ -134,7 +144,7 @@ public class PolarDataMiner { try { int convertedAngle = convertAngleIfNecessary(angle); speedWithConfidenceAndDataCount = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, - new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); + new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle), true); } catch (NotEnoughDataHasBeenAddedException e) { // No data so put in a 0 speed with 0 confidence speedWithConfidenceAndDataCount = new Pair, Integer>( @@ -159,7 +169,7 @@ public class PolarDataMiner { PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, totalDataCount, - dataCountPerAngleForWindspeed, defaultPolarSheetGenerationSettings.getWindStepping(), histogramDataMap); + dataCountPerAngleForWindspeed, backendPolarSheetGenerationSettings.getWindStepping(), histogramDataMap); return data; } @@ -206,7 +216,7 @@ public class PolarDataMiner { if (angle >= startAngleInclusive && angle < endAngleExclusive) { try { dataCounts[angle] = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, - new DegreeBearingImpl(convertAngleIfNecessary(angle))).getB(); + new DegreeBearingImpl(convertAngleIfNecessary(angle)), true).getB(); } catch (NotEnoughDataHasBeenAddedException e) { dataCounts[angle] = 0; } 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 8c90aa9355b..01a486ebd69 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 @@ -30,7 +30,15 @@ public class BoatSpeedEstimator { private int dataCount = 0; - public double estimateSpeed(double windSpeed, double angleToTheWind) throws NotEnoughDataHasBeenAddedException { + /** + * + * @param windSpeed + * @param angleToTheWind + * @param useLinearRegression if false mean of wind interval is used, otherwise lin. regression + * @return + * @throws NotEnoughDataHasBeenAddedException + */ + public double estimateSpeed(double windSpeed, double angleToTheWind, boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException { lock.readLock().lock(); try { double angleEstimated = angleRegression.getEstimatedY(angleToTheWind); 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 a094deab94e..d4babfb0573 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 @@ -11,5 +11,7 @@ public interface IncrementalLinearRegressionProcessor { double getIntercept(); int getDataCount(); + + double getMeanOfY() throws NotEnoughDataHasBeenAddedException; } 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 02acf4c3533..64b99193c44 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 @@ -119,4 +119,12 @@ public class IncrementalLeastSquaresProcessor implements IncrementalLinearRegres return numberOfPointsAdded; } + @Override + public double getMeanOfY() throws NotEnoughDataHasBeenAddedException { + if (numberOfPointsAdded < 1) { + throw new NotEnoughDataHasBeenAddedException(); + } + return meanOfY; + } + } 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 c11319d9ffb..b610876cca5 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 @@ -42,7 +42,7 @@ public class PolarResource extends AbstractSailingServerResource { ResponseBuilder responseBuilder; try { speedWithConfidence = getService().getPolarDataService().getSpeed(boatClass, new KnotSpeedImpl(windSpeed), - new DegreeBearingImpl(angle)); + new DegreeBearingImpl(angle), true); String resultString = "Speed: " + speedWithConfidence.getObject().getKnots() + "kn; Confidence: " + speedWithConfidence.getConfidence(); responseBuilder = Response.ok(resultString, MediaType.TEXT_PLAIN); From b90cf7c304fc0c9c834a5757052f5660b8781ed0 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Mon, 1 Dec 2014 13:56:51 +0100 Subject: [PATCH 057/103] Initial carousel implementation using slick slider --- java/com.sap.sailing.gwt.ui/Home.html | 8 +- .../client/shared/mainmedia/MainMedia.css | 3 - .../client/shared/mainmedia/MainMedia.java | 34 +- .../client/shared/mainmedia/MainMedia.ui.xml | 11 +- .../shared/recentevent/RecentEvent.java | 8 +- .../sailing/gwt/ui/shared/EventBaseDTO.java | 124 +++++++ java/com.sap.sse.gwt/META-INF/MANIFEST.MF | 1 + .../controls/carousel/ImageCarousel.java | 307 ++++++++++++++++++ .../controls/carousel/ImageCarousel.ui.xml | 52 +++ 9 files changed, 514 insertions(+), 34 deletions(-) create mode 100644 java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java create mode 100644 java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml diff --git a/java/com.sap.sailing.gwt.ui/Home.html b/java/com.sap.sailing.gwt.ui/Home.html index f0c73d4c6f4..8d7f43d8760 100644 --- a/java/com.sap.sailing.gwt.ui/Home.html +++ b/java/com.sap.sailing.gwt.ui/Home.html @@ -29,12 +29,18 @@ - + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.css b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.css index c774dce192b..81c5443c71a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.css +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.css @@ -5,10 +5,7 @@ .media_swipecontainer { position: relative; - width: 830px; - height: 622px; margin: 0 auto; - overflow: hidden; } .media_swipewrapper { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java index 8fccb67ec01..dbfa31948ec 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java @@ -23,6 +23,7 @@ import com.sap.sailing.gwt.idangerous.Swiper; import com.sap.sailing.gwt.ui.common.client.YoutubeApi; import com.sap.sailing.gwt.ui.shared.EventBaseDTO; import com.sap.sse.common.Util.Pair; +import com.sap.sse.gwt.client.controls.carousel.ImageCarousel; public class MainMedia extends Composite { @@ -33,9 +34,9 @@ public class MainMedia extends Composite { @UiField HTMLPanel videosPanel; @UiField DivElement videoLightBoxData; - @UiField HTMLPanel mediaSlides; + @UiField ImageCarousel imageCarousel; - private Swiper swiper; + // private Swiper swiper; private int videoCounter; interface MainMediaUiBinder extends UiBinder { @@ -73,20 +74,22 @@ public class MainMedia extends Composite { Collections.swap(photoGalleryUrls, i, random.nextInt(gallerySize)); } for (String url : photoGalleryUrls) { - SimplePanel imageContainer = new SimplePanel(); - imageContainer.addStyleName(STYLES.media_swiperslide()); - String image = "url(" + url + ")"; - imageContainer.getElement().getStyle().setBackgroundImage(image); - mediaSlides.add(imageContainer); + +// SimplePanel imageContainer = new SimplePanel(); +// imageContainer.addStyleName(STYLES.media_swiperslide()); +// String image = "url(" + url + ")"; +// imageContainer.getElement().getStyle().setBackgroundImage(image); + imageCarousel.addImage(url); } - this.swiper = Swiper.createWithLoopOption(STYLES.media_swipecontainer(), STYLES.media_swipewrapper(), STYLES.media_swiperslide()); + imageCarousel.init(); + // this.swiper = Swiper.createWithLoopOption(STYLES.media_swipecontainer(), STYLES.media_swipewrapper(), STYLES.media_swiperslide()); // See bug 2232: the stage image sizes are scaled incorrectly. https://github.com/ubilabs/sap-sailing-analytics/issues/421 and // http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=2232 have the details. A quick fix may be to send a resize event // after everything has been rendered. Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { - swiper.reInit(); + // swiper.reInit(); } }); } @@ -113,17 +116,4 @@ public class MainMedia extends Composite { return result; } - @UiHandler("nextPictureLink") - public void nextStageTeaserLinkClicked(ClickEvent e) { - if (this.swiper != null) { - this.swiper.swipeNext(); - } - } - - @UiHandler("prevPictureLink") - public void prevStageTeaserLinkClicked(ClickEvent e) { - if (this.swiper != null) { - this.swiper.swipePrev(); - } - } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.ui.xml index 5331f506a0d..8ab81ceca94 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.ui.xml @@ -1,5 +1,9 @@ - + @@ -20,10 +24,9 @@
+
- - - +
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.java index 94734a1b27d..e289b32f4a0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.java @@ -1,6 +1,5 @@ package com.sap.sailing.gwt.home.client.shared.recentevent; -import java.util.List; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.DivElement; @@ -62,11 +61,12 @@ public class RecentEvent extends Composite { } venueName.setInnerText(event.venue.getName()); eventStartDate.setInnerText(EventDatesFormatterUtil.formatDateRangeWithoutYear(event.startDate, event.endDate)); - List photoGalleryImageURLs = event.getPhotoGalleryImageURLs(); - if (photoGalleryImageURLs.isEmpty()) { + + final String thumbnailUrl = event.getEventThumbnailImageUrl(); + if (thumbnailUrl== null ) { eventImage.setSrc(RecentEventResources.INSTANCE.defaultEventPhotoImage().getSafeUri().asString()); } else { - eventImage.setSrc(photoGalleryImageURLs.get(0)); + eventImage.setSrc(thumbnailUrl); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java index c8bda9ba95c..85bc4083c1e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java @@ -17,6 +17,8 @@ import com.sap.sse.common.Util; public class EventBaseDTO extends NamedDTO implements IsSerializable { private static final long serialVersionUID = 818666323178097939L; private static final String STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE = "stage"; + private static final String THUMBNAIL_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE = "eventteaser"; + public VenueDTO venue; public Date startDate; public Date endDate; @@ -33,6 +35,9 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { /** placeholder for social media URL's -> attributes will be implemented later on */ private String facebookURL; private String twitterURL; + + + /** * For the image URL keys holds the sizes of these images if known. An image size is "known" by this object if it was * provided to the {@link #setImageSize} method. @@ -189,6 +194,125 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { } return result; } + + /** + * + * @return + */ + private String lastThumbnail; + public String getEventThumbnailImageUrl() { + if (lastThumbnail == null) { + lastThumbnail = findEventThumbnailImageUrl(); + } + return lastThumbnail; + } + + + private String findEventThumbnailImageUrl() { + + final class ImageHolder { + + final int PERFECT_HEIGHT = 240; + final int PERFECT_WIDTH = 370; + final double PERFECT_RATIO = PERFECT_HEIGHT / (double) PERFECT_WIDTH; + final String url; + final int height; + final int width; + final int size; + final double ratio; + + ImageHolder() { + this.url = ""; + this.height = -1; + this.width = -1; + this.size = -1; + this.ratio = -1; + } + ImageHolder(String url, ImageSize imageSize) { + assert url != null: "url must not be null"; + assert imageSize != null : "imageSize must not be null"; + this.url = url; + this.height = imageSize.getHeight(); + this.width = imageSize.getWidth(); + this.size = height * width; + this.ratio = height/ (double)width; + } + + boolean isBetterWorstcaseThan(ImageHolder otherImageHolder) { + if (isNull()) { + return false; + } + if (otherImageHolder.isNull()) { + return true; + } + if (!isBigEnough() && otherImageHolder.isBigEnough()) { + return true; + } + if (this.fitsRatio() && otherImageHolder.fitsRatio() && otherImageHolder.isBigEnough() && otherImageHolder.isSmallerThan(this)) { + return true; + } + return false; + } + + boolean isSmallerThan(ImageHolder otherImageHolder) { + return size < otherImageHolder.size; + } + + boolean isBigEnough() { + return height >= PERFECT_HEIGHT && width >= PERFECT_WIDTH; + } + + boolean fitsRatio() { + return ratio == PERFECT_RATIO; + } + + boolean isPerfectFit() { + return ( height == PERFECT_HEIGHT && width ==PERFECT_WIDTH ); + } + + boolean isNull() { + return size == -1; + } + } + + // search for name pattern + for (String imageUrl : imageURLs) { + if (imageUrl != null && imageUrl.toLowerCase().contains(THUMBNAIL_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { + return imageUrl; + } + } + + + ImageHolder actualWorstcase = new ImageHolder( ); + ImageHolder bestFit = new ImageHolder( ); + + for (String candidateImageUrl : getPhotoGalleryImageURLs()) { + final ImageHolder candidate = new ImageHolder(candidateImageUrl, getImageSize(candidateImageUrl)); + + if (candidate.isPerfectFit()) { + return candidate.url; + } + + if (candidate.fitsRatio() && candidate.isBigEnough()) { + if (candidate.isSmallerThan(bestFit)) { + bestFit = candidate; + } + } + + if (candidate.isBetterWorstcaseThan(actualWorstcase)) { + actualWorstcase = candidate; + } + + } + + if (!bestFit.isNull()) { + return bestFit.url; + } + if (!actualWorstcase.isNull()) { + return actualWorstcase.url; + } + return null; + } public List getVideoURLs() { return videoURLs; diff --git a/java/com.sap.sse.gwt/META-INF/MANIFEST.MF b/java/com.sap.sse.gwt/META-INF/MANIFEST.MF index ad1623902c3..7bb2e6a7133 100644 --- a/java/com.sap.sse.gwt/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.gwt/META-INF/MANIFEST.MF @@ -19,6 +19,7 @@ Export-Package: com.sap.sse.gwt.client, com.sap.sse.gwt.client.async, com.sap.sse.gwt.client.controls, com.sap.sse.gwt.client.controls.busyindicator, + com.sap.sse.gwt.client.controls.carousel, com.sap.sse.gwt.client.controls.listedit, com.sap.sse.gwt.client.controls.slider, com.sap.sse.gwt.client.dialog, diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java new file mode 100644 index 00000000000..7b9bc7b3b48 --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java @@ -0,0 +1,307 @@ +package com.sap.sse.gwt.client.controls.carousel; + +import java.util.LinkedList; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.core.client.ScriptInjector; +import com.google.gwt.dom.client.ButtonElement; +import com.google.gwt.dom.client.DivElement; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.HeadElement; +import com.google.gwt.dom.client.LinkElement; +import com.google.gwt.dom.client.Node; +import com.google.gwt.dom.client.ScriptElement; +import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.dom.client.StyleInjector; +import com.google.gwt.resources.client.ClientBundle; +import com.google.gwt.resources.client.TextResource; +import com.google.gwt.safecss.shared.SafeStyles; +import com.google.gwt.safecss.shared.SafeStylesBuilder; +import com.google.gwt.safehtml.client.SafeHtmlTemplates; +import com.google.gwt.safehtml.shared.SafeHtml; +import com.google.gwt.safehtml.shared.UriUtils; +import com.google.gwt.thirdparty.guava.common.base.Strings; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.Widget; + +/** + * Image carousel that uses the open source "slick carousel". + *

+ * Documentation/ download of slick js and css from homepate/ github page. + * + * @see Slick homepage. + * @see Slick github page. + * + * + *

+ * + * This class uses JSNI to wrap the JS slick carousel. + * + * Usage: + * + * - Create widget + * + * ImageCarousel slider = new ImageCarousel(); + * + * - Add images + * + * slider.addImage("http://demotivators.despair.com/demotivational/foresightdemotivator.jpg"); + * slider.addImage("http://demotivators.despair.com/demotivational/commitmentdemotivator.jpg"); + * slider.addImage("http://demotivators.despair.com/demotivational/platitudesdemotivator.jpg"); + * slider.addImage("http://demotivators.despair.com/demotivational/destinyrockdemotivator.jpg"); + * slider.addImage("http://demotivators.despair.com/demotivational/aspirationdemotivator.jpg"); + * + * + * The widget depends on css and js resources, as well as jquery 1.7+. + * + * To use the CDN resources, include: + * + * + * + * + * Created by pgtaboada on 10.11.14. + */ +public class ImageCarousel extends Widget { + + private static SlickSliderUiBinder ourUiBinder = GWT.create(SlickSliderUiBinder.class); + private static MyTemplate myTemplate = GWT.create(MyTemplate.class); + + @UiField + ButtonElement slickPrev; + + @UiField + ButtonElement slickNext; + + /** + * slick slider property: dots + */ + private boolean showDots = false; + /** + * slick slider property: centerMode + */ + private boolean centerMode = true; + /** + * slick slider property: variableWidth + */ + private boolean variableWidth = true; + /** + * slick slider property: infiniteScrolling + */ + private boolean infiniteScrolling = false; + /** + * Image margin (effectively sets spacing between images) + */ + private int contentMarginInPixels = 10; + /** + * Lazy load mechanism to use + */ + private String lazyload = LAZYLOAD.ONDEMAND.getPropertyValue(); + + /** + * The height of the images + */ + private int imagesHeight = 400; + + private final String uniqueId; + + + + /** + * Widget constructor + */ + public ImageCarousel() { + setElement(ourUiBinder.createAndBindUi(this)); + + uniqueId = "slider_"+Document.get().createUniqueId(); + getElement().addClassName(uniqueId); + + + } + + /** + * JSNI wrapper that does setup the slider + * + * @param uniqueId + */ + native void setupSlider(ImageCarousel sliderReference, ButtonElement prevArrowEl, ButtonElement nextArrowEl) /*-{ + + $wnd.$(document).ready(function() { + $wnd.$('.'+ (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)).slick( + { + dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::showDots), + infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::infiniteScrolling), + centerMode : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::centerMode), + variableWidth : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::variableWidth), + lazyLoad : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::lazyload), + swipeToSlide : false, + arrows : true, + responsive: true, + slidesToShow: 3, + prevArrow : prevArrowEl, + nextArrow : nextArrowEl + }); + }); + + }-*/; + + + native void addImage(ImageCarousel sliderReference, final DivElement el ) /*-{ + + $wnd.$( '.' + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)).slickAdd(el); + + }-*/; + + + /** + * Add new image to carousel. + * + * @param url + */ + public void addImage(String url) { + + GWT.log("Add image: " + url); + + DivElement imageHolder = Document.get().createDivElement(); + + SafeStylesBuilder ssb = new SafeStylesBuilder(); + + ssb.height(imagesHeight, Unit.PX); + ssb.margin(contentMarginInPixels, Style.Unit.PX); + + final ImageCarousel reference = this; + imageHolder.setInnerSafeHtml(myTemplate.imageDiv(UriUtils.fromString(url).asString(), ssb.toSafeStyles())); + + + getElement().appendChild(imageHolder); + //addImage( reference, imageHolder ); + + + } + + /** + * Define spacing between images in carousel. In fact, it is setting the img margins in pixels. + * + * @param contentMarginInPixels + */ + public void setContentMarginInPixels(int contentMarginInPixels) { + this.contentMarginInPixels = contentMarginInPixels; + } + + /** + * Set lazy loading technique. Accepts 'ondemand' or 'progressive'. + * + * @param lazyload + */ + public void setLazyload(LAZYLOAD lazyload) { + this.lazyload = lazyload.getPropertyValue(); + } + + /** + * Enable infinite loop sliding + */ + public void setInfiniteScrolling(boolean infiniteScrolling) { + this.infiniteScrolling = infiniteScrolling; + } + + /** + * Enable variable width slides. + * + * @param variableWidth + */ + public void setVariableWidth(boolean variableWidth) { + this.variableWidth = variableWidth; + } + + /** + * Enables centered view with partial prev/next slides. + * + * @param centerMode + */ + public void setCenterMode(boolean centerMode) { + this.centerMode = centerMode; + } + + /** + * Enable dot indicators + * + * @param showDots + */ + public void setShowDots(boolean showDots) { + this.showDots = showDots; + } + + /** + * UiBinder interface + */ + interface SlickSliderUiBinder extends UiBinder { + } + + /** + * Enum wrapper for lazy load techniques + */ + public enum LAZYLOAD { + + /** + * Loads each image "on demand" + */ + ONDEMAND("ondemand"), + + /** + * Progressively loads one image after another + */ + PROGRESSIVE("progressive"); + + String propertyValue; + + LAZYLOAD(String propertyValue) { + this.propertyValue = propertyValue; + } + + public String getPropertyValue() { + return propertyValue; + } + } + + + + /** + * Template used to create image element + */ + interface MyTemplate extends SafeHtmlTemplates { + @Template("

") + SafeHtml imageDiv(String uri, SafeStyles imageStyles); + } + + + /** + * Apply slick js to constructed dom tree. + */ + public void init() { + final ImageCarousel reference = this; + Scheduler.get().scheduleDeferred(new Command() { + @Override + public void execute() { + try { + setupSlider(reference, slickPrev, slickNext); + for (int i = 0; i < getElement().getChildCount(); i++) { + Element child = (Element) getElement().getChild(i); + String className = child.getClassName(); + if (className != null && className.contains("slick-list")) { + child.getStyle().setHeight(imagesHeight, Unit.PX); + } + } + GWT.log("Finished to init slider"); + } catch (Exception e) { + GWT.log("Catched Exception on slider init", e); + } + } + }); + } + +} \ No newline at end of file diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml new file mode 100644 index 00000000000..2df50bc0788 --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml @@ -0,0 +1,52 @@ + + + .slider { + width: 100%; + + } + .slider > ul > li > button:before { + font-size: 15px; + } + .slider img { + margin: 10px; + } + .slider slick-track { + } + + + .slider button { + height: 40px; + width: 40px; + right: -40px + } + .slick-prev:before { + color: #dddddd; + font-size: 30px; + height: 30px; + width: 30px; + left: -50px; + } + .slick-prev { + left: -50px; + } + + + + .slick-next { + right: -50px; + } + .slick-next:before { + color: #dddddd; + font-size: 30px; + height: 30px; + width: 30px; + right: -50px; + } + + + +
+ + +
+
\ No newline at end of file From c48955b2f31678cb2aa89338f9788967b0124659 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Mon, 1 Dec 2014 14:04:43 +0100 Subject: [PATCH 058/103] Removed startOnFirstThread option --- java/com.sap.sailing.gwt.ui/SailingGWT.launch | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT.launch b/java/com.sap.sailing.gwt.ui/SailingGWT.launch index 55ea7b4ab73..069ccfb3d6e 100755 --- a/java/com.sap.sailing.gwt.ui/SailingGWT.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT.launch @@ -29,8 +29,9 @@ + - + @@ -66,7 +67,7 @@ - + From 36094a3a14f5bd9ac6c4e2c766e1b51f36f3fa9a Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Mon, 1 Dec 2014 14:05:36 +0100 Subject: [PATCH 059/103] Removed startOnFirstThread option --- java/com.sap.sailing.server/SailingServer (No Proxy).launch | 1 + java/com.sap.sailing.server/SailingServer (Proxy).launch | 1 + 2 files changed, 2 insertions(+) diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index 7c7ad203ed7..b3896d29c88 100755 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -16,6 +16,7 @@ + diff --git a/java/com.sap.sailing.server/SailingServer (Proxy).launch b/java/com.sap.sailing.server/SailingServer (Proxy).launch index 19d31d495a2..0626f4d1958 100644 --- a/java/com.sap.sailing.server/SailingServer (Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy).launch @@ -16,6 +16,7 @@ + From 80074346f9a00597d3d1b3c1893cb671879eb722 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Mon, 1 Dec 2014 15:53:25 +0100 Subject: [PATCH 060/103] Fixed import to internal gwt/guava classes --- .../controls/carousel/ImageCarousel.java | 98 ++++++++----------- 1 file changed, 42 insertions(+), 56 deletions(-) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java index 7b9bc7b3b48..c9fc0be2892 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java @@ -1,33 +1,21 @@ package com.sap.sse.gwt.client.controls.carousel; -import java.util.LinkedList; - import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; -import com.google.gwt.core.client.ScriptInjector; import com.google.gwt.dom.client.ButtonElement; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.HeadElement; -import com.google.gwt.dom.client.LinkElement; -import com.google.gwt.dom.client.Node; -import com.google.gwt.dom.client.ScriptElement; import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; -import com.google.gwt.dom.client.StyleInjector; -import com.google.gwt.resources.client.ClientBundle; -import com.google.gwt.resources.client.TextResource; import com.google.gwt.safecss.shared.SafeStyles; import com.google.gwt.safecss.shared.SafeStylesBuilder; import com.google.gwt.safehtml.client.SafeHtmlTemplates; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.UriUtils; -import com.google.gwt.thirdparty.guava.common.base.Strings; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Command; -import com.google.gwt.user.client.Timer; import com.google.gwt.user.client.ui.Widget; /** @@ -37,8 +25,8 @@ import com.google.gwt.user.client.ui.Widget; * * @see Slick homepage. * @see Slick github page. - * - * + * + * *

* * This class uses JSNI to wrap the JS slick carousel. @@ -57,13 +45,13 @@ import com.google.gwt.user.client.ui.Widget; * slider.addImage("http://demotivators.despair.com/demotivational/destinyrockdemotivator.jpg"); * slider.addImage("http://demotivators.despair.com/demotivational/aspirationdemotivator.jpg"); * - * + * * The widget depends on css and js resources, as well as jquery 1.7+. * * To use the CDN resources, include: - * - * - * + * + * * * Created by pgtaboada on 10.11.14. */ @@ -74,10 +62,10 @@ public class ImageCarousel extends Widget { @UiField ButtonElement slickPrev; - + @UiField ButtonElement slickNext; - + /** * slick slider property: dots */ @@ -107,21 +95,18 @@ public class ImageCarousel extends Widget { * The height of the images */ private int imagesHeight = 400; - + private final String uniqueId; - - /** * Widget constructor */ public ImageCarousel() { setElement(ourUiBinder.createAndBindUi(this)); - uniqueId = "slider_"+Document.get().createUniqueId(); + uniqueId = "slider_" + Document.get().createUniqueId(); getElement().addClassName(uniqueId); - } /** @@ -131,33 +116,42 @@ public class ImageCarousel extends Widget { */ native void setupSlider(ImageCarousel sliderReference, ButtonElement prevArrowEl, ButtonElement nextArrowEl) /*-{ - $wnd.$(document).ready(function() { - $wnd.$('.'+ (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)).slick( - { - dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::showDots), - infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::infiniteScrolling), - centerMode : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::centerMode), - variableWidth : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::variableWidth), - lazyLoad : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::lazyload), - swipeToSlide : false, - arrows : true, - responsive: true, - slidesToShow: 3, - prevArrow : prevArrowEl, - nextArrow : nextArrowEl - }); - }); + $wnd + .$(document) + .ready( + function() { + $wnd + .$( + '.' + + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)) + .slick( + { + dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::showDots), + infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::infiniteScrolling), + centerMode : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::centerMode), + variableWidth : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::variableWidth), + lazyLoad : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::lazyload), + swipeToSlide : false, + arrows : true, + responsive : true, + slidesToShow : 3, + prevArrow : prevArrowEl, + nextArrow : nextArrowEl + }); + }); }-*/; - - native void addImage(ImageCarousel sliderReference, final DivElement el ) /*-{ - - $wnd.$( '.' + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)).slickAdd(el); - + native void addImage(ImageCarousel sliderReference, final DivElement el) /*-{ + + $wnd + .$( + '.' + + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)) + .slickAdd(el); + }-*/; - /** * Add new image to carousel. * @@ -173,14 +167,9 @@ public class ImageCarousel extends Widget { ssb.height(imagesHeight, Unit.PX); ssb.margin(contentMarginInPixels, Style.Unit.PX); - - final ImageCarousel reference = this; + imageHolder.setInnerSafeHtml(myTemplate.imageDiv(UriUtils.fromString(url).asString(), ssb.toSafeStyles())); - - getElement().appendChild(imageHolder); - //addImage( reference, imageHolder ); - } @@ -268,8 +257,6 @@ public class ImageCarousel extends Widget { } } - - /** * Template used to create image element */ @@ -278,7 +265,6 @@ public class ImageCarousel extends Widget { SafeHtml imageDiv(String uri, SafeStyles imageStyles); } - /** * Apply slick js to constructed dom tree. */ From d228c7a3a5ddc4b8c75f8bd84a238021e4489d84 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Tue, 2 Dec 2014 12:03:05 +0100 Subject: [PATCH 061/103] Initial stage implementation using slick slider instead of idangerous --- java/com.sap.sailing.gwt.ui/Home.html | 1 - ...WT Remote Profiling (Run-Mode only).launch | 2 +- java/com.sap.sailing.gwt.ui/SailingGWT.launch | 2 +- .../gwt/home/client/shared/stage/Stage.java | 87 ++---- .../gwt/home/client/shared/stage/Stage.ui.xml | 10 +- .../controls/carousel/ImageCarousel.ui.xml | 4 +- .../controls/carousel/WidgetCarousel.java | 271 ++++++++++++++++++ .../controls/carousel/WidgetCarousel.ui.xml | 54 ++++ 8 files changed, 359 insertions(+), 72 deletions(-) create mode 100644 java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java create mode 100644 java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml diff --git a/java/com.sap.sailing.gwt.ui/Home.html b/java/com.sap.sailing.gwt.ui/Home.html index 8d7f43d8760..da52a9e025f 100644 --- a/java/com.sap.sailing.gwt.ui/Home.html +++ b/java/com.sap.sailing.gwt.ui/Home.html @@ -27,7 +27,6 @@ - diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch b/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch index 0825d38b2c3..0f0f9a845e4 100644 --- a/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT Remote Profiling (Run-Mode only).launch @@ -51,5 +51,5 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT.launch b/java/com.sap.sailing.gwt.ui/SailingGWT.launch index 069ccfb3d6e..787da51a28a 100755 --- a/java/com.sap.sailing.gwt.ui/SailingGWT.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT.launch @@ -69,5 +69,5 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/Stage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/Stage.java index 4ec208a4d2c..defa6c771d3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/Stage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/Stage.java @@ -4,105 +4,66 @@ import java.util.ArrayList; import java.util.List; import com.google.gwt.core.client.GWT; -import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; -import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; -import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.home.client.app.HomePlacesNavigator; -import com.sap.sailing.gwt.idangerous.Swiper; import com.sap.sailing.gwt.ui.shared.EventBaseDTO; import com.sap.sse.common.Util.Pair; +import com.sap.sse.gwt.client.controls.carousel.WidgetCarousel; public class Stage extends Composite { - + @SuppressWarnings("unused") private List> featuredEvents; private List stageTeaserComposites; - @UiField HTMLPanel stageElementsPanel; - @UiField Anchor nextStageTeaserLink; - @UiField Anchor prevStageTeaserLink; + @UiField + WidgetCarousel widgetCarousel; private StageTeaser stageTeaser; - - private Swiper swiper; private final HomePlacesNavigator placeNavigator; - + interface StageUiBinder extends UiBinder { } - + private static StageUiBinder uiBinder = GWT.create(StageUiBinder.class); public Stage(HomePlacesNavigator placeNavigator) { this.placeNavigator = placeNavigator; - + StageResources.INSTANCE.css().ensureInjected(); initWidget(uiBinder.createAndBindUi(this)); - + stageTeaserComposites = new ArrayList(); } - + public void setFeaturedEvents(List> featuredEvents) { this.featuredEvents = featuredEvents; - - for(Pair typeAndEvent: featuredEvents) { + + for (Pair typeAndEvent : featuredEvents) { switch (typeAndEvent.getA()) { - case POPULAR: - stageTeaser = new PopularEventStageTeaser(typeAndEvent.getB(), placeNavigator); - break; - case RUNNING: - stageTeaser = new LiveEventStageTeaser(typeAndEvent.getB(), placeNavigator); - break; - case UPCOMING_SOON: - stageTeaser = new UpcomingEventStageTeaser(typeAndEvent.getB(), placeNavigator); - break; + case POPULAR: + stageTeaser = new PopularEventStageTeaser(typeAndEvent.getB(), placeNavigator); + break; + case RUNNING: + stageTeaser = new LiveEventStageTeaser(typeAndEvent.getB(), placeNavigator); + break; + case UPCOMING_SOON: + stageTeaser = new UpcomingEventStageTeaser(typeAndEvent.getB(), placeNavigator); + break; } - stageElementsPanel.add(stageTeaser); + widgetCarousel.addWidget(stageTeaser); stageTeaserComposites.add(stageTeaser); } - - if (featuredEvents.size() <= 1) { - prevStageTeaserLink.setVisible(false); - nextStageTeaserLink.setVisible(false); - } else { - swiper = Swiper.createWithoutLoopOption(StageResources.INSTANCE.css().stage_teasers(), - StageResources.INSTANCE.css().swiperwrapper(), - StageResources.INSTANCE.css().swiperslide(), new Swiper.PageChangeListener() { - - @Override - public void pageChanged(int newPageIndex, int pageCount) { - boolean isFirstSlide = newPageIndex == 0; - boolean isLastSlide = newPageIndex == pageCount - 1; - nextStageTeaserLink.setVisible(!isLastSlide); - prevStageTeaserLink.setVisible(!isFirstSlide); - } - }); - - prevStageTeaserLink.setVisible(false); - } + } - + public void adjustSize() { - swiper.reInit(); + } - @UiHandler("nextStageTeaserLink") - public void nextStageTeaserLinkClicked(ClickEvent e) { - if (this.swiper != null) { - this.swiper.swipeNext(); - } - } - - @UiHandler("prevStageTeaserLink") - public void prevStageTeaserLinkClicked(ClickEvent e) { - if (this.swiper != null) { - this.swiper.swipePrev(); - } - } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/Stage.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/Stage.ui.xml index ad1e6c63f55..fb6112ce521 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/Stage.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/Stage.ui.xml @@ -1,6 +1,10 @@ + xmlns:g="urn:import:com.google.gwt.user.client.ui" + xmlns:s="urn:import:com.sap.sailing.gwt.home.client.shared" + xmlns:sse="urn:import:com.sap.sse.gwt.client.controls" + + > @@ -9,13 +13,11 @@

- +
- -
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml index 2df50bc0788..2525c2bca12 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml @@ -20,7 +20,7 @@ right: -40px } .slick-prev:before { - color: #dddddd; + color: #aaaaaa; font-size: 30px; height: 30px; width: 30px; @@ -36,7 +36,7 @@ right: -50px; } .slick-next:before { - color: #dddddd; + color: #aaaaaa; font-size: 30px; height: 30px; width: 30px; diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java new file mode 100644 index 00000000000..d487477eac0 --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -0,0 +1,271 @@ +package com.sap.sse.gwt.client.controls.carousel; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.core.client.Scheduler; +import com.google.gwt.dom.client.ButtonElement; +import com.google.gwt.dom.client.Document; +import com.google.gwt.dom.client.Element; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.uibinder.client.UiBinder; +import com.google.gwt.uibinder.client.UiField; +import com.google.gwt.user.client.Command; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.HTMLPanel; +import com.google.gwt.user.client.ui.Widget; + +/** + * Image carousel that uses the open source "slick carousel". + *

+ * Documentation/ download of slick js and css from homepate/ github page. + * + * @see Slick homepage. + * @see Slick github page. + * + * + *

+ * + * This class uses JSNI to wrap the JS slick carousel. + * + * Usage: + * + * - Create widget + * + * ImageCarousel slider = new ImageCarousel(); + * + * - Add images + * + * slider.addImage("http://demotivators.despair.com/demotivational/foresightdemotivator.jpg"); + * slider.addImage("http://demotivators.despair.com/demotivational/commitmentdemotivator.jpg"); + * slider.addImage("http://demotivators.despair.com/demotivational/platitudesdemotivator.jpg"); + * slider.addImage("http://demotivators.despair.com/demotivational/destinyrockdemotivator.jpg"); + * slider.addImage("http://demotivators.despair.com/demotivational/aspirationdemotivator.jpg"); + * + * + * The widget depends on css and js resources, as well as jquery 1.7+. + * + * To use the CDN resources, include: + * + * + * + * Created by pgtaboada on 10.11.14. + */ +public class WidgetCarousel extends Composite { + + private static SlickSliderUiBinder ourUiBinder = GWT.create(SlickSliderUiBinder.class); + + @UiField + ButtonElement slickPrev; + + @UiField + ButtonElement slickNext; + + @UiField + HTMLPanel sliderMainUi; + + /** + * The height of the images + */ + private int imagesHeight = 400; + + /** + * slick slider property: dots + */ + private boolean showDots = true; + /** + * slick slider property: centerMode + */ + private boolean centerMode = true; + /** + * slick slider property: variableWidth + */ + private boolean variableWidth = true; + /** + * slick slider property: infiniteScrolling + */ + private boolean infiniteScrolling = false; + /** + * Image margin (effectively sets spacing between images) + */ + private int contentMarginInPixels = 10; + /** + * Lazy load mechanism to use + */ + private String lazyload = LAZYLOAD.ONDEMAND.getPropertyValue(); + + private final String uniqueId; + + /** + * Widget constructor + */ + public WidgetCarousel() { + initWidget(ourUiBinder.createAndBindUi(this)); + + uniqueId = Document.get().createUniqueId(); + + sliderMainUi.addStyleName(uniqueId); + + final WidgetCarousel reference = this; + Scheduler.get().scheduleDeferred(new Command() { + @Override + public void execute() { + try { + setupSlider(reference, slickPrev, slickNext); + GWT.log("Finished to init slider"); + } catch (Exception e) { + GWT.log("Catched Exception on slider init", e); + } + } + }); + } + + /** + * JSNI wrapper that does setup the slider + * + * @param uniqueId + */ + native void setupSlider(WidgetCarousel sliderReference, ButtonElement prevArrowEl, ButtonElement nextArrowEl) /*-{ + + $wnd + .$(document) + .ready( + function() { + $wnd + .$( + '.' + + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::uniqueId)) + .slick( + { + dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::showDots), + infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::infiniteScrolling), + swipeToSlide : false, + arrows : true, + responsive : false, + prevArrow : prevArrowEl, + nextArrow : nextArrowEl + }); + }); + + }-*/; + + /** + * Add new image to carousel. + * + * @param url + */ + public void addWidget(Widget slide) { + + sliderMainUi.add(slide); + + } + + /** + * Define spacing between images in carousel. In fact, it is setting the img margins in pixels. + * + * @param contentMarginInPixels + */ + public void setContentMarginInPixels(int contentMarginInPixels) { + this.contentMarginInPixels = contentMarginInPixels; + } + + /** + * Set lazy loading technique. Accepts 'ondemand' or 'progressive'. + * + * @param lazyload + */ + public void setLazyload(LAZYLOAD lazyload) { + this.lazyload = lazyload.getPropertyValue(); + } + + /** + * Enable infinite loop sliding + */ + public void setInfiniteScrolling(boolean infiniteScrolling) { + this.infiniteScrolling = infiniteScrolling; + } + + /** + * Enable variable width slides. + * + * @param variableWidth + */ + public void setVariableWidth(boolean variableWidth) { + this.variableWidth = variableWidth; + } + + /** + * Enables centered view with partial prev/next slides. + * + * @param centerMode + */ + public void setCenterMode(boolean centerMode) { + this.centerMode = centerMode; + } + + /** + * Enable dot indicators + * + * @param showDots + */ + public void setShowDots(boolean showDots) { + this.showDots = showDots; + } + + /** + * UiBinder interface + */ + interface SlickSliderUiBinder extends UiBinder { + } + + /** + * Enum wrapper for lazy load techniques + */ + public enum LAZYLOAD { + + /** + * Loads each image "on demand" + */ + ONDEMAND("ondemand"), + + /** + * Progressively loads one image after another + */ + PROGRESSIVE("progressive"); + + String propertyValue; + + LAZYLOAD(String propertyValue) { + this.propertyValue = propertyValue; + } + + public String getPropertyValue() { + return propertyValue; + } + } + + /** + * Apply slick js to constructed dom tree. + */ + public void init() { + final WidgetCarousel reference = this; + Scheduler.get().scheduleDeferred(new Command() { + @Override + public void execute() { + try { + setupSlider(reference, slickPrev, slickNext); + for (int i = 0; i < getElement().getChildCount(); i++) { + Element child = (Element) getElement().getChild(i); + String className = child.getClassName(); + if (className != null && className.contains("slick-list")) { + child.getStyle().setHeight(imagesHeight, Unit.PX); + } + } + GWT.log("Finished to init slider"); + } catch (Exception e) { + GWT.log("Catched Exception on slider init", e); + } + } + }); + } + +} \ No newline at end of file diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml new file mode 100644 index 00000000000..fe37d47fade --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml @@ -0,0 +1,54 @@ + + + + .slider { + } + + .slick-prev:before { + color: #dddddd; + font-size: 30px; + height: 30px; + width: 30px; + left: 0px; + } + + .slick-prev { + left: 0px; + z-index: 1; + } + + .slick-next { + right: 0px; + z-index: 1; + } + + .slick-next:before { + color: #dddddd; + font-size: 30px; + height: 30px; + width: 30px; + right: 0px; + } + + + + + + + + + + + + \ No newline at end of file From ddb0e64e891e6f21b893065b663ae4bc6898068a Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Tue, 2 Dec 2014 12:37:23 +0100 Subject: [PATCH 062/103] tuning of css for previous/ next buttons --- .../client/shared/mainmedia/MainMedia.ui.xml | 3 - .../controls/carousel/ImageCarousel.ui.xml | 51 ++++++++-------- .../controls/carousel/WidgetCarousel.ui.xml | 61 ++++++++----------- 3 files changed, 52 insertions(+), 63 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.ui.xml index 8ab81ceca94..6c747cb4be2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.ui.xml @@ -24,10 +24,7 @@

- -
-
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml index 2525c2bca12..423935cf265 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml @@ -7,42 +7,43 @@ .slider > ul > li > button:before { font-size: 15px; } + .slider img { margin: 10px; } - .slider slick-track { - } + - .slider button { - height: 40px; - width: 40px; - right: -40px - } - .slick-prev:before { - color: #aaaaaa; - font-size: 30px; - height: 30px; - width: 30px; - left: -50px; - } - .slick-prev { - left: -50px; - } - - + .slider { + width: 100%; + } + .slick-prev { + left: 20px; + z-index:1; + height: 60px; + width: 60px; + + } + .slick-prev:before { + color: #000000; + font-size: 60px; + left: -60px; + } .slick-next { - right: -50px; + right: 20px; + z-index:1; + height: 60px; + width: 60px; } .slick-next:before { - color: #aaaaaa; - font-size: 30px; - height: 30px; - width: 30px; - right: -50px; + color: #000000; + font-size: 60px; + left: -60px; } + +
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml index fe37d47fade..575d58d8f77 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml @@ -2,52 +2,43 @@ xmlns:g="urn:import:com.google.gwt.user.client.ui"> - .slider { - } - - .slick-prev:before { - color: #dddddd; - font-size: 30px; - height: 30px; - width: 30px; - left: 0px; - } - + .slider { + width: 100%; + } + .slick-prev { - left: 0px; - z-index: 1; + left: 20px; + z-index:1; + height: 60px; + width: 60px; + } - + .slick-prev:before { + color: #000000; + font-size: 60px; + left: -60px; + } .slick-next { - right: 0px; - z-index: 1; + right: 20px; + z-index:1; + height: 60px; + width: 60px; } - .slick-next:before { - color: #dddddd; - font-size: 30px; - height: 30px; - width: 30px; - right: 0px; + color: #000000; + font-size: 60px; + left: -60px; } - - - + + + From 0b32d9270d562697a2d1e662fb66fcc72b04032a Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Tue, 2 Dec 2014 15:01:46 +0100 Subject: [PATCH 063/103] Implemented lazy loading --- .../shared/stage/PopularEventStageTeaser.java | 9 +- .../home/client/shared/stage/StageTeaser.java | 76 ++++++++++++----- .../controls/carousel/LazyLoadable.java | 7 ++ .../controls/carousel/WidgetCarousel.java | 85 ++++++++++++++----- 4 files changed, 128 insertions(+), 49 deletions(-) create mode 100644 java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/LazyLoadable.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/PopularEventStageTeaser.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/PopularEventStageTeaser.java index 98ee80cf688..a685f1abcac 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/PopularEventStageTeaser.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/PopularEventStageTeaser.java @@ -11,11 +11,12 @@ public class PopularEventStageTeaser extends StageTeaser { title.setInnerText(event.getName()); subtitle.setInnerText(event.venue.getName()); - + countdown.getStyle().setDisplay(Display.NONE); - + bandCount.setAttribute("data-bandcount", "1"); - - stageTeaserBandsPanel.getElement().appendChild(new PopularEventStageTeaserBand(event, placeNavigator).getElement()); + + stageTeaserBandsPanel.getElement().appendChild( + new PopularEventStageTeaserBand(event, placeNavigator).getElement()); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaser.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaser.java index 090d002fe75..91679853988 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaser.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaser.java @@ -1,5 +1,6 @@ package com.sap.sailing.gwt.home.client.shared.stage; +import com.google.gwt.animation.client.Animation; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.SpanElement; @@ -16,39 +17,68 @@ import com.sap.sailing.gwt.home.client.shared.Countdown.RemainingTime; import com.sap.sailing.gwt.ui.shared.EventBaseDTO; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.gwt.client.controls.carousel.LazyLoadable; -public abstract class StageTeaser extends Composite { - @UiField DivElement bandCount; - @UiField SpanElement subtitle; - @UiField SpanElement title; - @UiField DivElement countdown; - @UiField DivElement countdownMajor; - @UiField DivElement countdownMajorValue; - @UiField DivElement countdownMajorUnit; - @UiField DivElement countdownMinor; - @UiField DivElement countdownMinorValue; - @UiField DivElement countdownMinorUnit; - @UiField HTMLPanel stageTeaserBandsPanel; - @UiField DivElement teaserImage; +public abstract class StageTeaser extends Composite implements LazyLoadable { + @UiField + DivElement bandCount; + @UiField + SpanElement subtitle; + @UiField + SpanElement title; + @UiField + DivElement countdown; + @UiField + DivElement countdownMajor; + @UiField + DivElement countdownMajorValue; + @UiField + DivElement countdownMajorUnit; + @UiField + DivElement countdownMinor; + @UiField + DivElement countdownMinorValue; + @UiField + DivElement countdownMinorUnit; + @UiField + HTMLPanel stageTeaserBandsPanel; + @UiField + DivElement teaserImage; interface StageTeaserUiBinder extends UiBinder { } - - private static StageTeaserUiBinder uiBinder = GWT.create(StageTeaserUiBinder.class); - public StageTeaser(EventBaseDTO event) { - StageResources.INSTANCE.css().ensureInjected(); - initWidget(uiBinder.createAndBindUi(this)); - - String stageImageUrl = event.getStageImageURL() != null ? event.getStageImageURL() : StageResources.INSTANCE.defaultStageEventTeaserImage().getSafeUri().asString();; - + private static StageTeaserUiBinder uiBinder = GWT.create(StageTeaserUiBinder.class); + private EventBaseDTO event; + + @Override + public void doInitializeLazyComponents() { + String stageImageUrl = event.getStageImageURL() != null ? event.getStageImageURL() : StageResources.INSTANCE + .defaultStageEventTeaserImage().getSafeUri().asString(); String backgroundImage = "url(" + stageImageUrl + ")"; teaserImage.getStyle().setBackgroundImage(backgroundImage); - + + teaserImage.getStyle().setOpacity(0); + new Animation() { + + @Override + protected void onUpdate(double progress) { + teaserImage.getStyle().setOpacity(progress); + + } + }.run(1000); + + } + + public StageTeaser(EventBaseDTO event) { + this.event = event; + StageResources.INSTANCE.css().ensureInjected(); + initWidget(uiBinder.createAndBindUi(this)); + if (event.startDate != null) { TimePoint eventStart = new MillisecondsTimePoint(event.startDate); CountdownListener countdownListener = new CountdownListener() { - + @Override public void changed(RemainingTime major, RemainingTime minor) { updateCountdown(major, minor); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/LazyLoadable.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/LazyLoadable.java new file mode 100644 index 00000000000..4219145ef91 --- /dev/null +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/LazyLoadable.java @@ -0,0 +1,7 @@ +package com.sap.sse.gwt.client.controls.carousel; + +public interface LazyLoadable { + + void doInitializeLazyComponents(); + +} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index d487477eac0..59650b968c0 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -1,5 +1,8 @@ package com.sap.sse.gwt.client.controls.carousel; +import java.util.Iterator; +import java.util.LinkedList; + import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.ButtonElement; @@ -95,6 +98,8 @@ public class WidgetCarousel extends Composite { private final String uniqueId; + private LinkedList items = new LinkedList(); + /** * Widget constructor */ @@ -117,6 +122,30 @@ public class WidgetCarousel extends Composite { } } }); + + } + + public void onAfterChange() { + + for (Iterator iterator = items.iterator(); iterator.hasNext();) { + Widget widget = (Widget) iterator.next(); + final Element parent = widget.getElement(); + GWT.log("checking widget...." + parent); + if (parent != null && parent.getClassName().contains("slick-active")) { + GWT.log("active widget...."); + if (iterator.hasNext()) { + Widget nextOne = iterator.next(); + if (nextOne instanceof LazyLoadable) { + LazyLoadable lazyLoadable = (LazyLoadable) nextOne; + GWT.log("Widgetcarousel lazy load trigger for: " + nextOne.getClass().getName()); + lazyLoadable.doInitializeLazyComponents(); + } else { + GWT.log("Next component ist not lazy loadable: " + nextOne.getClass().getName()); + } + return; + } + } + } } /** @@ -126,35 +155,47 @@ public class WidgetCarousel extends Composite { */ native void setupSlider(WidgetCarousel sliderReference, ButtonElement prevArrowEl, ButtonElement nextArrowEl) /*-{ - $wnd - .$(document) - .ready( - function() { - $wnd - .$( - '.' - + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::uniqueId)) - .slick( - { - dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::showDots), - infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::infiniteScrolling), - swipeToSlide : false, - arrows : true, - responsive : false, - prevArrow : prevArrowEl, - nextArrow : nextArrowEl - }); - }); + $wnd + .$(document) + .ready( + function() { + $wnd + .$( + '.' + + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::uniqueId)) + .slick( + { + dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::showDots), + infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::infiniteScrolling), + swipeToSlide : false, + arrows : true, + responsive : false, + prevArrow : prevArrowEl, + nextArrow : nextArrowEl, + onAfterChange : function() { + sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::onAfterChange()(); + }, + onInit : function() { + sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::onAfterChange()(); + } + }); + }); }-*/; /** - * Add new image to carousel. + * ƒ Add new image to carousel. * - * @param url + * @param urlƒ */ public void addWidget(Widget slide) { - + if (items.isEmpty()) { + if (slide instanceof LazyLoadable) { + LazyLoadable lazyLoadable = (LazyLoadable) slide; + lazyLoadable.doInitializeLazyComponents(); + } + } + items.add(slide); sliderMainUi.add(slide); } From e8598bb191e16c33b40fe6dd19bed7aaa98b4f38 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Tue, 2 Dec 2014 15:36:58 +0100 Subject: [PATCH 064/103] Moved anchor element one sibling up in the dom tree. float elements should come first (chrome is right) --- .../sailing/gwt/home/client/shared/stage/StageTeaserBand.ui.xml | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaserBand.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaserBand.ui.xml index b03eca5febb..553d63623ec 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaserBand.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaserBand.ui.xml @@ -6,6 +6,7 @@ +
@@ -13,6 +14,5 @@
-
From 734a29298b8541320cc926d320e810d0245c138c Mon Sep 17 00:00:00 2001 From: Frederik Petersen Date: Wed, 3 Dec 2014 10:39:31 +0100 Subject: [PATCH 065/103] Added x-y-diagram to polar sheets panel for now only for upwind starboard speed --- .../common/PolarSheetsXYDiagramData.java | 18 ++++ .../impl/PolarSheetsXYDiagramDataImpl.java | 49 ++++++++++ .../META-INF/MANIFEST.MF | 1 + .../sailing/gwt/ui/client/SailingService.java | 3 + .../gwt/ui/client/SailingServiceAsync.java | 3 + .../sailing/gwt/ui/client/StringMessages.java | 1 + .../gwt/ui/client/StringMessages.properties | 1 + .../AngleOverDataSizeHistogramPanel.java | 28 +++++- .../ui/polarsheets/PolarSheetsChartPanel.java | 14 +-- .../gwt/ui/polarsheets/PolarSheetsPanel.java | 24 +++++ .../PolarSheetsXYDiagramPopupPanel.java | 94 +++++++++++++++++++ .../gwt/ui/server/SailingServiceImpl.java | 40 ++++++++ .../polars/test/PolarSheetAnalyzerTest.java | 8 +- .../sap/sailing/polars/PolarDataService.java | 3 + .../polars/analysis/PolarSheetAnalyzer.java | 8 +- .../analysis/impl/PolarSheetAnalyzerImpl.java | 16 ++-- .../polars/impl/PolarDataServiceImpl.java | 14 ++- .../polars/regression/BoatSpeedEstimator.java | 14 ++- 18 files changed, 309 insertions(+), 30 deletions(-) create mode 100644 java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsXYDiagramData.java create mode 100644 java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetsXYDiagramDataImpl.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsXYDiagramPopupPanel.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsXYDiagramData.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsXYDiagramData.java new file mode 100644 index 00000000000..7b3116a66a6 --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/PolarSheetsXYDiagramData.java @@ -0,0 +1,18 @@ +package com.sap.sailing.domain.common; + +import java.io.Serializable; +import java.util.List; + +import com.sap.sse.common.Util.Pair; + +public interface PolarSheetsXYDiagramData extends Serializable { + + List> getPointsForUpwindStarboardAverageSpeed(); + + List> getPointsForUpwindStarboardAverageAngle(); + + List> getPointsForUpwindStarboardAverageAngleMovingAverage(); + + List> getPointsForUpwindStarboardAverageSpeedMovingAverage(); + +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetsXYDiagramDataImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetsXYDiagramDataImpl.java new file mode 100644 index 00000000000..cf09e43624a --- /dev/null +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/PolarSheetsXYDiagramDataImpl.java @@ -0,0 +1,49 @@ +package com.sap.sailing.domain.common.impl; + +import java.util.List; + +import com.sap.sailing.domain.common.PolarSheetsXYDiagramData; +import com.sap.sse.common.Util.Pair; + +public class PolarSheetsXYDiagramDataImpl implements PolarSheetsXYDiagramData { + + private static final long serialVersionUID = 778667444303004468L; + + PolarSheetsXYDiagramDataImpl() {} + + private List> pointsForUpwindStarboardAverageAngle; + private List> pointsForUpwindStarboardAverageSpeed; + private List> pointsForUpwindStarboardAverageAngleMovingAverage; + private List> pointsForUpwindStarboardAverageSpeedMovingAverage; + + public PolarSheetsXYDiagramDataImpl(List> pointsForUpwindStarboardAverageAngle, + List> pointsForUpwindStarboardAverageSpeed, + List> pointsForUpwindStarboardAverageAngleMovingAverage, + List> pointsForUpwindStarboardAverageSpeedMovingAverage) { + this.pointsForUpwindStarboardAverageAngle = pointsForUpwindStarboardAverageAngle; + this.pointsForUpwindStarboardAverageSpeed = pointsForUpwindStarboardAverageSpeed; + this.pointsForUpwindStarboardAverageAngleMovingAverage = pointsForUpwindStarboardAverageAngleMovingAverage; + this.pointsForUpwindStarboardAverageSpeedMovingAverage = pointsForUpwindStarboardAverageSpeedMovingAverage; + } + + @Override + public List> getPointsForUpwindStarboardAverageAngle() { + return pointsForUpwindStarboardAverageAngle; + } + + @Override + public List> getPointsForUpwindStarboardAverageSpeed() { + return pointsForUpwindStarboardAverageSpeed; + } + + @Override + public List> getPointsForUpwindStarboardAverageAngleMovingAverage() { + return pointsForUpwindStarboardAverageAngleMovingAverage; + } + + @Override + public List> getPointsForUpwindStarboardAverageSpeedMovingAverage() { + return pointsForUpwindStarboardAverageSpeedMovingAverage; + } + +} 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 8e55b20f2a4..4949c4fb85a 100755 --- a/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.gwt.ui/META-INF/MANIFEST.MF @@ -46,6 +46,7 @@ Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator Bundle-ActivationPolicy: lazy Import-Package: com.sap.sailing.polars, com.sap.sailing.polars.factory, + com.sap.sailing.polars.regression, javax.jms;version="1.1.0", javax.servlet;version="3.1.0", javax.servlet.descriptor;version="3.1.0", 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 0b36ef3d1b9..1d35af0401e 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 @@ -18,6 +18,7 @@ import com.sap.sailing.domain.common.NoWindException; import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PolarSheetGenerationResponse; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.PolarSheetsXYDiagramData; import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier; @@ -533,4 +534,6 @@ public interface SailingService extends RemoteService { * @return The RaceDTO of the modified race or null, if the given newStartTimeReceived was null. */ RaceDTO setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived); + + PolarSheetsXYDiagramData createXYDiagramForBoatClass(String itemText); } 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 559b9e66cf2..e239ee5825d 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 @@ -16,6 +16,7 @@ import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PolarSheetGenerationResponse; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; +import com.sap.sailing.domain.common.PolarSheetsXYDiagramData; import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier; @@ -646,4 +647,6 @@ public interface SailingServiceAsync extends BuildVersionRetriever { AsyncCallback> callback); void setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived, AsyncCallback callback); + + void createXYDiagramForBoatClass(String itemText, 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 be8d11e2f6b..7a61c4acc5a 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 @@ -1115,4 +1115,5 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages { String serverReplies(); String errorCommunicatingWithServer(); String userManagement(); + String showXYDiagram(); } 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 2962d951a49..701fd023a5d 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 @@ -1095,3 +1095,4 @@ remoteProcedureCall=Remote Procedure Call serverReplies=Server replies errorCommunicatingWithServer=Error communicating with server userManagement=User Management +showXYDiagram=Show x-y-diagram 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 index f5f62742d29..187fc1727ed 100644 --- 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 @@ -1,6 +1,9 @@ package com.sap.sailing.gwt.ui.polarsheets; +import java.util.HashMap; +import java.util.HashSet; import java.util.Map; +import java.util.Set; import org.moxieapps.gwt.highcharts.client.AxisTitle; import org.moxieapps.gwt.highcharts.client.Chart; @@ -29,9 +32,13 @@ public class AngleOverDataSizeHistogramPanel extends DockLayoutPanel { private final Chart chart; private final StringMessages stringMessages; + + private final Map> seriesForId; public AngleOverDataSizeHistogramPanel(StringMessages stringMessages) { super(Unit.PX); + + this.seriesForId = new HashMap<>(); this.stringMessages = stringMessages; setSize("100%", "100%"); @@ -51,13 +58,20 @@ public class AngleOverDataSizeHistogramPanel extends DockLayoutPanel { return histogramChart; } - public void addData(Map histogramDataPerAngle) { + public void addData(Map histogramDataPerAngle, String seriesId, String actualSeriesName) { chart.setTitle(""); Series series = chart.createSeries(); Point[] points = toPoints(histogramDataPerAngle); series.setPoints(points); + series.setName(actualSeriesName); chart.addSeries(series); + + if (!seriesForId.containsKey(seriesId)) { + seriesForId.put(seriesId, new HashSet()); + } + Set seriesSet = seriesForId.get(seriesId); + seriesSet.add(series); } private Point[] toPoints(Map histogramDataPerAngle) { @@ -97,6 +111,18 @@ public class AngleOverDataSizeHistogramPanel extends DockLayoutPanel { chart.setSizeToMatchContainer(); super.onResize(); } + + public void removeSeries(String seriesId) { + for (Series series : seriesForId.get(seriesId)) { + chart.removeSeries(series); + } + seriesForId.remove(seriesId); + } + + public void removeAllSeries() { + chart.removeAllSeries(); + seriesForId.clear(); + } 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 4fa726aa74e..f21fe20539f 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 @@ -93,13 +93,12 @@ public class PolarSheetsChartPanel extends DockLayoutPanel { * @param windSpeedLevel The id of the windspeed level in the windspeed steppings * @param windSpeed The actual windspeed */ - private void createSeriesForWindspeed(String name, int windSpeedLevel, int windSpeed) { + private void createSeriesForWindspeed(String name, int windSpeedLevel, String seriesName) { Series[] seriesPerWindSpeed = seriesMap.get(name); Number[] forEachDeg = initializeDataForNewSeries(); seriesPerWindSpeed[windSpeedLevel] = chart.createSeries().setPoints(forEachDeg); - String actualSeriesName = name + "-" + windSpeed; - seriesPerWindSpeed[windSpeedLevel].setName(actualSeriesName); - nameForSeries.put(seriesPerWindSpeed[windSpeedLevel],actualSeriesName); + seriesPerWindSpeed[windSpeedLevel].setName(seriesName); + nameForSeries.put(seriesPerWindSpeed[windSpeedLevel],seriesName); chart.addSeries(seriesPerWindSpeed[windSpeedLevel]); } @@ -120,15 +119,16 @@ public class PolarSheetsChartPanel extends DockLayoutPanel { if (seriesMap.containsKey(seriesId)) { for (int i = 0; i < stepCount; i++) { if (hasSufficientDataForWindspeed(result.getDataCountPerAngleForWindspeed(i))) { + String actualSeriesName = seriesId + "-" + result.getStepping().getRawStepping()[i]; if (seriesMap.get(seriesId)[i] == null) { - createSeriesForWindspeed(seriesId, i, (int) Math.round(result.getStepping().getRawStepping()[i])); + createSeriesForWindspeed(seriesId, i, actualSeriesName); } Series series = seriesMap.get(seriesId)[i]; //series.setPoints(result.getAveragedPolarDataByWindSpeed()[i], false); Point[] points = createPointsWithMarkerAlphaAccordingToDataCount(result, i); if (points != null) { series.setPoints(points); - angleOverDataSizeHistogramPanel.addData(result.getHistogramDataMap().get(i)); + angleOverDataSizeHistogramPanel.addData(result.getHistogramDataMap().get(i), seriesId, actualSeriesName); } } } @@ -209,6 +209,7 @@ public class PolarSheetsChartPanel extends DockLayoutPanel { } seriesMap.remove(seriesId); polarSheetsDataMap.remove(seriesId); + angleOverDataSizeHistogramPanel.removeSeries(seriesId); } } @@ -219,6 +220,7 @@ public class PolarSheetsChartPanel extends DockLayoutPanel { chart.removeAllSeries(); seriesMap.clear(); polarSheetsDataMap.clear(); + angleOverDataSizeHistogramPanel.clear(); } /** 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 9005b3a9af9..1494ce70446 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 @@ -26,6 +26,7 @@ import com.sap.sailing.domain.common.PolarSheetGenerationResponse; 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.PolarSheetsXYDiagramData; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.WindStepping; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; @@ -158,6 +159,29 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC } }); leftPanel.add(showBoatClassDiagramButton); + + Button showXYDiagramsButton = new Button(stringMessages.showXYDiagram()); + showXYDiagramsButton.addClickHandler(new ClickHandler() { + + @Override + public void onClick(ClickEvent event) { + sailingService.createXYDiagramForBoatClass( + boatClassListBox.getItemText(boatClassListBox.getSelectedIndex()), new AsyncCallback() { + + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError(caught.getLocalizedMessage()); + } + + @Override + public void onSuccess(PolarSheetsXYDiagramData result) { + PolarSheetsXYDiagramPopupPanel popupPanel = new PolarSheetsXYDiagramPopupPanel(stringMessages, result); + popupPanel.show(); + } + }); + } + }); + leftPanel.add(showXYDiagramsButton); } private ListBox createBoatClassListBox() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsXYDiagramPopupPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsXYDiagramPopupPanel.java new file mode 100644 index 00000000000..eb5d47c8ad9 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/polarsheets/PolarSheetsXYDiagramPopupPanel.java @@ -0,0 +1,94 @@ +package com.sap.sailing.gwt.ui.polarsheets; + +import java.util.List; + +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 com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.Timer; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.DialogBox; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.sap.sailing.domain.common.PolarSheetsXYDiagramData; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.common.Util.Pair; + +public class PolarSheetsXYDiagramPopupPanel extends DialogBox { + + + private final Chart chart; + + public PolarSheetsXYDiagramPopupPanel(StringMessages stringMessages, PolarSheetsXYDiagramData result) { + chart = createChart(); + VerticalPanel containerPanel = new VerticalPanel(); + containerPanel.setWidth("100%"); + containerPanel.setHeight("100%"); + this.add(containerPanel); + containerPanel.add(chart); + + + Button closeButton = new Button(stringMessages.close()); + closeButton.addClickHandler(new ClickHandler() { + + @Override + public void onClick(ClickEvent event) { + PolarSheetsXYDiagramPopupPanel.this.hide(false); + } + }); + containerPanel.add(closeButton); + + Point[] pointsForUpwindStarboardAverageAngle = toPointArray(result.getPointsForUpwindStarboardAverageAngle()); + Series angleSeries = chart.createSeries(); + angleSeries.setPoints(pointsForUpwindStarboardAverageAngle); + //chart.addSeries(angleSeries); + + Point[] pointsForUpwindStarboardAverageSpeed = toPointArray(result.getPointsForUpwindStarboardAverageSpeed()); + Series speedSeries = chart.createSeries(); + speedSeries.setName("Upwind Starboard Speed - LinearRegressions"); + speedSeries.setPoints(pointsForUpwindStarboardAverageSpeed); + chart.addSeries(speedSeries); + + Point[] pointsForUpwindStarboardAverageSpeedMovingAverage = toPointArray(result.getPointsForUpwindStarboardAverageSpeedMovingAverage()); + Series speedSeriesMovingAverage = chart.createSeries(); + speedSeriesMovingAverage.setName("Upwind Starboard Speed - MovingAverage"); + speedSeriesMovingAverage.setPoints(pointsForUpwindStarboardAverageSpeedMovingAverage); + chart.addSeries(speedSeriesMovingAverage); + + this.setWidth("1200px"); + this.setHeight("800px"); + } + + private Point[] toPointArray(List> pointsForUpwindStarboardAverageAngle) { + Point[] points = new Point[pointsForUpwindStarboardAverageAngle.size()]; + int i = 0; + for (Pair point : pointsForUpwindStarboardAverageAngle) { + points[i] = new Point(point.getA(), point.getB()); + i++; + } + return points; + } + + private Chart createChart() { + Chart chart = new Chart().setType(Type.LINE); + chart.setHeight("80%"); + return chart; + } + + @Override + protected void onLoad() { + Timer timer = new Timer() { + + @Override + public void run() { + chart.setSizeToMatchContainer(); + } + }; + timer.schedule(200); + super.onLoad(); + } + +} 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 f08cb3db26f..6b66654b926 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 @@ -77,6 +77,7 @@ import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.RemoteSailingServerReference; import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Sideline; +import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.configuration.DeviceConfiguration; import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher; @@ -116,6 +117,7 @@ import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PolarSheetGenerationResponse; import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetsData; +import com.sap.sailing.domain.common.PolarSheetsXYDiagramData; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RaceFetcher; import com.sap.sailing.domain.common.RaceIdentifier; @@ -158,6 +160,7 @@ import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.impl.PolarSheetGenerationResponseImpl; +import com.sap.sailing.domain.common.impl.PolarSheetsXYDiagramDataImpl; import com.sap.sailing.domain.common.impl.TimeRangeImpl; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.media.MediaTrack; @@ -329,6 +332,7 @@ import com.sap.sailing.manage2sail.Manage2SailEventResultsParserImpl; import com.sap.sailing.manage2sail.RaceResultDescriptor; import com.sap.sailing.manage2sail.RegattaResultDescriptor; import com.sap.sailing.polars.PolarDataService; +import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; import com.sap.sailing.resultimport.ResultUrlProvider; import com.sap.sailing.resultimport.ResultUrlRegistry; import com.sap.sailing.server.RacingEventService; @@ -5107,4 +5111,40 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } return null; } + + @Override + public PolarSheetsXYDiagramData createXYDiagramForBoatClass(String boatClassName) { + BoatClass boatClass = getService().getBaseDomainFactory().getOrCreateBoatClass(boatClassName); + List> pointsForUpwindStarboardAverageSpeed = new ArrayList>(); + List> pointsForUpwindStarboardAverageAngle = new ArrayList>(); + List> pointsForUpwindStarboardAverageSpeedMovingAverage = new ArrayList>(); + List> pointsForUpwindStarboardAverageAngleMovingAverage = new ArrayList>(); + for (double windInKnots = 0.1; windInKnots < 30; windInKnots = windInKnots + 0.1) { + try { + SpeedWithBearingWithConfidence averageUpwindStarboard = getService().getPolarDataService() + .getAverageSpeedWithBearing(boatClass, new KnotSpeedImpl(windInKnots), LegType.UPWIND, + Tack.STARBOARD, true); + pointsForUpwindStarboardAverageSpeed.add(new Pair(windInKnots, averageUpwindStarboard + .getObject().getKnots())); + pointsForUpwindStarboardAverageAngle.add(new Pair(windInKnots, averageUpwindStarboard + .getObject().getBearing().getDegrees())); + + SpeedWithBearingWithConfidence averageUpwindStarboardMovingAverage = getService().getPolarDataService() + .getAverageSpeedWithBearing(boatClass, new KnotSpeedImpl(windInKnots), LegType.UPWIND, + Tack.STARBOARD, false); + pointsForUpwindStarboardAverageSpeedMovingAverage.add(new Pair(windInKnots, + averageUpwindStarboardMovingAverage.getObject().getKnots())); + pointsForUpwindStarboardAverageAngleMovingAverage.add(new Pair(windInKnots, + averageUpwindStarboardMovingAverage.getObject().getBearing().getDegrees())); + + } catch (NotEnoughDataHasBeenAddedException e) { + // Do not add a point to the result + } + } + PolarSheetsXYDiagramData data = new PolarSheetsXYDiagramDataImpl(pointsForUpwindStarboardAverageAngle, + pointsForUpwindStarboardAverageSpeed, pointsForUpwindStarboardAverageAngleMovingAverage, + pointsForUpwindStarboardAverageSpeedMovingAverage); + + return data; + } } 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 25d148b0e03..bc49367a62a 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,25 +30,25 @@ public class PolarSheetAnalyzerTest { BoatClass boatClass = mock(BoatClass.class); SpeedWithBearingWithConfidence result = analyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, - new KnotSpeedImpl(14)); + new KnotSpeedImpl(14), true); 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)); SpeedWithBearingWithConfidence result2 = analyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, - new KnotSpeedImpl(14)); + new KnotSpeedImpl(14), true); 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)); SpeedWithBearingWithConfidence result3 = analyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, - new KnotSpeedImpl(14)); + new KnotSpeedImpl(14), true); 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)); SpeedWithBearingWithConfidence result4 = analyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, - new KnotSpeedImpl(14)); + new KnotSpeedImpl(14), true); 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)); 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 19d5aaf6e11..679e7dc33ed 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 @@ -126,6 +126,9 @@ public interface PolarDataService { * @return array with datacount for all angles in the given area, else null */ Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive); + + SpeedWithBearingWithConfidence getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed, + LegType legType, Tack tack, boolean useLinReg) 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 70a6ae0bddc..b0e82141562 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,15 +8,15 @@ import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException; public interface PolarSheetAnalyzer { SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, - Speed windSpeed) throws NotEnoughDataHasBeenAddedException; + Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException; SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, - Speed windSpeed) throws NotEnoughDataHasBeenAddedException; + Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException; SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, - Speed windSpeed) throws NotEnoughDataHasBeenAddedException; + Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException; SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, - Speed windSpeed) throws NotEnoughDataHasBeenAddedException; + Speed windSpeed, boolean useLinReg) 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 15fffc78e83..4d26f83e94d 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 @@ -33,43 +33,43 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer { } @Override - public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 1; int endAngleExclusive = 90; SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, - endAngleExclusive, true); + endAngleExclusive, useLinReg); return speedWithBearing; } @Override - public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 91; int endAngleExclusive = 180; SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, - endAngleExclusive, true); + endAngleExclusive, useLinReg); return speedWithBearing; } @Override - public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 181; int endAngleExclusive = 270; SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, - endAngleExclusive, true); + endAngleExclusive, useLinReg); return speedWithBearing; } @Override - public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) + public SpeedWithBearingWithConfidence getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive = 271; int endAngleExclusive = 360; SpeedWithBearingWithConfidence speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, - endAngleExclusive, true); + endAngleExclusive, useLinReg); return speedWithBearing; } 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 62653587319..d669092b27d 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 @@ -67,18 +67,24 @@ public class PolarDataServiceImpl implements PolarDataService { @Override public SpeedWithBearingWithConfidence getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { + return getAverageSpeedWithBearing(boatClass, windSpeed, legType, tack, true); + } + + @Override + public SpeedWithBearingWithConfidence getAverageSpeedWithBearing(BoatClass boatClass, + Speed windSpeed, LegType legType, Tack tack, boolean useLinReg) throws NotEnoughDataHasBeenAddedException { SpeedWithBearingWithConfidence speedWithBearing = null; if (tack.equals(Tack.STARBOARD)) { if (legType.equals(LegType.UPWIND)) { - speedWithBearing = polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + speedWithBearing = polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed, useLinReg); } else if (legType.equals(LegType.DOWNWIND)) { - speedWithBearing = polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); + speedWithBearing = polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed, useLinReg); } } else if (tack.equals(Tack.PORT)) { if (legType.equals(LegType.UPWIND)) { - return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed, useLinReg); } else if (legType.equals(LegType.DOWNWIND)) { - return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); + return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed, useLinReg); } } return speedWithBearing; 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 01a486ebd69..2d175578be3 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 @@ -40,13 +40,21 @@ public class BoatSpeedEstimator { */ public double estimateSpeed(double windSpeed, double angleToTheWind, boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException { lock.readLock().lock(); + double result; try { - double angleEstimated = angleRegression.getEstimatedY(angleToTheWind); - double windSpeedEstimated = windSpeedRegression.getEstimatedY(windSpeed); - return (angleEstimated + windSpeedEstimated) / 2.0; + if (useLinearRegression) { + double angleEstimated = angleRegression.getEstimatedY(angleToTheWind); + double windSpeedEstimated = windSpeedRegression.getEstimatedY(windSpeed); + + result = (angleEstimated + windSpeedEstimated) / 2.0; + } else { + //Return average for that wind interval + result = windSpeedRegression.getMeanOfY(); + } } finally { lock.readLock().unlock(); } + return result; } public void addData(double windSpeed, double angleToTheWind, double boatSpeed, double confidence) { From dc0919d01244411eec00195515cc5bd0dd78e4ef Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Wed, 3 Dec 2014 10:53:56 +0100 Subject: [PATCH 066/103] Introduced dedicated sailing loves photography image selection with size and stage image check --- .../client/shared/mainmedia/MainMedia.java | 58 +++---- .../sailing/gwt/ui/shared/EventBaseDTO.java | 144 ++++++++++-------- .../sap/sailing/gwt/ui/shared/EventDTO.java | 128 ++++++++-------- 3 files changed, 179 insertions(+), 151 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java index dbfa31948ec..beb2d561855 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java @@ -9,51 +9,50 @@ import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.DivElement; -import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; -import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.HTMLPanel; -import com.google.gwt.user.client.ui.SimplePanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.home.client.app.PlaceNavigator; import com.sap.sailing.gwt.home.client.shared.stage.StageEventType; -import com.sap.sailing.gwt.idangerous.Swiper; import com.sap.sailing.gwt.ui.common.client.YoutubeApi; import com.sap.sailing.gwt.ui.shared.EventBaseDTO; import com.sap.sse.common.Util.Pair; import com.sap.sse.gwt.client.controls.carousel.ImageCarousel; public class MainMedia extends Composite { - + private static final int MAX_VIDEO_COUNT = 3; - private static final MainMediaResources.LocalCss STYLES = MainMediaResources.INSTANCE.css(); + private static final MainMediaResources.LocalCss STYLES = MainMediaResources.INSTANCE.css(); - @UiField HTMLPanel videosPanel; - @UiField DivElement videoLightBoxData; - - @UiField ImageCarousel imageCarousel; + @UiField + HTMLPanel videosPanel; + @UiField + DivElement videoLightBoxData; - // private Swiper swiper; + @UiField + ImageCarousel imageCarousel; + + // private Swiper swiper; private int videoCounter; - + interface MainMediaUiBinder extends UiBinder { } - + private static MainMediaUiBinder uiBinder = GWT.create(MainMediaUiBinder.class); public MainMedia(PlaceNavigator navigator) { videoCounter = 0; - + MainMediaResources.INSTANCE.css().ensureInjected(); initWidget(uiBinder.createAndBindUi(this)); } public void setFeaturedEvents(List> featuredEvents) { - for(Pair featuredEventTypeAndEvent: featuredEvents) { - if(featuredEventTypeAndEvent.getB().getVideoURLs().size() > 0 && videoCounter < MAX_VIDEO_COUNT) { + for (Pair featuredEventTypeAndEvent : featuredEvents) { + if (featuredEventTypeAndEvent.getB().getVideoURLs().size() > 0 && videoCounter < MAX_VIDEO_COUNT) { addVideoToVideoPanel(featuredEventTypeAndEvent.getB()); } } @@ -62,7 +61,9 @@ public class MainMedia extends Composite { public void setRecentEvents(List recentEvents) { List photoGalleryUrls = new ArrayList(); for (EventBaseDTO event : recentEvents) { + photoGalleryUrls.addAll(event.getPhotoGalleryImageURLs()); + if (!event.getVideoURLs().isEmpty() && videoCounter < MAX_VIDEO_COUNT) { addVideoToVideoPanel(event); } @@ -74,22 +75,25 @@ public class MainMedia extends Composite { Collections.swap(photoGalleryUrls, i, random.nextInt(gallerySize)); } for (String url : photoGalleryUrls) { - -// SimplePanel imageContainer = new SimplePanel(); -// imageContainer.addStyleName(STYLES.media_swiperslide()); -// String image = "url(" + url + ")"; -// imageContainer.getElement().getStyle().setBackgroundImage(image); - imageCarousel.addImage(url); + + // SimplePanel imageContainer = new SimplePanel(); + // imageContainer.addStyleName(STYLES.media_swiperslide()); + // String image = "url(" + url + ")"; + // imageContainer.getElement().getStyle().setBackgroundImage(image); + imageCarousel.addImage(url); } imageCarousel.init(); - // this.swiper = Swiper.createWithLoopOption(STYLES.media_swipecontainer(), STYLES.media_swipewrapper(), STYLES.media_swiperslide()); - // See bug 2232: the stage image sizes are scaled incorrectly. https://github.com/ubilabs/sap-sailing-analytics/issues/421 and - // http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=2232 have the details. A quick fix may be to send a resize event + // this.swiper = Swiper.createWithLoopOption(STYLES.media_swipecontainer(), STYLES.media_swipewrapper(), + // STYLES.media_swiperslide()); + // See bug 2232: the stage image sizes are scaled incorrectly. + // https://github.com/ubilabs/sap-sailing-analytics/issues/421 and + // http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=2232 have the details. A quick fix may be to send a + // resize event // after everything has been rendered. Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { - // swiper.reInit(); + // swiper.reInit(); } }); } @@ -104,7 +108,7 @@ public class MainMedia extends Composite { videoCounter++; } } - + private String getRandomVideoURL(EventBaseDTO event) { final String result; List videoURLs = event.getVideoURLs(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java index 85bc4083c1e..32112437ae4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java @@ -5,6 +5,7 @@ import java.util.Collections; import java.util.Comparator; import java.util.Date; import java.util.HashMap; +import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.UUID; @@ -15,6 +16,7 @@ import com.sap.sailing.domain.common.dto.NamedDTO; import com.sap.sse.common.Util; public class EventBaseDTO extends NamedDTO implements IsSerializable { + private static final int MINIMUM_IMAGE_HEIGHT_FOR_SAILING_PHOTOGRAPHY_IN_PIXELS = 500; private static final long serialVersionUID = 818666323178097939L; private static final String STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE = "stage"; private static final String THUMBNAIL_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE = "eventteaser"; @@ -35,36 +37,37 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { /** placeholder for social media URL's -> attributes will be implemented later on */ private String facebookURL; private String twitterURL; - - - + + private String lastThumbnail; + /** - * For the image URL keys holds the sizes of these images if known. An image size is "known" by this object if it was - * provided to the {@link #setImageSize} method. + * For the image URL keys holds the sizes of these images if known. An image size is "known" by this object if it + * was provided to the {@link #setImageSize} method. */ private Map imageSizes; - + /** * The base URL for the server instance on which the data for this event can be reached. Could be something like * http://sapsailing.com for archived events that will forever remain in the archive, or * http://danishleague2014.sapsailing.com for other events that may not yet be archived or may change - * servers at any time in the future, therefore requiring a dedicated stable URL that the Apache server can - * resolve to the correct host IP and Java server instance. + * servers at any time in the future, therefore requiring a dedicated stable URL that the Apache server can resolve + * to the correct host IP and Java server instance. */ private String baseURL; - + /** - * Indicates whether the event is hosted on a remote server or not + * Indicates whether the event is hosted on a remote server or not */ private boolean isOnRemoteServer; - EventBaseDTO() {} // for serialization only - + EventBaseDTO() { + } // for serialization only + public EventBaseDTO(List leaderboardGroups) { this.leaderboardGroups = leaderboardGroups; this.imageSizes = new HashMap(); } - + public EventBaseDTO(String name, List leaderboardGroups) { super(name); this.leaderboardGroups = leaderboardGroups; @@ -73,12 +76,12 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { public boolean isRunning() { Date now = new Date(); - if(startDate != null && endDate != null && (now.after(startDate) && now.before(endDate))) { + if (startDate != null && endDate != null && (now.after(startDate) && now.before(endDate))) { return true; } return false; } - + public String getDescription() { return description; } @@ -119,7 +122,7 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { public void addImageURL(String imageURL) { imageURLs.add(imageURL); } - + public void addVideoURL(String videoURL) { videoURLs.add(videoURL); } @@ -133,11 +136,14 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { } /** - * The stage image is determined from the {@link #imageURLs} collection by a series of heuristics and fall-back rules: + * The stage image is determined from the {@link #imageURLs} collection by a series of heuristics and fall-back + * rules: *
    *
  1. If one or more image URLs has "stage" (ignoring case) in its name, only they are considered candidates.
  2. - *
  3. If no image URL has "stage" (ignoring case) in its name, all images from {@link #imageURLs} are considered candidates.
  4. - *
  5. From all candidates, the one with the biggest known size (determined by the product of width and height) is chosen.
  6. + *
  7. If no image URL has "stage" (ignoring case) in its name, all images from {@link #imageURLs} are considered + * candidates.
  8. + *
  9. From all candidates, the one with the biggest known size (determined by the product of width and height) is + * chosen.
  10. *
  11. If the size isn't known for any candidate, the first candidate in {@link #imageURLs} is picked.
  12. *
*/ @@ -194,24 +200,33 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { } return result; } - - /** - * - * @return - */ - private String lastThumbnail; + + public LinkedList getSailingLovesPhotographyImages() { + + final LinkedList acceptedImages = new LinkedList(); + for (String candidateImageUrl : acceptedImages) { + ImageSize imageSize = getImageSize(candidateImageUrl); + if (imageSize.getHeight() > MINIMUM_IMAGE_HEIGHT_FOR_SAILING_PHOTOGRAPHY_IN_PIXELS + && !candidateImageUrl.toLowerCase().contains(STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { + + acceptedImages.add(candidateImageUrl); + + } + } + return acceptedImages; + } + public String getEventThumbnailImageUrl() { if (lastThumbnail == null) { lastThumbnail = findEventThumbnailImageUrl(); } return lastThumbnail; } - - + private String findEventThumbnailImageUrl() { - + final class ImageHolder { - + final int PERFECT_HEIGHT = 240; final int PERFECT_WIDTH = 370; final double PERFECT_RATIO = PERFECT_HEIGHT / (double) PERFECT_WIDTH; @@ -220,79 +235,88 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { final int width; final int size; final double ratio; - + ImageHolder() { this.url = ""; this.height = -1; this.width = -1; this.size = -1; - this.ratio = -1; + this.ratio = -1; } + ImageHolder(String url, ImageSize imageSize) { - assert url != null: "url must not be null"; + assert url != null : "url must not be null"; assert imageSize != null : "imageSize must not be null"; this.url = url; this.height = imageSize.getHeight(); this.width = imageSize.getWidth(); this.size = height * width; - this.ratio = height/ (double)width; + this.ratio = height / (double) width; } - + boolean isBetterWorstcaseThan(ImageHolder otherImageHolder) { + if (isNull()) { return false; } + + if (url.contains(STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { + return false; + } + if (otherImageHolder.isNull()) { return true; } + if (!isBigEnough() && otherImageHolder.isBigEnough()) { return true; } - if (this.fitsRatio() && otherImageHolder.fitsRatio() && otherImageHolder.isBigEnough() && otherImageHolder.isSmallerThan(this)) { - return true; - } + if (this.fitsRatio() && otherImageHolder.fitsRatio() && otherImageHolder.isBigEnough() + && otherImageHolder.isSmallerThan(this)) { + return true; + } return false; } - + boolean isSmallerThan(ImageHolder otherImageHolder) { return size < otherImageHolder.size; } - + boolean isBigEnough() { return height >= PERFECT_HEIGHT && width >= PERFECT_WIDTH; } - + boolean fitsRatio() { return ratio == PERFECT_RATIO; } - + boolean isPerfectFit() { - return ( height == PERFECT_HEIGHT && width ==PERFECT_WIDTH ); + return (height == PERFECT_HEIGHT && width == PERFECT_WIDTH); } - + boolean isNull() { return size == -1; } } - + // search for name pattern for (String imageUrl : imageURLs) { - if (imageUrl != null && imageUrl.toLowerCase().contains(THUMBNAIL_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { - return imageUrl; - } - } - - - ImageHolder actualWorstcase = new ImageHolder( ); - ImageHolder bestFit = new ImageHolder( ); - + if (imageUrl != null + && imageUrl.toLowerCase().contains(THUMBNAIL_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { + return imageUrl; + } + } + + ImageHolder actualWorstcase = new ImageHolder(); + ImageHolder bestFit = new ImageHolder(); + for (String candidateImageUrl : getPhotoGalleryImageURLs()) { final ImageHolder candidate = new ImageHolder(candidateImageUrl, getImageSize(candidateImageUrl)); - + if (candidate.isPerfectFit()) { return candidate.url; } - + if (candidate.fitsRatio() && candidate.isBigEnough()) { if (candidate.isSmallerThan(bestFit)) { bestFit = candidate; @@ -301,10 +325,10 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { if (candidate.isBetterWorstcaseThan(actualWorstcase)) { actualWorstcase = candidate; - } - + } + } - + if (!bestFit.isNull()) { return bestFit.url; } @@ -317,7 +341,7 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { public List getVideoURLs() { return videoURLs; } - + public List getSponsorImageURLs() { return sponsorImageURLs; } @@ -349,7 +373,7 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { public void setTwitterURL(String twitterURL) { this.twitterURL = twitterURL; } - + public void setImageSize(String imageURL, ImageSize imageSize) { if (imageSize == null) { imageSizes.remove(imageURL); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventDTO.java index 4da9e173c6b..c93777de1a4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventDTO.java @@ -1,64 +1,64 @@ -package com.sap.sailing.gwt.ui.shared; - -import java.util.ArrayList; -import java.util.Date; -import java.util.List; - -public class EventDTO extends EventBaseDTO { - private static final long serialVersionUID = -7100030301376959817L; - public List regattas; - - private Date currentServerTime; - - private List leaderboardGroups; // keeps the more specific type accessible in a type-safe way - - public EventDTO() { - this(new ArrayList()); - } - - private EventDTO(List leaderboardGroups) { - super(leaderboardGroups); - this.leaderboardGroups = leaderboardGroups; - initCurrentServerTime(); - } - - public EventDTO(String name) { - this(name, new ArrayList()); - } - - private EventDTO(String name, List leaderboardGroups) { - super(name, leaderboardGroups); - this.leaderboardGroups = leaderboardGroups; - initCurrentServerTime(); - regattas = new ArrayList(); - } - - public boolean isFakeSeries() { - return leaderboardGroups.size() == 1 && leaderboardGroups.get(0).hasOverallLeaderboard(); - } - - public boolean isRunning() { - return getCurrentServerTime().after(startDate) && getCurrentServerTime().before(endDate); - } - - public boolean isFinished() { - return getCurrentServerTime().after(endDate); - } - - private void initCurrentServerTime() { - currentServerTime = new Date(); - } - - public Date getCurrentServerTime() { - return currentServerTime; - } - - public void addLeaderboardGroup(LeaderboardGroupDTO leaderboardGroup) { - leaderboardGroups.add(leaderboardGroup); - } - - public List getLeaderboardGroups() { - return leaderboardGroups; - } - -} +package com.sap.sailing.gwt.ui.shared; + +import java.util.ArrayList; +import java.util.Date; +import java.util.List; + +public class EventDTO extends EventBaseDTO { + private static final long serialVersionUID = -7100030301376959817L; + public List regattas; + + private Date currentServerTime; + + private List leaderboardGroups; // keeps the more specific type accessible in a type-safe way + + public EventDTO() { + this(new ArrayList()); + } + + private EventDTO(List leaderboardGroups) { + super(leaderboardGroups); + this.leaderboardGroups = leaderboardGroups; + initCurrentServerTime(); + } + + public EventDTO(String name) { + this(name, new ArrayList()); + } + + private EventDTO(String name, List leaderboardGroups) { + super(name, leaderboardGroups); + this.leaderboardGroups = leaderboardGroups; + initCurrentServerTime(); + regattas = new ArrayList(); + } + + public boolean isFakeSeries() { + return leaderboardGroups.size() == 1 && leaderboardGroups.get(0).hasOverallLeaderboard(); + } + + public boolean isRunning() { + return getCurrentServerTime().after(startDate) && getCurrentServerTime().before(endDate); + } + + public boolean isFinished() { + return getCurrentServerTime().after(endDate); + } + + private void initCurrentServerTime() { + currentServerTime = new Date(); + } + + public Date getCurrentServerTime() { + return currentServerTime; + } + + public void addLeaderboardGroup(LeaderboardGroupDTO leaderboardGroup) { + leaderboardGroups.add(leaderboardGroup); + } + + public List getLeaderboardGroups() { + return leaderboardGroups; + } + +} From ec5188c264fdd85296f20b62c7247ea1dbcd7040 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 3 Dec 2014 13:12:12 +0100 Subject: [PATCH 067/103] use JavaSE-1.8 execution environment JRE System Library for com.sap.sailing.polars.* projects, solving compile errors in Luna --- java/com.sap.sailing.polars.test/.classpath | 14 +++++++------- java/com.sap.sailing.polars/.classpath | 14 +++++++------- 2 files changed, 14 insertions(+), 14 deletions(-) diff --git a/java/com.sap.sailing.polars.test/.classpath b/java/com.sap.sailing.polars.test/.classpath index 16d067f284a..e334ecd9727 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/.classpath b/java/com.sap.sailing.polars/.classpath index 751c8f2e504..1f733c48034 100644 --- a/java/com.sap.sailing.polars/.classpath +++ b/java/com.sap.sailing.polars/.classpath @@ -1,7 +1,7 @@ - - - - - - - + + + + + + + From d3cd465cb0a93a391ea2b256f90b59fa21423ea2 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 3 Dec 2014 13:15:15 +0100 Subject: [PATCH 068/103] added missing StringMessages to Russion translation (in English for now, to satisfy GWT compiler) --- .../main/java/com/sap/sailing/gwt/ui/client/StringMessages.java | 2 -- .../com/sap/sailing/gwt/ui/client/StringMessages_ru.properties | 2 ++ 2 files changed, 2 insertions(+), 2 deletions(-) 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 be8d11e2f6b..39514882813 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 @@ -967,9 +967,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages { String distanceToLine(); String beatAngleTooltip(); String beatAngle(); - String showBoatClassChartsLabel(); - String showDiagram(); String runAutomaticallyTooltip(); String rerunQueryAfterRefreshTooltip(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index ad4854113f4..0613a361993 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -943,6 +943,8 @@ distanceToLine=Растояние до линии старта beatAngleTooltip=Угол между направлением движения участника и ветром beatAngle=Угол лавирования runAutomaticallyTooltip=Запустить запрос автоматически, например, после изменения статистических данных или группировки. +showBoatClassChartsLabel=You can also view the overall diagram for the available boat classes. +showDiagram=Show Diagram rerunQueryAfterRefreshTooltip=Повторно запускает запрос после обновления таблиц. queryDefinitionProvider=Поставщик определения запроса statisticProvider=Поставщик статистических данных From be55f0f324947bb569fcbeb4948d7d6f022798f3 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 3 Dec 2014 13:34:11 +0100 Subject: [PATCH 069/103] removed version specificity where not required; upgraded mockito version to 1.10.14 where needed --- java/com.sap.sailing.gwt.ui/SailingGWT.launch | 146 +++++++++--------- .../META-INF/MANIFEST.MF | 4 +- .../META-INF/MANIFEST.MF | 2 +- 3 files changed, 76 insertions(+), 76 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT.launch b/java/com.sap.sailing.gwt.ui/SailingGWT.launch index 495d9608fa0..8b88b9b832c 100755 --- a/java/com.sap.sailing.gwt.ui/SailingGWT.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT.launch @@ -1,73 +1,73 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + 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 728fcdc2942..9e12ea444e6 100755 --- a/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.mongodb.test/META-INF/MANIFEST.MF @@ -20,9 +20,9 @@ Require-Bundle: com.sap.sailing.domain, org.junit4;bundle-version="4.8.2", org.hamcrest;bundle-version="1.1.0", org.mockito.mockito-core;bundle-version="1.9.5", - com.sap.sailing.polars;bundle-version="1.0.0", + com.sap.sailing.polars, com.sap.sailing.domain.racelogtrackingadapter.testsupport, - com.sap.sailing.server.gateway;bundle-version="1.0.0", + com.sap.sailing.server.gateway, com.sap.sse.common, com.sap.sse.operationaltransformation, com.sap.sse.replication, diff --git a/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF index d84e41d4b8e..ae3c881fe94 100644 --- a/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.test/META-INF/MANIFEST.MF @@ -18,7 +18,7 @@ Require-Bundle: com.sap.sailing.domain, org.hamcrest;bundle-version="1.1.0", org.junit4;bundle-version="4.8.2", com.sap.sailing.domain.test, - org.mockito.mockito-core;bundle-version="1.9.5", + org.mockito.mockito-core;bundle-version="1.10.14", com.sap.sse.common, org.eclipse.jetty.server;bundle-version="9.2.2" Import-Package: com.sap.sailing.server.gateway.jaxrs, From 5cdc0011ebd822b2268e7f19ffeaad6251518cb2 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 3 Dec 2014 13:45:46 +0100 Subject: [PATCH 070/103] removed another specific version from a manifest where it's not desired --- .../META-INF/MANIFEST.MF | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) 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 09a199f631a..e62721228be 100755 --- a/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.server.replication.test/META-INF/MANIFEST.MF @@ -21,7 +21,7 @@ Require-Bundle: com.sap.sailing.server, org.json.simple;bundle-version="1.1.0", org.junit4;bundle-version="4.8.2", org.hamcrest;bundle-version="1.1.0", - com.sap.sailing.polars;bundle-version="1.0.0", + com.sap.sailing.polars, com.sap.sse.common, org.mockito.mockito-core;bundle-version="1.9.5", com.sap.sailing.domain.shared.android, From 6473a7b517d128f542d4dabd83a404aa90a06f5a Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Wed, 3 Dec 2014 15:41:03 +0100 Subject: [PATCH 071/103] Removed GWT log entries, fixed collections used to iterate/ retrieve images --- .../client/shared/mainmedia/MainMedia.java | 28 ++------- .../sailing/gwt/ui/shared/EventBaseDTO.java | 7 ++- .../controls/carousel/ImageCarousel.java | 59 +++++++++---------- .../controls/carousel/WidgetCarousel.java | 11 +--- 4 files changed, 39 insertions(+), 66 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java index beb2d561855..e1355673e51 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java @@ -6,8 +6,6 @@ import java.util.List; import java.util.Random; import com.google.gwt.core.client.GWT; -import com.google.gwt.core.client.Scheduler; -import com.google.gwt.core.client.Scheduler.ScheduledCommand; import com.google.gwt.dom.client.DivElement; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; @@ -61,41 +59,23 @@ public class MainMedia extends Composite { public void setRecentEvents(List recentEvents) { List photoGalleryUrls = new ArrayList(); for (EventBaseDTO event : recentEvents) { - - photoGalleryUrls.addAll(event.getPhotoGalleryImageURLs()); - + photoGalleryUrls.addAll(event.getSailingLovesPhotographyImages()); if (!event.getVideoURLs().isEmpty() && videoCounter < MAX_VIDEO_COUNT) { addVideoToVideoPanel(event); } } + // shuffle the image url list (Remark: Collections.shuffle() is not implemented in GWT) - int gallerySize = photoGalleryUrls.size(); + final int gallerySize = photoGalleryUrls.size(); Random random = new Random(gallerySize); for (int i = 0; i < gallerySize; i++) { Collections.swap(photoGalleryUrls, i, random.nextInt(gallerySize)); } - for (String url : photoGalleryUrls) { - // SimplePanel imageContainer = new SimplePanel(); - // imageContainer.addStyleName(STYLES.media_swiperslide()); - // String image = "url(" + url + ")"; - // imageContainer.getElement().getStyle().setBackgroundImage(image); + for (String url : photoGalleryUrls) { imageCarousel.addImage(url); } imageCarousel.init(); - // this.swiper = Swiper.createWithLoopOption(STYLES.media_swipecontainer(), STYLES.media_swipewrapper(), - // STYLES.media_swiperslide()); - // See bug 2232: the stage image sizes are scaled incorrectly. - // https://github.com/ubilabs/sap-sailing-analytics/issues/421 and - // http://bugzilla.sapsailing.com/bugzilla/show_bug.cgi?id=2232 have the details. A quick fix may be to send a - // resize event - // after everything has been rendered. - Scheduler.get().scheduleDeferred(new ScheduledCommand() { - @Override - public void execute() { - // swiper.reInit(); - } - }); } private void addVideoToVideoPanel(EventBaseDTO event) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java index 32112437ae4..2952f33cf74 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java @@ -10,6 +10,7 @@ import java.util.List; import java.util.Map; import java.util.UUID; +import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.IsSerializable; import com.sap.sailing.domain.common.ImageSize; import com.sap.sailing.domain.common.dto.NamedDTO; @@ -204,13 +205,13 @@ public class EventBaseDTO extends NamedDTO implements IsSerializable { public LinkedList getSailingLovesPhotographyImages() { final LinkedList acceptedImages = new LinkedList(); - for (String candidateImageUrl : acceptedImages) { + for (String candidateImageUrl : imageURLs) { ImageSize imageSize = getImageSize(candidateImageUrl); if (imageSize.getHeight() > MINIMUM_IMAGE_HEIGHT_FOR_SAILING_PHOTOGRAPHY_IN_PIXELS && !candidateImageUrl.toLowerCase().contains(STAGE_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { - acceptedImages.add(candidateImageUrl); - + } else { + GWT.log("Rejected: " + candidateImageUrl); } } return acceptedImages; diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java index c9fc0be2892..d5f8fe54797 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java @@ -116,39 +116,39 @@ public class ImageCarousel extends Widget { */ native void setupSlider(ImageCarousel sliderReference, ButtonElement prevArrowEl, ButtonElement nextArrowEl) /*-{ - $wnd - .$(document) - .ready( - function() { - $wnd - .$( - '.' - + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)) - .slick( - { - dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::showDots), - infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::infiniteScrolling), - centerMode : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::centerMode), - variableWidth : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::variableWidth), - lazyLoad : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::lazyload), - swipeToSlide : false, - arrows : true, - responsive : true, - slidesToShow : 3, - prevArrow : prevArrowEl, - nextArrow : nextArrowEl - }); - }); + $wnd + .$(document) + .ready( + function() { + $wnd + .$( + '.' + + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)) + .slick( + { + dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::showDots), + infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::infiniteScrolling), + centerMode : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::centerMode), + variableWidth : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::variableWidth), + lazyLoad : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::lazyload), + swipeToSlide : false, + arrows : true, + responsive : true, + slidesToShow : 3, + prevArrow : prevArrowEl, + nextArrow : nextArrowEl + }); + }); }-*/; native void addImage(ImageCarousel sliderReference, final DivElement el) /*-{ - $wnd - .$( - '.' - + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)) - .slickAdd(el); + $wnd + .$( + '.' + + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)) + .slickAdd(el); }-*/; @@ -159,8 +159,6 @@ public class ImageCarousel extends Widget { */ public void addImage(String url) { - GWT.log("Add image: " + url); - DivElement imageHolder = Document.get().createDivElement(); SafeStylesBuilder ssb = new SafeStylesBuilder(); @@ -282,7 +280,6 @@ public class ImageCarousel extends Widget { child.getStyle().setHeight(imagesHeight, Unit.PX); } } - GWT.log("Finished to init slider"); } catch (Exception e) { GWT.log("Catched Exception on slider init", e); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index 59650b968c0..f66428d3f99 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -116,7 +116,6 @@ public class WidgetCarousel extends Composite { public void execute() { try { setupSlider(reference, slickPrev, slickNext); - GWT.log("Finished to init slider"); } catch (Exception e) { GWT.log("Catched Exception on slider init", e); } @@ -130,17 +129,13 @@ public class WidgetCarousel extends Composite { for (Iterator iterator = items.iterator(); iterator.hasNext();) { Widget widget = (Widget) iterator.next(); final Element parent = widget.getElement(); - GWT.log("checking widget...." + parent); if (parent != null && parent.getClassName().contains("slick-active")) { - GWT.log("active widget...."); if (iterator.hasNext()) { Widget nextOne = iterator.next(); if (nextOne instanceof LazyLoadable) { LazyLoadable lazyLoadable = (LazyLoadable) nextOne; - GWT.log("Widgetcarousel lazy load trigger for: " + nextOne.getClass().getName()); lazyLoadable.doInitializeLazyComponents(); } else { - GWT.log("Next component ist not lazy loadable: " + nextOne.getClass().getName()); } return; } @@ -184,9 +179,10 @@ public class WidgetCarousel extends Composite { }-*/; /** - * ƒ Add new image to carousel. + * Add new image to carousel. * - * @param urlƒ + * @param url + * */ public void addWidget(Widget slide) { if (items.isEmpty()) { @@ -301,7 +297,6 @@ public class WidgetCarousel extends Composite { child.getStyle().setHeight(imagesHeight, Unit.PX); } } - GWT.log("Finished to init slider"); } catch (Exception e) { GWT.log("Catched Exception on slider init", e); } From 382f082ce13e316a1cee67edf4762564f90fec72 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Wed, 3 Dec 2014 18:08:40 +0100 Subject: [PATCH 072/103] Width auto for Safari resizing --- .../com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java index d5f8fe54797..ffee1556cb8 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java @@ -164,6 +164,7 @@ public class ImageCarousel extends Widget { SafeStylesBuilder ssb = new SafeStylesBuilder(); ssb.height(imagesHeight, Unit.PX); + ssb.appendTrustedString("width:auto;"); ssb.margin(contentMarginInPixels, Style.Unit.PX); imageHolder.setInnerSafeHtml(myTemplate.imageDiv(UriUtils.fromString(url).asString(), ssb.toSafeStyles())); From de6fe8734c89ac35a610145bb34e6516fef37640 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Thu, 4 Dec 2014 14:10:00 +0100 Subject: [PATCH 073/103] Provding image holder size for lazy loading --- .../client/shared/mainmedia/MainMedia.java | 26 ++++-- .../controls/carousel/ImageCarousel.java | 88 ++++++------------- .../controls/carousel/ImageCarousel.ui.xml | 25 +++--- .../controls/carousel/WidgetCarousel.java | 15 +--- .../controls/carousel/WidgetCarousel.ui.xml | 32 +++++-- 5 files changed, 88 insertions(+), 98 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java index e1355673e51..45114523c9c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java @@ -1,7 +1,7 @@ package com.sap.sailing.gwt.home.client.shared.mainmedia; -import java.util.ArrayList; import java.util.Collections; +import java.util.LinkedList; import java.util.List; import java.util.Random; @@ -12,6 +12,7 @@ import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.ImageSize; import com.sap.sailing.gwt.home.client.app.PlaceNavigator; import com.sap.sailing.gwt.home.client.shared.stage.StageEventType; import com.sap.sailing.gwt.ui.common.client.YoutubeApi; @@ -57,9 +58,23 @@ public class MainMedia extends Composite { } public void setRecentEvents(List recentEvents) { - List photoGalleryUrls = new ArrayList(); + class Holder { + public Holder(String url, int height, int width) { + this.url = url; + this.height = height; + this.width = width; + } + + String url; + int height; + int width; + } + List photoGalleryUrls = new LinkedList<>(); for (EventBaseDTO event : recentEvents) { - photoGalleryUrls.addAll(event.getSailingLovesPhotographyImages()); + for (String url : event.getSailingLovesPhotographyImages()) { + ImageSize size = event.getImageSize(url); + photoGalleryUrls.add(new Holder(url, size.getHeight(), size.getWidth())); + } if (!event.getVideoURLs().isEmpty() && videoCounter < MAX_VIDEO_COUNT) { addVideoToVideoPanel(event); } @@ -72,8 +87,9 @@ public class MainMedia extends Composite { Collections.swap(photoGalleryUrls, i, random.nextInt(gallerySize)); } - for (String url : photoGalleryUrls) { - imageCarousel.addImage(url); + for (Holder holder : photoGalleryUrls) { + + imageCarousel.addImage(holder.url, holder.height, holder.width); } imageCarousel.init(); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java index ffee1556cb8..cab59127c53 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java @@ -2,19 +2,15 @@ package com.sap.sse.gwt.client.controls.carousel; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; -import com.google.gwt.dom.client.ButtonElement; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.Document; -import com.google.gwt.dom.client.Element; -import com.google.gwt.dom.client.Style; +import com.google.gwt.dom.client.ImageElement; import com.google.gwt.dom.client.Style.Unit; import com.google.gwt.safecss.shared.SafeStyles; -import com.google.gwt.safecss.shared.SafeStylesBuilder; import com.google.gwt.safehtml.client.SafeHtmlTemplates; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.UriUtils; import com.google.gwt.uibinder.client.UiBinder; -import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.Widget; @@ -60,16 +56,10 @@ public class ImageCarousel extends Widget { private static SlickSliderUiBinder ourUiBinder = GWT.create(SlickSliderUiBinder.class); private static MyTemplate myTemplate = GWT.create(MyTemplate.class); - @UiField - ButtonElement slickPrev; - - @UiField - ButtonElement slickNext; - /** * slick slider property: dots */ - private boolean showDots = false; + private boolean showDots = true; /** * slick slider property: centerMode */ @@ -81,7 +71,7 @@ public class ImageCarousel extends Widget { /** * slick slider property: infiniteScrolling */ - private boolean infiniteScrolling = false; + private boolean infiniteScrolling = true; /** * Image margin (effectively sets spacing between images) */ @@ -94,7 +84,7 @@ public class ImageCarousel extends Widget { /** * The height of the images */ - private int imagesHeight = 400; + private int imagesHeight = 300; private final String uniqueId; @@ -114,41 +104,25 @@ public class ImageCarousel extends Widget { * * @param uniqueId */ - native void setupSlider(ImageCarousel sliderReference, ButtonElement prevArrowEl, ButtonElement nextArrowEl) /*-{ - - $wnd - .$(document) - .ready( - function() { - $wnd - .$( - '.' - + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)) - .slick( - { - dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::showDots), - infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::infiniteScrolling), - centerMode : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::centerMode), - variableWidth : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::variableWidth), - lazyLoad : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::lazyload), - swipeToSlide : false, - arrows : true, - responsive : true, - slidesToShow : 3, - prevArrow : prevArrowEl, - nextArrow : nextArrowEl - }); - }); - - }-*/; - - native void addImage(ImageCarousel sliderReference, final DivElement el) /*-{ + native void setupSlider(ImageCarousel sliderReference) /*-{ $wnd .$( '.' + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)) - .slickAdd(el); + .slick( + { + dots : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::showDots), + infinite : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::infiniteScrolling), + centerMode : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::centerMode), + variableWidth : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::variableWidth), + lazyLoad : (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::lazyload), + swipeToSlide : false, + arrows : true, + responsive : true, + slidesToShow : 2 + + }); }-*/; @@ -157,17 +131,16 @@ public class ImageCarousel extends Widget { * * @param url */ - public void addImage(String url) { + public void addImage(String url, int height, int width) { DivElement imageHolder = Document.get().createDivElement(); + ImageElement imageElement = Document.get().createImageElement(); + imageElement.setAttribute("data-lazy", UriUtils.fromString(url).asString()); - SafeStylesBuilder ssb = new SafeStylesBuilder(); + imageHolder.getStyle().setHeight(imagesHeight, Unit.PX); + imageHolder.getStyle().setWidth(Math.round(width * (imagesHeight / (double) height)), Unit.PX); + imageHolder.appendChild(imageElement); - ssb.height(imagesHeight, Unit.PX); - ssb.appendTrustedString("width:auto;"); - ssb.margin(contentMarginInPixels, Style.Unit.PX); - - imageHolder.setInnerSafeHtml(myTemplate.imageDiv(UriUtils.fromString(url).asString(), ssb.toSafeStyles())); getElement().appendChild(imageHolder); } @@ -260,7 +233,7 @@ public class ImageCarousel extends Widget { * Template used to create image element */ interface MyTemplate extends SafeHtmlTemplates { - @Template("
") + @Template("") SafeHtml imageDiv(String uri, SafeStyles imageStyles); } @@ -269,18 +242,13 @@ public class ImageCarousel extends Widget { */ public void init() { final ImageCarousel reference = this; + Scheduler.get().scheduleDeferred(new Command() { + @Override public void execute() { try { - setupSlider(reference, slickPrev, slickNext); - for (int i = 0; i < getElement().getChildCount(); i++) { - Element child = (Element) getElement().getChild(i); - String className = child.getClassName(); - if (className != null && className.contains("slick-list")) { - child.getStyle().setHeight(imagesHeight, Unit.PX); - } - } + setupSlider(reference); } catch (Exception e) { GWT.log("Catched Exception on slider init", e); } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml index 423935cf265..3a0640e3e16 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml @@ -1,23 +1,26 @@ + @external slick-slide, slick-prev, slick-next, slick-track; .slider { width: 100%; } + + .slick-track { + + } + .slider > ul > li > button:before { font-size: 15px; } + + .slick-slide { + margin-left: 15px; + margin-right: 15px; + background-color: #fcfcfc; + } - .slider img { - margin: 10px; - } - - - - .slider { - width: 100%; - } - + .slick-prev { left: 20px; z-index:1; @@ -47,7 +50,5 @@
- -
\ No newline at end of file diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java index f66428d3f99..df0bd622e00 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.java @@ -5,7 +5,6 @@ import java.util.LinkedList; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; -import com.google.gwt.dom.client.ButtonElement; import com.google.gwt.dom.client.Document; import com.google.gwt.dom.client.Element; import com.google.gwt.dom.client.Style.Unit; @@ -57,12 +56,6 @@ public class WidgetCarousel extends Composite { private static SlickSliderUiBinder ourUiBinder = GWT.create(SlickSliderUiBinder.class); - @UiField - ButtonElement slickPrev; - - @UiField - ButtonElement slickNext; - @UiField HTMLPanel sliderMainUi; @@ -115,7 +108,7 @@ public class WidgetCarousel extends Composite { @Override public void execute() { try { - setupSlider(reference, slickPrev, slickNext); + setupSlider(reference); } catch (Exception e) { GWT.log("Catched Exception on slider init", e); } @@ -148,7 +141,7 @@ public class WidgetCarousel extends Composite { * * @param uniqueId */ - native void setupSlider(WidgetCarousel sliderReference, ButtonElement prevArrowEl, ButtonElement nextArrowEl) /*-{ + native void setupSlider(WidgetCarousel sliderReference) /*-{ $wnd .$(document) @@ -165,8 +158,6 @@ public class WidgetCarousel extends Composite { swipeToSlide : false, arrows : true, responsive : false, - prevArrow : prevArrowEl, - nextArrow : nextArrowEl, onAfterChange : function() { sliderReference.@com.sap.sse.gwt.client.controls.carousel.WidgetCarousel::onAfterChange()(); }, @@ -289,7 +280,7 @@ public class WidgetCarousel extends Composite { @Override public void execute() { try { - setupSlider(reference, slickPrev, slickNext); + setupSlider(reference); for (int i = 0; i < getElement().getChildCount(); i++) { Element child = (Element) getElement().getChild(i); String className = child.getClassName(); diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml index 575d58d8f77..052d65296ff 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml @@ -2,10 +2,28 @@ xmlns:g="urn:import:com.google.gwt.user.client.ui"> - .slider { - width: 100%; - } + @external slick-slide, slick-prev, slick-next, slick-track; + .slider { + width: 100%; + + } + + .slick-track { + + } + + .slider > ul > li > button:before { + font-size: 15px; + } + + .slick-slide { + margin-left: 15px; + margin-right: 15px; + background-color: #fcfcfc; + } + + .slick-prev { left: 20px; z-index:1; @@ -30,16 +48,12 @@ left: -60px; } + - - - - + \ No newline at end of file From 0307e86740e48ec16142552984b0e671a573b637 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Thu, 4 Dec 2014 21:15:56 +0100 Subject: [PATCH 074/103] Switched from image to div using image as background --- .../client/shared/recentevent/RecentEvent.css | 21 ++++--- .../shared/recentevent/RecentEvent.java | 55 +++++++++++-------- .../shared/recentevent/RecentEvent.ui.xml | 4 +- 3 files changed, 46 insertions(+), 34 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css index c68f10f5773..c84cdf6d7a3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css @@ -21,13 +21,16 @@ } .eventteaser:hover .eventteaser_image { opacity: 0.9; - -ms-filter: "progid:DXImageTransform.Microsoft.Alpha(Opacity=90)"; - filter: literal("alpha(opacity=90)"); } .eventteaser_image { - position: relative; - display: inline-block; - max-width: 100%; + position: relative; + display: inline-block; + max-width: 100%; + width: 100%; + height: 240px; + background-position: center; + background-size: cover; + background-repeat: no-repeat; } .eventteaser_series { position: absolute; @@ -35,10 +38,10 @@ right: 0; } .eventteaser_name { - font-size: 1.466666666666667em; - font-weight: bold; - color: #008fcc; - margin-top: 0.333333333333333em; + font-size: 1.466666666666667em; + font-weight: bold; + color: #008fcc; + margin-top: 0.333333333333333em; } .eventteaser_location { color: #222; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.java index e289b32f4a0..bce7bf0d89a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.java @@ -1,9 +1,7 @@ package com.sap.sailing.gwt.home.client.shared.recentevent; - import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.DivElement; -import com.google.gwt.dom.client.ImageElement; import com.google.gwt.dom.client.SpanElement; import com.google.gwt.dom.client.Style.Display; import com.google.gwt.event.dom.client.ClickEvent; @@ -22,37 +20,44 @@ import com.sap.sailing.gwt.home.client.shared.LongNamesUtil; import com.sap.sailing.gwt.ui.shared.EventBaseDTO; public class RecentEvent extends Composite { - - @UiField SpanElement eventName; - @UiField SpanElement venueName; - @UiField SpanElement eventStartDate; - @UiField Anchor eventOverviewLink; - @UiField ImageElement eventImage; - @UiField DivElement isLiveDiv; - + + @UiField + SpanElement eventName; + @UiField + SpanElement venueName; + @UiField + SpanElement eventStartDate; + @UiField + Anchor eventOverviewLink; + @UiField + DivElement eventImageContainerDiv; + @UiField + DivElement isLiveDiv; + private final EventBaseDTO event; private final HomePlacesNavigator navigator; - private final PlaceNavigation eventNavigation; + private final PlaceNavigation eventNavigation; interface RecentEventUiBinder extends UiBinder { } - + private static RecentEventUiBinder uiBinder = GWT.create(RecentEventUiBinder.class); public RecentEvent(final HomePlacesNavigator navigator, final EventBaseDTO event) { this.navigator = navigator; this.event = event; - + RecentEventResources.INSTANCE.css().ensureInjected(); initWidget(uiBinder.createAndBindUi(this)); - - eventNavigation = navigator.getEventNavigation(event.id.toString(), event.getBaseURL(), event.isOnRemoteServer()); + + eventNavigation = navigator.getEventNavigation(event.id.toString(), event.getBaseURL(), + event.isOnRemoteServer()); eventOverviewLink.setHref(eventNavigation.getTargetUrl()); - + updateUI(); } - + private void updateUI() { SafeHtml safeHtmlEventName = LongNamesUtil.breakLongName(event.getName()); eventName.setInnerSafeHtml(safeHtmlEventName); @@ -61,15 +66,19 @@ public class RecentEvent extends Composite { } venueName.setInnerText(event.venue.getName()); eventStartDate.setInnerText(EventDatesFormatterUtil.formatDateRangeWithoutYear(event.startDate, event.endDate)); - - final String thumbnailUrl = event.getEventThumbnailImageUrl(); - if (thumbnailUrl== null ) { - eventImage.setSrc(RecentEventResources.INSTANCE.defaultEventPhotoImage().getSafeUri().asString()); + + final StringBuilder thumbnailUrlBuilder = new StringBuilder("url('"); + + final String thumbnailImageUrl = event.getEventThumbnailImageUrl(); + if (thumbnailImageUrl == null || thumbnailImageUrl.isEmpty()) { + thumbnailUrlBuilder.append(RecentEventResources.INSTANCE.defaultEventPhotoImage().getSafeUri().asString()); } else { - eventImage.setSrc(thumbnailUrl); + thumbnailUrlBuilder.append(thumbnailImageUrl); } + thumbnailUrlBuilder.append("')"); + eventImageContainerDiv.getStyle().setBackgroundImage(thumbnailUrlBuilder.toString()); } - + @UiHandler("eventOverviewLink") public void goToEventOverview(ClickEvent e) { navigator.goToPlace(eventNavigation); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.ui.xml index 1c61c1e2b46..8a4e25da556 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.ui.xml @@ -8,9 +8,9 @@
-
+
-
+
,  From aaa1547d7f417e255f83b9b83ef079e60a815e54 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Thu, 4 Dec 2014 21:28:14 +0100 Subject: [PATCH 075/103] Inlined css with partial --- .../sailing/gwt/home/client/shared/recentevent/RecentEvent.css | 2 -- 1 file changed, 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css index c84cdf6d7a3..4a0f807bcec 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css @@ -1,12 +1,10 @@ .eventteasercontainer { - font-size: 15px; font-size: 1rem; text-align: center; width: 100%; height: 24em; } .eventteaser { - font-size: 15px; font-size: 1rem; display: inline-block; margin-bottom: 3em; From 90cc613c365a0bf5455f1d9781862e61f976c09f Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Thu, 4 Dec 2014 21:30:46 +0100 Subject: [PATCH 076/103] Removed maring. The widget carousel goes edge to edge --- .../sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml index 052d65296ff..ebeaad6fe66 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml @@ -18,8 +18,7 @@ } .slick-slide { - margin-left: 15px; - margin-right: 15px; + margin: 0px; background-color: #fcfcfc; } From 4d0e3d041e1f94b3e1d14e8bc0324900e3ae6849 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Thu, 4 Dec 2014 22:21:05 +0100 Subject: [PATCH 077/103] Added css selector to encapsulate settings --- .../controls/carousel/ImageCarousel.ui.xml | 19 ++++++++----------- .../controls/carousel/WidgetCarousel.ui.xml | 16 ++++++++-------- 2 files changed, 16 insertions(+), 19 deletions(-) diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml index 3a0640e3e16..e7c88bd4c6e 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.ui.xml @@ -6,47 +6,44 @@ } - .slick-track { + .slider .slick-track { } - .slider > ul > li > button:before { + .slider ul > li > button:before { font-size: 15px; } - .slick-slide { + .slider .slick-slide { margin-left: 15px; margin-right: 15px; background-color: #fcfcfc; } - - .slick-prev { + .slider .slick-prev { left: 20px; z-index:1; height: 60px; width: 60px; } - .slick-prev:before { + .slider .slick-prev:before { color: #000000; font-size: 60px; left: -60px; + } - .slick-next { + .slider .slick-next { right: 20px; z-index:1; height: 60px; width: 60px; } - .slick-next:before { + .slider .slick-next:before { color: #000000; font-size: 60px; left: -60px; } - - -
diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml index ebeaad6fe66..1658df697d5 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/WidgetCarousel.ui.xml @@ -9,45 +9,45 @@ } - .slick-track { + .slider .slick-track { } - .slider > ul > li > button:before { + .slider ul li > button:before { font-size: 15px; } - .slick-slide { + .slider .slick-slide { margin: 0px; background-color: #fcfcfc; } - .slick-prev { + .slider .slick-prev { left: 20px; z-index:1; height: 60px; width: 60px; } - .slick-prev:before { + .slider .slick-prev:before { color: #000000; font-size: 60px; left: -60px; + top: 10px; } - .slick-next { + .slider .slick-next { right: 20px; z-index:1; height: 60px; width: 60px; } - .slick-next:before { + .slider .slick-next:before { color: #000000; font-size: 60px; left: -60px; } - From 57420cd16bad44d3c50ff4461357d65a207fa170 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Thu, 4 Dec 2014 23:14:18 +0100 Subject: [PATCH 078/103] Added missing width for anchor representing event teaser container --- .../sailing/gwt/home/client/shared/recentevent/RecentEvent.css | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css index 4a0f807bcec..519353979c9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/recentevent/RecentEvent.css @@ -10,6 +10,7 @@ margin-bottom: 3em; text-align: left; max-width: 100%; + width: 100%; } .eventteaser:hover { text-decoration: none; From af32f421f2498e1bf4236db328dfe1f096d600f3 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Fri, 5 Dec 2014 10:12:19 +0100 Subject: [PATCH 079/103] Load JS and CSS resources from own server, not CDN --- java/com.sap.sailing.gwt.ui/Home.html | 7 +-- .../js/jquery-1.11.1.min.js | 4 ++ .../js/jquery-1.11.1.min.map | 1 + .../js/jquery.slick/fonts/slick.eot | Bin 0 -> 2048 bytes .../js/jquery.slick/fonts/slick.svg | 14 +++++ .../js/jquery.slick/fonts/slick.ttf | Bin 0 -> 1892 bytes .../js/jquery.slick/fonts/slick.woff | Bin 0 -> 1380 bytes .../js/jquery.slick/slick-1.3.15.css | 57 ++++++++++++++++++ .../js/jquery.slick/slick-1.3.15.css.map | 7 +++ .../js/jquery.slick/slick-1.3.15.min.js | 18 ++++++ .../src/main/resources/fonts/slick.eot | Bin 0 -> 2048 bytes .../src/main/resources/fonts/slick.svg | 14 +++++ .../src/main/resources/fonts/slick.ttf | Bin 0 -> 1892 bytes .../src/main/resources/fonts/slick.woff | Bin 0 -> 1380 bytes 14 files changed, 118 insertions(+), 4 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/js/jquery-1.11.1.min.js create mode 100644 java/com.sap.sailing.gwt.ui/js/jquery-1.11.1.min.map create mode 100644 java/com.sap.sailing.gwt.ui/js/jquery.slick/fonts/slick.eot create mode 100644 java/com.sap.sailing.gwt.ui/js/jquery.slick/fonts/slick.svg create mode 100644 java/com.sap.sailing.gwt.ui/js/jquery.slick/fonts/slick.ttf create mode 100644 java/com.sap.sailing.gwt.ui/js/jquery.slick/fonts/slick.woff create mode 100644 java/com.sap.sailing.gwt.ui/js/jquery.slick/slick-1.3.15.css create mode 100644 java/com.sap.sailing.gwt.ui/js/jquery.slick/slick-1.3.15.css.map create mode 100644 java/com.sap.sailing.gwt.ui/js/jquery.slick/slick-1.3.15.min.js create mode 100644 java/com.sap.sailing.gwt.ui/src/main/resources/fonts/slick.eot create mode 100644 java/com.sap.sailing.gwt.ui/src/main/resources/fonts/slick.svg create mode 100644 java/com.sap.sailing.gwt.ui/src/main/resources/fonts/slick.ttf create mode 100644 java/com.sap.sailing.gwt.ui/src/main/resources/fonts/slick.woff diff --git a/java/com.sap.sailing.gwt.ui/Home.html b/java/com.sap.sailing.gwt.ui/Home.html index da52a9e025f..44fb443a868 100644 --- a/java/com.sap.sailing.gwt.ui/Home.html +++ b/java/com.sap.sailing.gwt.ui/Home.html @@ -28,8 +28,7 @@ - - + @@ -37,8 +36,8 @@ - - + + diff --git a/java/com.sap.sailing.gwt.ui/js/jquery-1.11.1.min.js b/java/com.sap.sailing.gwt.ui/js/jquery-1.11.1.min.js new file mode 100644 index 00000000000..ab28a24729b --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/js/jquery-1.11.1.min.js @@ -0,0 +1,4 @@ +/*! jQuery v1.11.1 | (c) 2005, 2014 jQuery Foundation, Inc. | jquery.org/license */ +!function(a,b){"object"==typeof module&&"object"==typeof module.exports?module.exports=a.document?b(a,!0):function(a){if(!a.document)throw new Error("jQuery requires a window with a document");return b(a)}:b(a)}("undefined"!=typeof window?window:this,function(a,b){var c=[],d=c.slice,e=c.concat,f=c.push,g=c.indexOf,h={},i=h.toString,j=h.hasOwnProperty,k={},l="1.11.1",m=function(a,b){return new m.fn.init(a,b)},n=/^[\s\uFEFF\xA0]+|[\s\uFEFF\xA0]+$/g,o=/^-ms-/,p=/-([\da-z])/gi,q=function(a,b){return b.toUpperCase()};m.fn=m.prototype={jquery:l,constructor:m,selector:"",length:0,toArray:function(){return d.call(this)},get:function(a){return null!=a?0>a?this[a+this.length]:this[a]:d.call(this)},pushStack:function(a){var b=m.merge(this.constructor(),a);return b.prevObject=this,b.context=this.context,b},each:function(a,b){return m.each(this,a,b)},map:function(a){return this.pushStack(m.map(this,function(b,c){return a.call(b,c,b)}))},slice:function(){return this.pushStack(d.apply(this,arguments))},first:function(){return this.eq(0)},last:function(){return this.eq(-1)},eq:function(a){var b=this.length,c=+a+(0>a?b:0);return this.pushStack(c>=0&&b>c?[this[c]]:[])},end:function(){return this.prevObject||this.constructor(null)},push:f,sort:c.sort,splice:c.splice},m.extend=m.fn.extend=function(){var a,b,c,d,e,f,g=arguments[0]||{},h=1,i=arguments.length,j=!1;for("boolean"==typeof g&&(j=g,g=arguments[h]||{},h++),"object"==typeof g||m.isFunction(g)||(g={}),h===i&&(g=this,h--);i>h;h++)if(null!=(e=arguments[h]))for(d in e)a=g[d],c=e[d],g!==c&&(j&&c&&(m.isPlainObject(c)||(b=m.isArray(c)))?(b?(b=!1,f=a&&m.isArray(a)?a:[]):f=a&&m.isPlainObject(a)?a:{},g[d]=m.extend(j,f,c)):void 0!==c&&(g[d]=c));return g},m.extend({expando:"jQuery"+(l+Math.random()).replace(/\D/g,""),isReady:!0,error:function(a){throw new Error(a)},noop:function(){},isFunction:function(a){return"function"===m.type(a)},isArray:Array.isArray||function(a){return"array"===m.type(a)},isWindow:function(a){return null!=a&&a==a.window},isNumeric:function(a){return!m.isArray(a)&&a-parseFloat(a)>=0},isEmptyObject:function(a){var b;for(b in a)return!1;return!0},isPlainObject:function(a){var b;if(!a||"object"!==m.type(a)||a.nodeType||m.isWindow(a))return!1;try{if(a.constructor&&!j.call(a,"constructor")&&!j.call(a.constructor.prototype,"isPrototypeOf"))return!1}catch(c){return!1}if(k.ownLast)for(b in a)return j.call(a,b);for(b in a);return void 0===b||j.call(a,b)},type:function(a){return null==a?a+"":"object"==typeof a||"function"==typeof a?h[i.call(a)]||"object":typeof a},globalEval:function(b){b&&m.trim(b)&&(a.execScript||function(b){a.eval.call(a,b)})(b)},camelCase:function(a){return a.replace(o,"ms-").replace(p,q)},nodeName:function(a,b){return a.nodeName&&a.nodeName.toLowerCase()===b.toLowerCase()},each:function(a,b,c){var d,e=0,f=a.length,g=r(a);if(c){if(g){for(;f>e;e++)if(d=b.apply(a[e],c),d===!1)break}else for(e in a)if(d=b.apply(a[e],c),d===!1)break}else if(g){for(;f>e;e++)if(d=b.call(a[e],e,a[e]),d===!1)break}else for(e in a)if(d=b.call(a[e],e,a[e]),d===!1)break;return a},trim:function(a){return null==a?"":(a+"").replace(n,"")},makeArray:function(a,b){var c=b||[];return null!=a&&(r(Object(a))?m.merge(c,"string"==typeof a?[a]:a):f.call(c,a)),c},inArray:function(a,b,c){var d;if(b){if(g)return g.call(b,a,c);for(d=b.length,c=c?0>c?Math.max(0,d+c):c:0;d>c;c++)if(c in b&&b[c]===a)return c}return-1},merge:function(a,b){var c=+b.length,d=0,e=a.length;while(c>d)a[e++]=b[d++];if(c!==c)while(void 0!==b[d])a[e++]=b[d++];return a.length=e,a},grep:function(a,b,c){for(var d,e=[],f=0,g=a.length,h=!c;g>f;f++)d=!b(a[f],f),d!==h&&e.push(a[f]);return e},map:function(a,b,c){var d,f=0,g=a.length,h=r(a),i=[];if(h)for(;g>f;f++)d=b(a[f],f,c),null!=d&&i.push(d);else for(f in a)d=b(a[f],f,c),null!=d&&i.push(d);return e.apply([],i)},guid:1,proxy:function(a,b){var c,e,f;return"string"==typeof b&&(f=a[b],b=a,a=f),m.isFunction(a)?(c=d.call(arguments,2),e=function(){return a.apply(b||this,c.concat(d.call(arguments)))},e.guid=a.guid=a.guid||m.guid++,e):void 0},now:function(){return+new Date},support:k}),m.each("Boolean Number String Function Array Date RegExp Object Error".split(" "),function(a,b){h["[object "+b+"]"]=b.toLowerCase()});function r(a){var b=a.length,c=m.type(a);return"function"===c||m.isWindow(a)?!1:1===a.nodeType&&b?!0:"array"===c||0===b||"number"==typeof b&&b>0&&b-1 in a}var s=function(a){var b,c,d,e,f,g,h,i,j,k,l,m,n,o,p,q,r,s,t,u="sizzle"+-new Date,v=a.document,w=0,x=0,y=gb(),z=gb(),A=gb(),B=function(a,b){return a===b&&(l=!0),0},C="undefined",D=1<<31,E={}.hasOwnProperty,F=[],G=F.pop,H=F.push,I=F.push,J=F.slice,K=F.indexOf||function(a){for(var b=0,c=this.length;c>b;b++)if(this[b]===a)return b;return-1},L="checked|selected|async|autofocus|autoplay|controls|defer|disabled|hidden|ismap|loop|multiple|open|readonly|required|scoped",M="[\\x20\\t\\r\\n\\f]",N="(?:\\\\.|[\\w-]|[^\\x00-\\xa0])+",O=N.replace("w","w#"),P="\\["+M+"*("+N+")(?:"+M+"*([*^$|!~]?=)"+M+"*(?:'((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\"|("+O+"))|)"+M+"*\\]",Q=":("+N+")(?:\\((('((?:\\\\.|[^\\\\'])*)'|\"((?:\\\\.|[^\\\\\"])*)\")|((?:\\\\.|[^\\\\()[\\]]|"+P+")*)|.*)\\)|)",R=new RegExp("^"+M+"+|((?:^|[^\\\\])(?:\\\\.)*)"+M+"+$","g"),S=new RegExp("^"+M+"*,"+M+"*"),T=new RegExp("^"+M+"*([>+~]|"+M+")"+M+"*"),U=new RegExp("="+M+"*([^\\]'\"]*?)"+M+"*\\]","g"),V=new RegExp(Q),W=new RegExp("^"+O+"$"),X={ID:new RegExp("^#("+N+")"),CLASS:new RegExp("^\\.("+N+")"),TAG:new RegExp("^("+N.replace("w","w*")+")"),ATTR:new RegExp("^"+P),PSEUDO:new RegExp("^"+Q),CHILD:new RegExp("^:(only|first|last|nth|nth-last)-(child|of-type)(?:\\("+M+"*(even|odd|(([+-]|)(\\d*)n|)"+M+"*(?:([+-]|)"+M+"*(\\d+)|))"+M+"*\\)|)","i"),bool:new RegExp("^(?:"+L+")$","i"),needsContext:new RegExp("^"+M+"*[>+~]|:(even|odd|eq|gt|lt|nth|first|last)(?:\\("+M+"*((?:-\\d)?\\d*)"+M+"*\\)|)(?=[^-]|$)","i")},Y=/^(?:input|select|textarea|button)$/i,Z=/^h\d$/i,$=/^[^{]+\{\s*\[native \w/,_=/^(?:#([\w-]+)|(\w+)|\.([\w-]+))$/,ab=/[+~]/,bb=/'|\\/g,cb=new RegExp("\\\\([\\da-f]{1,6}"+M+"?|("+M+")|.)","ig"),db=function(a,b,c){var d="0x"+b-65536;return d!==d||c?b:0>d?String.fromCharCode(d+65536):String.fromCharCode(d>>10|55296,1023&d|56320)};try{I.apply(F=J.call(v.childNodes),v.childNodes),F[v.childNodes.length].nodeType}catch(eb){I={apply:F.length?function(a,b){H.apply(a,J.call(b))}:function(a,b){var c=a.length,d=0;while(a[c++]=b[d++]);a.length=c-1}}}function fb(a,b,d,e){var f,h,j,k,l,o,r,s,w,x;if((b?b.ownerDocument||b:v)!==n&&m(b),b=b||n,d=d||[],!a||"string"!=typeof a)return d;if(1!==(k=b.nodeType)&&9!==k)return[];if(p&&!e){if(f=_.exec(a))if(j=f[1]){if(9===k){if(h=b.getElementById(j),!h||!h.parentNode)return d;if(h.id===j)return d.push(h),d}else if(b.ownerDocument&&(h=b.ownerDocument.getElementById(j))&&t(b,h)&&h.id===j)return d.push(h),d}else{if(f[2])return I.apply(d,b.getElementsByTagName(a)),d;if((j=f[3])&&c.getElementsByClassName&&b.getElementsByClassName)return I.apply(d,b.getElementsByClassName(j)),d}if(c.qsa&&(!q||!q.test(a))){if(s=r=u,w=b,x=9===k&&a,1===k&&"object"!==b.nodeName.toLowerCase()){o=g(a),(r=b.getAttribute("id"))?s=r.replace(bb,"\\$&"):b.setAttribute("id",s),s="[id='"+s+"'] ",l=o.length;while(l--)o[l]=s+qb(o[l]);w=ab.test(a)&&ob(b.parentNode)||b,x=o.join(",")}if(x)try{return I.apply(d,w.querySelectorAll(x)),d}catch(y){}finally{r||b.removeAttribute("id")}}}return i(a.replace(R,"$1"),b,d,e)}function gb(){var a=[];function b(c,e){return a.push(c+" ")>d.cacheLength&&delete b[a.shift()],b[c+" "]=e}return b}function hb(a){return a[u]=!0,a}function ib(a){var b=n.createElement("div");try{return!!a(b)}catch(c){return!1}finally{b.parentNode&&b.parentNode.removeChild(b),b=null}}function jb(a,b){var c=a.split("|"),e=a.length;while(e--)d.attrHandle[c[e]]=b}function kb(a,b){var c=b&&a,d=c&&1===a.nodeType&&1===b.nodeType&&(~b.sourceIndex||D)-(~a.sourceIndex||D);if(d)return d;if(c)while(c=c.nextSibling)if(c===b)return-1;return a?1:-1}function lb(a){return function(b){var c=b.nodeName.toLowerCase();return"input"===c&&b.type===a}}function mb(a){return function(b){var c=b.nodeName.toLowerCase();return("input"===c||"button"===c)&&b.type===a}}function nb(a){return hb(function(b){return b=+b,hb(function(c,d){var e,f=a([],c.length,b),g=f.length;while(g--)c[e=f[g]]&&(c[e]=!(d[e]=c[e]))})})}function ob(a){return a&&typeof a.getElementsByTagName!==C&&a}c=fb.support={},f=fb.isXML=function(a){var b=a&&(a.ownerDocument||a).documentElement;return b?"HTML"!==b.nodeName:!1},m=fb.setDocument=function(a){var b,e=a?a.ownerDocument||a:v,g=e.defaultView;return e!==n&&9===e.nodeType&&e.documentElement?(n=e,o=e.documentElement,p=!f(e),g&&g!==g.top&&(g.addEventListener?g.addEventListener("unload",function(){m()},!1):g.attachEvent&&g.attachEvent("onunload",function(){m()})),c.attributes=ib(function(a){return a.className="i",!a.getAttribute("className")}),c.getElementsByTagName=ib(function(a){return a.appendChild(e.createComment("")),!a.getElementsByTagName("*").length}),c.getElementsByClassName=$.test(e.getElementsByClassName)&&ib(function(a){return a.innerHTML="
",a.firstChild.className="i",2===a.getElementsByClassName("i").length}),c.getById=ib(function(a){return o.appendChild(a).id=u,!e.getElementsByName||!e.getElementsByName(u).length}),c.getById?(d.find.ID=function(a,b){if(typeof b.getElementById!==C&&p){var c=b.getElementById(a);return c&&c.parentNode?[c]:[]}},d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){return a.getAttribute("id")===b}}):(delete d.find.ID,d.filter.ID=function(a){var b=a.replace(cb,db);return function(a){var c=typeof a.getAttributeNode!==C&&a.getAttributeNode("id");return c&&c.value===b}}),d.find.TAG=c.getElementsByTagName?function(a,b){return typeof b.getElementsByTagName!==C?b.getElementsByTagName(a):void 0}:function(a,b){var c,d=[],e=0,f=b.getElementsByTagName(a);if("*"===a){while(c=f[e++])1===c.nodeType&&d.push(c);return d}return f},d.find.CLASS=c.getElementsByClassName&&function(a,b){return typeof b.getElementsByClassName!==C&&p?b.getElementsByClassName(a):void 0},r=[],q=[],(c.qsa=$.test(e.querySelectorAll))&&(ib(function(a){a.innerHTML="",a.querySelectorAll("[msallowclip^='']").length&&q.push("[*^$]="+M+"*(?:''|\"\")"),a.querySelectorAll("[selected]").length||q.push("\\["+M+"*(?:value|"+L+")"),a.querySelectorAll(":checked").length||q.push(":checked")}),ib(function(a){var b=e.createElement("input");b.setAttribute("type","hidden"),a.appendChild(b).setAttribute("name","D"),a.querySelectorAll("[name=d]").length&&q.push("name"+M+"*[*^$|!~]?="),a.querySelectorAll(":enabled").length||q.push(":enabled",":disabled"),a.querySelectorAll("*,:x"),q.push(",.*:")})),(c.matchesSelector=$.test(s=o.matches||o.webkitMatchesSelector||o.mozMatchesSelector||o.oMatchesSelector||o.msMatchesSelector))&&ib(function(a){c.disconnectedMatch=s.call(a,"div"),s.call(a,"[s!='']:x"),r.push("!=",Q)}),q=q.length&&new RegExp(q.join("|")),r=r.length&&new RegExp(r.join("|")),b=$.test(o.compareDocumentPosition),t=b||$.test(o.contains)?function(a,b){var c=9===a.nodeType?a.documentElement:a,d=b&&b.parentNode;return a===d||!(!d||1!==d.nodeType||!(c.contains?c.contains(d):a.compareDocumentPosition&&16&a.compareDocumentPosition(d)))}:function(a,b){if(b)while(b=b.parentNode)if(b===a)return!0;return!1},B=b?function(a,b){if(a===b)return l=!0,0;var d=!a.compareDocumentPosition-!b.compareDocumentPosition;return d?d:(d=(a.ownerDocument||a)===(b.ownerDocument||b)?a.compareDocumentPosition(b):1,1&d||!c.sortDetached&&b.compareDocumentPosition(a)===d?a===e||a.ownerDocument===v&&t(v,a)?-1:b===e||b.ownerDocument===v&&t(v,b)?1:k?K.call(k,a)-K.call(k,b):0:4&d?-1:1)}:function(a,b){if(a===b)return l=!0,0;var c,d=0,f=a.parentNode,g=b.parentNode,h=[a],i=[b];if(!f||!g)return a===e?-1:b===e?1:f?-1:g?1:k?K.call(k,a)-K.call(k,b):0;if(f===g)return kb(a,b);c=a;while(c=c.parentNode)h.unshift(c);c=b;while(c=c.parentNode)i.unshift(c);while(h[d]===i[d])d++;return d?kb(h[d],i[d]):h[d]===v?-1:i[d]===v?1:0},e):n},fb.matches=function(a,b){return fb(a,null,null,b)},fb.matchesSelector=function(a,b){if((a.ownerDocument||a)!==n&&m(a),b=b.replace(U,"='$1']"),!(!c.matchesSelector||!p||r&&r.test(b)||q&&q.test(b)))try{var d=s.call(a,b);if(d||c.disconnectedMatch||a.document&&11!==a.document.nodeType)return d}catch(e){}return fb(b,n,null,[a]).length>0},fb.contains=function(a,b){return(a.ownerDocument||a)!==n&&m(a),t(a,b)},fb.attr=function(a,b){(a.ownerDocument||a)!==n&&m(a);var e=d.attrHandle[b.toLowerCase()],f=e&&E.call(d.attrHandle,b.toLowerCase())?e(a,b,!p):void 0;return void 0!==f?f:c.attributes||!p?a.getAttribute(b):(f=a.getAttributeNode(b))&&f.specified?f.value:null},fb.error=function(a){throw new Error("Syntax error, unrecognized expression: "+a)},fb.uniqueSort=function(a){var b,d=[],e=0,f=0;if(l=!c.detectDuplicates,k=!c.sortStable&&a.slice(0),a.sort(B),l){while(b=a[f++])b===a[f]&&(e=d.push(f));while(e--)a.splice(d[e],1)}return k=null,a},e=fb.getText=function(a){var b,c="",d=0,f=a.nodeType;if(f){if(1===f||9===f||11===f){if("string"==typeof a.textContent)return a.textContent;for(a=a.firstChild;a;a=a.nextSibling)c+=e(a)}else if(3===f||4===f)return a.nodeValue}else while(b=a[d++])c+=e(b);return c},d=fb.selectors={cacheLength:50,createPseudo:hb,match:X,attrHandle:{},find:{},relative:{">":{dir:"parentNode",first:!0}," ":{dir:"parentNode"},"+":{dir:"previousSibling",first:!0},"~":{dir:"previousSibling"}},preFilter:{ATTR:function(a){return a[1]=a[1].replace(cb,db),a[3]=(a[3]||a[4]||a[5]||"").replace(cb,db),"~="===a[2]&&(a[3]=" "+a[3]+" "),a.slice(0,4)},CHILD:function(a){return a[1]=a[1].toLowerCase(),"nth"===a[1].slice(0,3)?(a[3]||fb.error(a[0]),a[4]=+(a[4]?a[5]+(a[6]||1):2*("even"===a[3]||"odd"===a[3])),a[5]=+(a[7]+a[8]||"odd"===a[3])):a[3]&&fb.error(a[0]),a},PSEUDO:function(a){var b,c=!a[6]&&a[2];return X.CHILD.test(a[0])?null:(a[3]?a[2]=a[4]||a[5]||"":c&&V.test(c)&&(b=g(c,!0))&&(b=c.indexOf(")",c.length-b)-c.length)&&(a[0]=a[0].slice(0,b),a[2]=c.slice(0,b)),a.slice(0,3))}},filter:{TAG:function(a){var b=a.replace(cb,db).toLowerCase();return"*"===a?function(){return!0}:function(a){return a.nodeName&&a.nodeName.toLowerCase()===b}},CLASS:function(a){var b=y[a+" "];return b||(b=new RegExp("(^|"+M+")"+a+"("+M+"|$)"))&&y(a,function(a){return b.test("string"==typeof a.className&&a.className||typeof a.getAttribute!==C&&a.getAttribute("class")||"")})},ATTR:function(a,b,c){return function(d){var e=fb.attr(d,a);return null==e?"!="===b:b?(e+="","="===b?e===c:"!="===b?e!==c:"^="===b?c&&0===e.indexOf(c):"*="===b?c&&e.indexOf(c)>-1:"$="===b?c&&e.slice(-c.length)===c:"~="===b?(" "+e+" ").indexOf(c)>-1:"|="===b?e===c||e.slice(0,c.length+1)===c+"-":!1):!0}},CHILD:function(a,b,c,d,e){var f="nth"!==a.slice(0,3),g="last"!==a.slice(-4),h="of-type"===b;return 1===d&&0===e?function(a){return!!a.parentNode}:function(b,c,i){var j,k,l,m,n,o,p=f!==g?"nextSibling":"previousSibling",q=b.parentNode,r=h&&b.nodeName.toLowerCase(),s=!i&&!h;if(q){if(f){while(p){l=b;while(l=l[p])if(h?l.nodeName.toLowerCase()===r:1===l.nodeType)return!1;o=p="only"===a&&!o&&"nextSibling"}return!0}if(o=[g?q.firstChild:q.lastChild],g&&s){k=q[u]||(q[u]={}),j=k[a]||[],n=j[0]===w&&j[1],m=j[0]===w&&j[2],l=n&&q.childNodes[n];while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if(1===l.nodeType&&++m&&l===b){k[a]=[w,n,m];break}}else if(s&&(j=(b[u]||(b[u]={}))[a])&&j[0]===w)m=j[1];else while(l=++n&&l&&l[p]||(m=n=0)||o.pop())if((h?l.nodeName.toLowerCase()===r:1===l.nodeType)&&++m&&(s&&((l[u]||(l[u]={}))[a]=[w,m]),l===b))break;return m-=e,m===d||m%d===0&&m/d>=0}}},PSEUDO:function(a,b){var c,e=d.pseudos[a]||d.setFilters[a.toLowerCase()]||fb.error("unsupported pseudo: "+a);return e[u]?e(b):e.length>1?(c=[a,a,"",b],d.setFilters.hasOwnProperty(a.toLowerCase())?hb(function(a,c){var d,f=e(a,b),g=f.length;while(g--)d=K.call(a,f[g]),a[d]=!(c[d]=f[g])}):function(a){return e(a,0,c)}):e}},pseudos:{not:hb(function(a){var b=[],c=[],d=h(a.replace(R,"$1"));return d[u]?hb(function(a,b,c,e){var f,g=d(a,null,e,[]),h=a.length;while(h--)(f=g[h])&&(a[h]=!(b[h]=f))}):function(a,e,f){return b[0]=a,d(b,null,f,c),!c.pop()}}),has:hb(function(a){return function(b){return fb(a,b).length>0}}),contains:hb(function(a){return function(b){return(b.textContent||b.innerText||e(b)).indexOf(a)>-1}}),lang:hb(function(a){return W.test(a||"")||fb.error("unsupported lang: "+a),a=a.replace(cb,db).toLowerCase(),function(b){var c;do if(c=p?b.lang:b.getAttribute("xml:lang")||b.getAttribute("lang"))return c=c.toLowerCase(),c===a||0===c.indexOf(a+"-");while((b=b.parentNode)&&1===b.nodeType);return!1}}),target:function(b){var c=a.location&&a.location.hash;return c&&c.slice(1)===b.id},root:function(a){return a===o},focus:function(a){return a===n.activeElement&&(!n.hasFocus||n.hasFocus())&&!!(a.type||a.href||~a.tabIndex)},enabled:function(a){return a.disabled===!1},disabled:function(a){return a.disabled===!0},checked:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&!!a.checked||"option"===b&&!!a.selected},selected:function(a){return a.parentNode&&a.parentNode.selectedIndex,a.selected===!0},empty:function(a){for(a=a.firstChild;a;a=a.nextSibling)if(a.nodeType<6)return!1;return!0},parent:function(a){return!d.pseudos.empty(a)},header:function(a){return Z.test(a.nodeName)},input:function(a){return Y.test(a.nodeName)},button:function(a){var b=a.nodeName.toLowerCase();return"input"===b&&"button"===a.type||"button"===b},text:function(a){var b;return"input"===a.nodeName.toLowerCase()&&"text"===a.type&&(null==(b=a.getAttribute("type"))||"text"===b.toLowerCase())},first:nb(function(){return[0]}),last:nb(function(a,b){return[b-1]}),eq:nb(function(a,b,c){return[0>c?c+b:c]}),even:nb(function(a,b){for(var c=0;b>c;c+=2)a.push(c);return a}),odd:nb(function(a,b){for(var c=1;b>c;c+=2)a.push(c);return a}),lt:nb(function(a,b,c){for(var d=0>c?c+b:c;--d>=0;)a.push(d);return a}),gt:nb(function(a,b,c){for(var d=0>c?c+b:c;++db;b++)d+=a[b].value;return d}function rb(a,b,c){var d=b.dir,e=c&&"parentNode"===d,f=x++;return b.first?function(b,c,f){while(b=b[d])if(1===b.nodeType||e)return a(b,c,f)}:function(b,c,g){var h,i,j=[w,f];if(g){while(b=b[d])if((1===b.nodeType||e)&&a(b,c,g))return!0}else while(b=b[d])if(1===b.nodeType||e){if(i=b[u]||(b[u]={}),(h=i[d])&&h[0]===w&&h[1]===f)return j[2]=h[2];if(i[d]=j,j[2]=a(b,c,g))return!0}}}function sb(a){return a.length>1?function(b,c,d){var e=a.length;while(e--)if(!a[e](b,c,d))return!1;return!0}:a[0]}function tb(a,b,c){for(var d=0,e=b.length;e>d;d++)fb(a,b[d],c);return c}function ub(a,b,c,d,e){for(var f,g=[],h=0,i=a.length,j=null!=b;i>h;h++)(f=a[h])&&(!c||c(f,d,e))&&(g.push(f),j&&b.push(h));return g}function vb(a,b,c,d,e,f){return d&&!d[u]&&(d=vb(d)),e&&!e[u]&&(e=vb(e,f)),hb(function(f,g,h,i){var j,k,l,m=[],n=[],o=g.length,p=f||tb(b||"*",h.nodeType?[h]:h,[]),q=!a||!f&&b?p:ub(p,m,a,h,i),r=c?e||(f?a:o||d)?[]:g:q;if(c&&c(q,r,h,i),d){j=ub(r,n),d(j,[],h,i),k=j.length;while(k--)(l=j[k])&&(r[n[k]]=!(q[n[k]]=l))}if(f){if(e||a){if(e){j=[],k=r.length;while(k--)(l=r[k])&&j.push(q[k]=l);e(null,r=[],j,i)}k=r.length;while(k--)(l=r[k])&&(j=e?K.call(f,l):m[k])>-1&&(f[j]=!(g[j]=l))}}else r=ub(r===g?r.splice(o,r.length):r),e?e(null,g,r,i):I.apply(g,r)})}function wb(a){for(var b,c,e,f=a.length,g=d.relative[a[0].type],h=g||d.relative[" "],i=g?1:0,k=rb(function(a){return a===b},h,!0),l=rb(function(a){return K.call(b,a)>-1},h,!0),m=[function(a,c,d){return!g&&(d||c!==j)||((b=c).nodeType?k(a,c,d):l(a,c,d))}];f>i;i++)if(c=d.relative[a[i].type])m=[rb(sb(m),c)];else{if(c=d.filter[a[i].type].apply(null,a[i].matches),c[u]){for(e=++i;f>e;e++)if(d.relative[a[e].type])break;return vb(i>1&&sb(m),i>1&&qb(a.slice(0,i-1).concat({value:" "===a[i-2].type?"*":""})).replace(R,"$1"),c,e>i&&wb(a.slice(i,e)),f>e&&wb(a=a.slice(e)),f>e&&qb(a))}m.push(c)}return sb(m)}function xb(a,b){var c=b.length>0,e=a.length>0,f=function(f,g,h,i,k){var l,m,o,p=0,q="0",r=f&&[],s=[],t=j,u=f||e&&d.find.TAG("*",k),v=w+=null==t?1:Math.random()||.1,x=u.length;for(k&&(j=g!==n&&g);q!==x&&null!=(l=u[q]);q++){if(e&&l){m=0;while(o=a[m++])if(o(l,g,h)){i.push(l);break}k&&(w=v)}c&&((l=!o&&l)&&p--,f&&r.push(l))}if(p+=q,c&&q!==p){m=0;while(o=b[m++])o(r,s,g,h);if(f){if(p>0)while(q--)r[q]||s[q]||(s[q]=G.call(i));s=ub(s)}I.apply(i,s),k&&!f&&s.length>0&&p+b.length>1&&fb.uniqueSort(i)}return k&&(w=v,j=t),r};return c?hb(f):f}return h=fb.compile=function(a,b){var c,d=[],e=[],f=A[a+" "];if(!f){b||(b=g(a)),c=b.length;while(c--)f=wb(b[c]),f[u]?d.push(f):e.push(f);f=A(a,xb(e,d)),f.selector=a}return f},i=fb.select=function(a,b,e,f){var i,j,k,l,m,n="function"==typeof a&&a,o=!f&&g(a=n.selector||a);if(e=e||[],1===o.length){if(j=o[0]=o[0].slice(0),j.length>2&&"ID"===(k=j[0]).type&&c.getById&&9===b.nodeType&&p&&d.relative[j[1].type]){if(b=(d.find.ID(k.matches[0].replace(cb,db),b)||[])[0],!b)return e;n&&(b=b.parentNode),a=a.slice(j.shift().value.length)}i=X.needsContext.test(a)?0:j.length;while(i--){if(k=j[i],d.relative[l=k.type])break;if((m=d.find[l])&&(f=m(k.matches[0].replace(cb,db),ab.test(j[0].type)&&ob(b.parentNode)||b))){if(j.splice(i,1),a=f.length&&qb(j),!a)return I.apply(e,f),e;break}}}return(n||h(a,o))(f,b,!p,e,ab.test(a)&&ob(b.parentNode)||b),e},c.sortStable=u.split("").sort(B).join("")===u,c.detectDuplicates=!!l,m(),c.sortDetached=ib(function(a){return 1&a.compareDocumentPosition(n.createElement("div"))}),ib(function(a){return a.innerHTML="","#"===a.firstChild.getAttribute("href")})||jb("type|href|height|width",function(a,b,c){return c?void 0:a.getAttribute(b,"type"===b.toLowerCase()?1:2)}),c.attributes&&ib(function(a){return a.innerHTML="",a.firstChild.setAttribute("value",""),""===a.firstChild.getAttribute("value")})||jb("value",function(a,b,c){return c||"input"!==a.nodeName.toLowerCase()?void 0:a.defaultValue}),ib(function(a){return null==a.getAttribute("disabled")})||jb(L,function(a,b,c){var d;return c?void 0:a[b]===!0?b.toLowerCase():(d=a.getAttributeNode(b))&&d.specified?d.value:null}),fb}(a);m.find=s,m.expr=s.selectors,m.expr[":"]=m.expr.pseudos,m.unique=s.uniqueSort,m.text=s.getText,m.isXMLDoc=s.isXML,m.contains=s.contains;var t=m.expr.match.needsContext,u=/^<(\w+)\s*\/?>(?:<\/\1>|)$/,v=/^.[^:#\[\.,]*$/;function w(a,b,c){if(m.isFunction(b))return m.grep(a,function(a,d){return!!b.call(a,d,a)!==c});if(b.nodeType)return m.grep(a,function(a){return a===b!==c});if("string"==typeof b){if(v.test(b))return m.filter(b,a,c);b=m.filter(b,a)}return m.grep(a,function(a){return m.inArray(a,b)>=0!==c})}m.filter=function(a,b,c){var d=b[0];return c&&(a=":not("+a+")"),1===b.length&&1===d.nodeType?m.find.matchesSelector(d,a)?[d]:[]:m.find.matches(a,m.grep(b,function(a){return 1===a.nodeType}))},m.fn.extend({find:function(a){var b,c=[],d=this,e=d.length;if("string"!=typeof a)return this.pushStack(m(a).filter(function(){for(b=0;e>b;b++)if(m.contains(d[b],this))return!0}));for(b=0;e>b;b++)m.find(a,d[b],c);return c=this.pushStack(e>1?m.unique(c):c),c.selector=this.selector?this.selector+" "+a:a,c},filter:function(a){return this.pushStack(w(this,a||[],!1))},not:function(a){return this.pushStack(w(this,a||[],!0))},is:function(a){return!!w(this,"string"==typeof a&&t.test(a)?m(a):a||[],!1).length}});var x,y=a.document,z=/^(?:\s*(<[\w\W]+>)[^>]*|#([\w-]*))$/,A=m.fn.init=function(a,b){var c,d;if(!a)return this;if("string"==typeof a){if(c="<"===a.charAt(0)&&">"===a.charAt(a.length-1)&&a.length>=3?[null,a,null]:z.exec(a),!c||!c[1]&&b)return!b||b.jquery?(b||x).find(a):this.constructor(b).find(a);if(c[1]){if(b=b instanceof m?b[0]:b,m.merge(this,m.parseHTML(c[1],b&&b.nodeType?b.ownerDocument||b:y,!0)),u.test(c[1])&&m.isPlainObject(b))for(c in b)m.isFunction(this[c])?this[c](b[c]):this.attr(c,b[c]);return this}if(d=y.getElementById(c[2]),d&&d.parentNode){if(d.id!==c[2])return x.find(a);this.length=1,this[0]=d}return this.context=y,this.selector=a,this}return a.nodeType?(this.context=this[0]=a,this.length=1,this):m.isFunction(a)?"undefined"!=typeof x.ready?x.ready(a):a(m):(void 0!==a.selector&&(this.selector=a.selector,this.context=a.context),m.makeArray(a,this))};A.prototype=m.fn,x=m(y);var B=/^(?:parents|prev(?:Until|All))/,C={children:!0,contents:!0,next:!0,prev:!0};m.extend({dir:function(a,b,c){var d=[],e=a[b];while(e&&9!==e.nodeType&&(void 0===c||1!==e.nodeType||!m(e).is(c)))1===e.nodeType&&d.push(e),e=e[b];return d},sibling:function(a,b){for(var c=[];a;a=a.nextSibling)1===a.nodeType&&a!==b&&c.push(a);return c}}),m.fn.extend({has:function(a){var b,c=m(a,this),d=c.length;return this.filter(function(){for(b=0;d>b;b++)if(m.contains(this,c[b]))return!0})},closest:function(a,b){for(var c,d=0,e=this.length,f=[],g=t.test(a)||"string"!=typeof a?m(a,b||this.context):0;e>d;d++)for(c=this[d];c&&c!==b;c=c.parentNode)if(c.nodeType<11&&(g?g.index(c)>-1:1===c.nodeType&&m.find.matchesSelector(c,a))){f.push(c);break}return this.pushStack(f.length>1?m.unique(f):f)},index:function(a){return a?"string"==typeof a?m.inArray(this[0],m(a)):m.inArray(a.jquery?a[0]:a,this):this[0]&&this[0].parentNode?this.first().prevAll().length:-1},add:function(a,b){return this.pushStack(m.unique(m.merge(this.get(),m(a,b))))},addBack:function(a){return this.add(null==a?this.prevObject:this.prevObject.filter(a))}});function D(a,b){do a=a[b];while(a&&1!==a.nodeType);return a}m.each({parent:function(a){var b=a.parentNode;return b&&11!==b.nodeType?b:null},parents:function(a){return m.dir(a,"parentNode")},parentsUntil:function(a,b,c){return m.dir(a,"parentNode",c)},next:function(a){return D(a,"nextSibling")},prev:function(a){return D(a,"previousSibling")},nextAll:function(a){return m.dir(a,"nextSibling")},prevAll:function(a){return m.dir(a,"previousSibling")},nextUntil:function(a,b,c){return m.dir(a,"nextSibling",c)},prevUntil:function(a,b,c){return m.dir(a,"previousSibling",c)},siblings:function(a){return m.sibling((a.parentNode||{}).firstChild,a)},children:function(a){return m.sibling(a.firstChild)},contents:function(a){return m.nodeName(a,"iframe")?a.contentDocument||a.contentWindow.document:m.merge([],a.childNodes)}},function(a,b){m.fn[a]=function(c,d){var e=m.map(this,b,c);return"Until"!==a.slice(-5)&&(d=c),d&&"string"==typeof d&&(e=m.filter(d,e)),this.length>1&&(C[a]||(e=m.unique(e)),B.test(a)&&(e=e.reverse())),this.pushStack(e)}});var E=/\S+/g,F={};function G(a){var b=F[a]={};return m.each(a.match(E)||[],function(a,c){b[c]=!0}),b}m.Callbacks=function(a){a="string"==typeof a?F[a]||G(a):m.extend({},a);var b,c,d,e,f,g,h=[],i=!a.once&&[],j=function(l){for(c=a.memory&&l,d=!0,f=g||0,g=0,e=h.length,b=!0;h&&e>f;f++)if(h[f].apply(l[0],l[1])===!1&&a.stopOnFalse){c=!1;break}b=!1,h&&(i?i.length&&j(i.shift()):c?h=[]:k.disable())},k={add:function(){if(h){var d=h.length;!function f(b){m.each(b,function(b,c){var d=m.type(c);"function"===d?a.unique&&k.has(c)||h.push(c):c&&c.length&&"string"!==d&&f(c)})}(arguments),b?e=h.length:c&&(g=d,j(c))}return this},remove:function(){return h&&m.each(arguments,function(a,c){var d;while((d=m.inArray(c,h,d))>-1)h.splice(d,1),b&&(e>=d&&e--,f>=d&&f--)}),this},has:function(a){return a?m.inArray(a,h)>-1:!(!h||!h.length)},empty:function(){return h=[],e=0,this},disable:function(){return h=i=c=void 0,this},disabled:function(){return!h},lock:function(){return i=void 0,c||k.disable(),this},locked:function(){return!i},fireWith:function(a,c){return!h||d&&!i||(c=c||[],c=[a,c.slice?c.slice():c],b?i.push(c):j(c)),this},fire:function(){return k.fireWith(this,arguments),this},fired:function(){return!!d}};return k},m.extend({Deferred:function(a){var b=[["resolve","done",m.Callbacks("once memory"),"resolved"],["reject","fail",m.Callbacks("once memory"),"rejected"],["notify","progress",m.Callbacks("memory")]],c="pending",d={state:function(){return c},always:function(){return e.done(arguments).fail(arguments),this},then:function(){var a=arguments;return m.Deferred(function(c){m.each(b,function(b,f){var g=m.isFunction(a[b])&&a[b];e[f[1]](function(){var a=g&&g.apply(this,arguments);a&&m.isFunction(a.promise)?a.promise().done(c.resolve).fail(c.reject).progress(c.notify):c[f[0]+"With"](this===d?c.promise():this,g?[a]:arguments)})}),a=null}).promise()},promise:function(a){return null!=a?m.extend(a,d):d}},e={};return d.pipe=d.then,m.each(b,function(a,f){var g=f[2],h=f[3];d[f[1]]=g.add,h&&g.add(function(){c=h},b[1^a][2].disable,b[2][2].lock),e[f[0]]=function(){return e[f[0]+"With"](this===e?d:this,arguments),this},e[f[0]+"With"]=g.fireWith}),d.promise(e),a&&a.call(e,e),e},when:function(a){var b=0,c=d.call(arguments),e=c.length,f=1!==e||a&&m.isFunction(a.promise)?e:0,g=1===f?a:m.Deferred(),h=function(a,b,c){return function(e){b[a]=this,c[a]=arguments.length>1?d.call(arguments):e,c===i?g.notifyWith(b,c):--f||g.resolveWith(b,c)}},i,j,k;if(e>1)for(i=new Array(e),j=new Array(e),k=new Array(e);e>b;b++)c[b]&&m.isFunction(c[b].promise)?c[b].promise().done(h(b,k,c)).fail(g.reject).progress(h(b,j,i)):--f;return f||g.resolveWith(k,c),g.promise()}});var H;m.fn.ready=function(a){return m.ready.promise().done(a),this},m.extend({isReady:!1,readyWait:1,holdReady:function(a){a?m.readyWait++:m.ready(!0)},ready:function(a){if(a===!0?!--m.readyWait:!m.isReady){if(!y.body)return setTimeout(m.ready);m.isReady=!0,a!==!0&&--m.readyWait>0||(H.resolveWith(y,[m]),m.fn.triggerHandler&&(m(y).triggerHandler("ready"),m(y).off("ready")))}}});function I(){y.addEventListener?(y.removeEventListener("DOMContentLoaded",J,!1),a.removeEventListener("load",J,!1)):(y.detachEvent("onreadystatechange",J),a.detachEvent("onload",J))}function J(){(y.addEventListener||"load"===event.type||"complete"===y.readyState)&&(I(),m.ready())}m.ready.promise=function(b){if(!H)if(H=m.Deferred(),"complete"===y.readyState)setTimeout(m.ready);else if(y.addEventListener)y.addEventListener("DOMContentLoaded",J,!1),a.addEventListener("load",J,!1);else{y.attachEvent("onreadystatechange",J),a.attachEvent("onload",J);var c=!1;try{c=null==a.frameElement&&y.documentElement}catch(d){}c&&c.doScroll&&!function e(){if(!m.isReady){try{c.doScroll("left")}catch(a){return setTimeout(e,50)}I(),m.ready()}}()}return H.promise(b)};var K="undefined",L;for(L in m(k))break;k.ownLast="0"!==L,k.inlineBlockNeedsLayout=!1,m(function(){var a,b,c,d;c=y.getElementsByTagName("body")[0],c&&c.style&&(b=y.createElement("div"),d=y.createElement("div"),d.style.cssText="position:absolute;border:0;width:0;height:0;top:0;left:-9999px",c.appendChild(d).appendChild(b),typeof b.style.zoom!==K&&(b.style.cssText="display:inline;margin:0;border:0;padding:1px;width:1px;zoom:1",k.inlineBlockNeedsLayout=a=3===b.offsetWidth,a&&(c.style.zoom=1)),c.removeChild(d))}),function(){var a=y.createElement("div");if(null==k.deleteExpando){k.deleteExpando=!0;try{delete a.test}catch(b){k.deleteExpando=!1}}a=null}(),m.acceptData=function(a){var b=m.noData[(a.nodeName+" ").toLowerCase()],c=+a.nodeType||1;return 1!==c&&9!==c?!1:!b||b!==!0&&a.getAttribute("classid")===b};var M=/^(?:\{[\w\W]*\}|\[[\w\W]*\])$/,N=/([A-Z])/g;function O(a,b,c){if(void 0===c&&1===a.nodeType){var d="data-"+b.replace(N,"-$1").toLowerCase();if(c=a.getAttribute(d),"string"==typeof c){try{c="true"===c?!0:"false"===c?!1:"null"===c?null:+c+""===c?+c:M.test(c)?m.parseJSON(c):c}catch(e){}m.data(a,b,c)}else c=void 0}return c}function P(a){var b;for(b in a)if(("data"!==b||!m.isEmptyObject(a[b]))&&"toJSON"!==b)return!1;return!0}function Q(a,b,d,e){if(m.acceptData(a)){var f,g,h=m.expando,i=a.nodeType,j=i?m.cache:a,k=i?a[h]:a[h]&&h; +if(k&&j[k]&&(e||j[k].data)||void 0!==d||"string"!=typeof b)return k||(k=i?a[h]=c.pop()||m.guid++:h),j[k]||(j[k]=i?{}:{toJSON:m.noop}),("object"==typeof b||"function"==typeof b)&&(e?j[k]=m.extend(j[k],b):j[k].data=m.extend(j[k].data,b)),g=j[k],e||(g.data||(g.data={}),g=g.data),void 0!==d&&(g[m.camelCase(b)]=d),"string"==typeof b?(f=g[b],null==f&&(f=g[m.camelCase(b)])):f=g,f}}function R(a,b,c){if(m.acceptData(a)){var d,e,f=a.nodeType,g=f?m.cache:a,h=f?a[m.expando]:m.expando;if(g[h]){if(b&&(d=c?g[h]:g[h].data)){m.isArray(b)?b=b.concat(m.map(b,m.camelCase)):b in d?b=[b]:(b=m.camelCase(b),b=b in d?[b]:b.split(" ")),e=b.length;while(e--)delete d[b[e]];if(c?!P(d):!m.isEmptyObject(d))return}(c||(delete g[h].data,P(g[h])))&&(f?m.cleanData([a],!0):k.deleteExpando||g!=g.window?delete g[h]:g[h]=null)}}}m.extend({cache:{},noData:{"applet ":!0,"embed ":!0,"object ":"clsid:D27CDB6E-AE6D-11cf-96B8-444553540000"},hasData:function(a){return a=a.nodeType?m.cache[a[m.expando]]:a[m.expando],!!a&&!P(a)},data:function(a,b,c){return Q(a,b,c)},removeData:function(a,b){return R(a,b)},_data:function(a,b,c){return Q(a,b,c,!0)},_removeData:function(a,b){return R(a,b,!0)}}),m.fn.extend({data:function(a,b){var c,d,e,f=this[0],g=f&&f.attributes;if(void 0===a){if(this.length&&(e=m.data(f),1===f.nodeType&&!m._data(f,"parsedAttrs"))){c=g.length;while(c--)g[c]&&(d=g[c].name,0===d.indexOf("data-")&&(d=m.camelCase(d.slice(5)),O(f,d,e[d])));m._data(f,"parsedAttrs",!0)}return e}return"object"==typeof a?this.each(function(){m.data(this,a)}):arguments.length>1?this.each(function(){m.data(this,a,b)}):f?O(f,a,m.data(f,a)):void 0},removeData:function(a){return this.each(function(){m.removeData(this,a)})}}),m.extend({queue:function(a,b,c){var d;return a?(b=(b||"fx")+"queue",d=m._data(a,b),c&&(!d||m.isArray(c)?d=m._data(a,b,m.makeArray(c)):d.push(c)),d||[]):void 0},dequeue:function(a,b){b=b||"fx";var c=m.queue(a,b),d=c.length,e=c.shift(),f=m._queueHooks(a,b),g=function(){m.dequeue(a,b)};"inprogress"===e&&(e=c.shift(),d--),e&&("fx"===b&&c.unshift("inprogress"),delete f.stop,e.call(a,g,f)),!d&&f&&f.empty.fire()},_queueHooks:function(a,b){var c=b+"queueHooks";return m._data(a,c)||m._data(a,c,{empty:m.Callbacks("once memory").add(function(){m._removeData(a,b+"queue"),m._removeData(a,c)})})}}),m.fn.extend({queue:function(a,b){var c=2;return"string"!=typeof a&&(b=a,a="fx",c--),arguments.lengthh;h++)b(a[h],c,g?d:d.call(a[h],h,b(a[h],c)));return e?a:j?b.call(a):i?b(a[0],c):f},W=/^(?:checkbox|radio)$/i;!function(){var a=y.createElement("input"),b=y.createElement("div"),c=y.createDocumentFragment();if(b.innerHTML="
a",k.leadingWhitespace=3===b.firstChild.nodeType,k.tbody=!b.getElementsByTagName("tbody").length,k.htmlSerialize=!!b.getElementsByTagName("link").length,k.html5Clone="<:nav>"!==y.createElement("nav").cloneNode(!0).outerHTML,a.type="checkbox",a.checked=!0,c.appendChild(a),k.appendChecked=a.checked,b.innerHTML="",k.noCloneChecked=!!b.cloneNode(!0).lastChild.defaultValue,c.appendChild(b),b.innerHTML="",k.checkClone=b.cloneNode(!0).cloneNode(!0).lastChild.checked,k.noCloneEvent=!0,b.attachEvent&&(b.attachEvent("onclick",function(){k.noCloneEvent=!1}),b.cloneNode(!0).click()),null==k.deleteExpando){k.deleteExpando=!0;try{delete b.test}catch(d){k.deleteExpando=!1}}}(),function(){var b,c,d=y.createElement("div");for(b in{submit:!0,change:!0,focusin:!0})c="on"+b,(k[b+"Bubbles"]=c in a)||(d.setAttribute(c,"t"),k[b+"Bubbles"]=d.attributes[c].expando===!1);d=null}();var X=/^(?:input|select|textarea)$/i,Y=/^key/,Z=/^(?:mouse|pointer|contextmenu)|click/,$=/^(?:focusinfocus|focusoutblur)$/,_=/^([^.]*)(?:\.(.+)|)$/;function ab(){return!0}function bb(){return!1}function cb(){try{return y.activeElement}catch(a){}}m.event={global:{},add:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m._data(a);if(r){c.handler&&(i=c,c=i.handler,e=i.selector),c.guid||(c.guid=m.guid++),(g=r.events)||(g=r.events={}),(k=r.handle)||(k=r.handle=function(a){return typeof m===K||a&&m.event.triggered===a.type?void 0:m.event.dispatch.apply(k.elem,arguments)},k.elem=a),b=(b||"").match(E)||[""],h=b.length;while(h--)f=_.exec(b[h])||[],o=q=f[1],p=(f[2]||"").split(".").sort(),o&&(j=m.event.special[o]||{},o=(e?j.delegateType:j.bindType)||o,j=m.event.special[o]||{},l=m.extend({type:o,origType:q,data:d,handler:c,guid:c.guid,selector:e,needsContext:e&&m.expr.match.needsContext.test(e),namespace:p.join(".")},i),(n=g[o])||(n=g[o]=[],n.delegateCount=0,j.setup&&j.setup.call(a,d,p,k)!==!1||(a.addEventListener?a.addEventListener(o,k,!1):a.attachEvent&&a.attachEvent("on"+o,k))),j.add&&(j.add.call(a,l),l.handler.guid||(l.handler.guid=c.guid)),e?n.splice(n.delegateCount++,0,l):n.push(l),m.event.global[o]=!0);a=null}},remove:function(a,b,c,d,e){var f,g,h,i,j,k,l,n,o,p,q,r=m.hasData(a)&&m._data(a);if(r&&(k=r.events)){b=(b||"").match(E)||[""],j=b.length;while(j--)if(h=_.exec(b[j])||[],o=q=h[1],p=(h[2]||"").split(".").sort(),o){l=m.event.special[o]||{},o=(d?l.delegateType:l.bindType)||o,n=k[o]||[],h=h[2]&&new RegExp("(^|\\.)"+p.join("\\.(?:.*\\.|)")+"(\\.|$)"),i=f=n.length;while(f--)g=n[f],!e&&q!==g.origType||c&&c.guid!==g.guid||h&&!h.test(g.namespace)||d&&d!==g.selector&&("**"!==d||!g.selector)||(n.splice(f,1),g.selector&&n.delegateCount--,l.remove&&l.remove.call(a,g));i&&!n.length&&(l.teardown&&l.teardown.call(a,p,r.handle)!==!1||m.removeEvent(a,o,r.handle),delete k[o])}else for(o in k)m.event.remove(a,o+b[j],c,d,!0);m.isEmptyObject(k)&&(delete r.handle,m._removeData(a,"events"))}},trigger:function(b,c,d,e){var f,g,h,i,k,l,n,o=[d||y],p=j.call(b,"type")?b.type:b,q=j.call(b,"namespace")?b.namespace.split("."):[];if(h=l=d=d||y,3!==d.nodeType&&8!==d.nodeType&&!$.test(p+m.event.triggered)&&(p.indexOf(".")>=0&&(q=p.split("."),p=q.shift(),q.sort()),g=p.indexOf(":")<0&&"on"+p,b=b[m.expando]?b:new m.Event(p,"object"==typeof b&&b),b.isTrigger=e?2:3,b.namespace=q.join("."),b.namespace_re=b.namespace?new RegExp("(^|\\.)"+q.join("\\.(?:.*\\.|)")+"(\\.|$)"):null,b.result=void 0,b.target||(b.target=d),c=null==c?[b]:m.makeArray(c,[b]),k=m.event.special[p]||{},e||!k.trigger||k.trigger.apply(d,c)!==!1)){if(!e&&!k.noBubble&&!m.isWindow(d)){for(i=k.delegateType||p,$.test(i+p)||(h=h.parentNode);h;h=h.parentNode)o.push(h),l=h;l===(d.ownerDocument||y)&&o.push(l.defaultView||l.parentWindow||a)}n=0;while((h=o[n++])&&!b.isPropagationStopped())b.type=n>1?i:k.bindType||p,f=(m._data(h,"events")||{})[b.type]&&m._data(h,"handle"),f&&f.apply(h,c),f=g&&h[g],f&&f.apply&&m.acceptData(h)&&(b.result=f.apply(h,c),b.result===!1&&b.preventDefault());if(b.type=p,!e&&!b.isDefaultPrevented()&&(!k._default||k._default.apply(o.pop(),c)===!1)&&m.acceptData(d)&&g&&d[p]&&!m.isWindow(d)){l=d[g],l&&(d[g]=null),m.event.triggered=p;try{d[p]()}catch(r){}m.event.triggered=void 0,l&&(d[g]=l)}return b.result}},dispatch:function(a){a=m.event.fix(a);var b,c,e,f,g,h=[],i=d.call(arguments),j=(m._data(this,"events")||{})[a.type]||[],k=m.event.special[a.type]||{};if(i[0]=a,a.delegateTarget=this,!k.preDispatch||k.preDispatch.call(this,a)!==!1){h=m.event.handlers.call(this,a,j),b=0;while((f=h[b++])&&!a.isPropagationStopped()){a.currentTarget=f.elem,g=0;while((e=f.handlers[g++])&&!a.isImmediatePropagationStopped())(!a.namespace_re||a.namespace_re.test(e.namespace))&&(a.handleObj=e,a.data=e.data,c=((m.event.special[e.origType]||{}).handle||e.handler).apply(f.elem,i),void 0!==c&&(a.result=c)===!1&&(a.preventDefault(),a.stopPropagation()))}return k.postDispatch&&k.postDispatch.call(this,a),a.result}},handlers:function(a,b){var c,d,e,f,g=[],h=b.delegateCount,i=a.target;if(h&&i.nodeType&&(!a.button||"click"!==a.type))for(;i!=this;i=i.parentNode||this)if(1===i.nodeType&&(i.disabled!==!0||"click"!==a.type)){for(e=[],f=0;h>f;f++)d=b[f],c=d.selector+" ",void 0===e[c]&&(e[c]=d.needsContext?m(c,this).index(i)>=0:m.find(c,this,null,[i]).length),e[c]&&e.push(d);e.length&&g.push({elem:i,handlers:e})}return h]","i"),hb=/^\s+/,ib=/<(?!area|br|col|embed|hr|img|input|link|meta|param)(([\w:]+)[^>]*)\/>/gi,jb=/<([\w:]+)/,kb=/\s*$/g,rb={option:[1,""],legend:[1,"
","
"],area:[1,"",""],param:[1,"",""],thead:[1,"","
"],tr:[2,"","
"],col:[2,"","
"],td:[3,"","
"],_default:k.htmlSerialize?[0,"",""]:[1,"X
","
"]},sb=db(y),tb=sb.appendChild(y.createElement("div"));rb.optgroup=rb.option,rb.tbody=rb.tfoot=rb.colgroup=rb.caption=rb.thead,rb.th=rb.td;function ub(a,b){var c,d,e=0,f=typeof a.getElementsByTagName!==K?a.getElementsByTagName(b||"*"):typeof a.querySelectorAll!==K?a.querySelectorAll(b||"*"):void 0;if(!f)for(f=[],c=a.childNodes||a;null!=(d=c[e]);e++)!b||m.nodeName(d,b)?f.push(d):m.merge(f,ub(d,b));return void 0===b||b&&m.nodeName(a,b)?m.merge([a],f):f}function vb(a){W.test(a.type)&&(a.defaultChecked=a.checked)}function wb(a,b){return m.nodeName(a,"table")&&m.nodeName(11!==b.nodeType?b:b.firstChild,"tr")?a.getElementsByTagName("tbody")[0]||a.appendChild(a.ownerDocument.createElement("tbody")):a}function xb(a){return a.type=(null!==m.find.attr(a,"type"))+"/"+a.type,a}function yb(a){var b=pb.exec(a.type);return b?a.type=b[1]:a.removeAttribute("type"),a}function zb(a,b){for(var c,d=0;null!=(c=a[d]);d++)m._data(c,"globalEval",!b||m._data(b[d],"globalEval"))}function Ab(a,b){if(1===b.nodeType&&m.hasData(a)){var c,d,e,f=m._data(a),g=m._data(b,f),h=f.events;if(h){delete g.handle,g.events={};for(c in h)for(d=0,e=h[c].length;e>d;d++)m.event.add(b,c,h[c][d])}g.data&&(g.data=m.extend({},g.data))}}function Bb(a,b){var c,d,e;if(1===b.nodeType){if(c=b.nodeName.toLowerCase(),!k.noCloneEvent&&b[m.expando]){e=m._data(b);for(d in e.events)m.removeEvent(b,d,e.handle);b.removeAttribute(m.expando)}"script"===c&&b.text!==a.text?(xb(b).text=a.text,yb(b)):"object"===c?(b.parentNode&&(b.outerHTML=a.outerHTML),k.html5Clone&&a.innerHTML&&!m.trim(b.innerHTML)&&(b.innerHTML=a.innerHTML)):"input"===c&&W.test(a.type)?(b.defaultChecked=b.checked=a.checked,b.value!==a.value&&(b.value=a.value)):"option"===c?b.defaultSelected=b.selected=a.defaultSelected:("input"===c||"textarea"===c)&&(b.defaultValue=a.defaultValue)}}m.extend({clone:function(a,b,c){var d,e,f,g,h,i=m.contains(a.ownerDocument,a);if(k.html5Clone||m.isXMLDoc(a)||!gb.test("<"+a.nodeName+">")?f=a.cloneNode(!0):(tb.innerHTML=a.outerHTML,tb.removeChild(f=tb.firstChild)),!(k.noCloneEvent&&k.noCloneChecked||1!==a.nodeType&&11!==a.nodeType||m.isXMLDoc(a)))for(d=ub(f),h=ub(a),g=0;null!=(e=h[g]);++g)d[g]&&Bb(e,d[g]);if(b)if(c)for(h=h||ub(a),d=d||ub(f),g=0;null!=(e=h[g]);g++)Ab(e,d[g]);else Ab(a,f);return d=ub(f,"script"),d.length>0&&zb(d,!i&&ub(a,"script")),d=h=e=null,f},buildFragment:function(a,b,c,d){for(var e,f,g,h,i,j,l,n=a.length,o=db(b),p=[],q=0;n>q;q++)if(f=a[q],f||0===f)if("object"===m.type(f))m.merge(p,f.nodeType?[f]:f);else if(lb.test(f)){h=h||o.appendChild(b.createElement("div")),i=(jb.exec(f)||["",""])[1].toLowerCase(),l=rb[i]||rb._default,h.innerHTML=l[1]+f.replace(ib,"<$1>")+l[2],e=l[0];while(e--)h=h.lastChild;if(!k.leadingWhitespace&&hb.test(f)&&p.push(b.createTextNode(hb.exec(f)[0])),!k.tbody){f="table"!==i||kb.test(f)?""!==l[1]||kb.test(f)?0:h:h.firstChild,e=f&&f.childNodes.length;while(e--)m.nodeName(j=f.childNodes[e],"tbody")&&!j.childNodes.length&&f.removeChild(j)}m.merge(p,h.childNodes),h.textContent="";while(h.firstChild)h.removeChild(h.firstChild);h=o.lastChild}else p.push(b.createTextNode(f));h&&o.removeChild(h),k.appendChecked||m.grep(ub(p,"input"),vb),q=0;while(f=p[q++])if((!d||-1===m.inArray(f,d))&&(g=m.contains(f.ownerDocument,f),h=ub(o.appendChild(f),"script"),g&&zb(h),c)){e=0;while(f=h[e++])ob.test(f.type||"")&&c.push(f)}return h=null,o},cleanData:function(a,b){for(var d,e,f,g,h=0,i=m.expando,j=m.cache,l=k.deleteExpando,n=m.event.special;null!=(d=a[h]);h++)if((b||m.acceptData(d))&&(f=d[i],g=f&&j[f])){if(g.events)for(e in g.events)n[e]?m.event.remove(d,e):m.removeEvent(d,e,g.handle);j[f]&&(delete j[f],l?delete d[i]:typeof d.removeAttribute!==K?d.removeAttribute(i):d[i]=null,c.push(f))}}}),m.fn.extend({text:function(a){return V(this,function(a){return void 0===a?m.text(this):this.empty().append((this[0]&&this[0].ownerDocument||y).createTextNode(a))},null,a,arguments.length)},append:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.appendChild(a)}})},prepend:function(){return this.domManip(arguments,function(a){if(1===this.nodeType||11===this.nodeType||9===this.nodeType){var b=wb(this,a);b.insertBefore(a,b.firstChild)}})},before:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this)})},after:function(){return this.domManip(arguments,function(a){this.parentNode&&this.parentNode.insertBefore(a,this.nextSibling)})},remove:function(a,b){for(var c,d=a?m.filter(a,this):this,e=0;null!=(c=d[e]);e++)b||1!==c.nodeType||m.cleanData(ub(c)),c.parentNode&&(b&&m.contains(c.ownerDocument,c)&&zb(ub(c,"script")),c.parentNode.removeChild(c));return this},empty:function(){for(var a,b=0;null!=(a=this[b]);b++){1===a.nodeType&&m.cleanData(ub(a,!1));while(a.firstChild)a.removeChild(a.firstChild);a.options&&m.nodeName(a,"select")&&(a.options.length=0)}return this},clone:function(a,b){return a=null==a?!1:a,b=null==b?a:b,this.map(function(){return m.clone(this,a,b)})},html:function(a){return V(this,function(a){var b=this[0]||{},c=0,d=this.length;if(void 0===a)return 1===b.nodeType?b.innerHTML.replace(fb,""):void 0;if(!("string"!=typeof a||mb.test(a)||!k.htmlSerialize&&gb.test(a)||!k.leadingWhitespace&&hb.test(a)||rb[(jb.exec(a)||["",""])[1].toLowerCase()])){a=a.replace(ib,"<$1>");try{for(;d>c;c++)b=this[c]||{},1===b.nodeType&&(m.cleanData(ub(b,!1)),b.innerHTML=a);b=0}catch(e){}}b&&this.empty().append(a)},null,a,arguments.length)},replaceWith:function(){var a=arguments[0];return this.domManip(arguments,function(b){a=this.parentNode,m.cleanData(ub(this)),a&&a.replaceChild(b,this)}),a&&(a.length||a.nodeType)?this:this.remove()},detach:function(a){return this.remove(a,!0)},domManip:function(a,b){a=e.apply([],a);var c,d,f,g,h,i,j=0,l=this.length,n=this,o=l-1,p=a[0],q=m.isFunction(p);if(q||l>1&&"string"==typeof p&&!k.checkClone&&nb.test(p))return this.each(function(c){var d=n.eq(c);q&&(a[0]=p.call(this,c,d.html())),d.domManip(a,b)});if(l&&(i=m.buildFragment(a,this[0].ownerDocument,!1,this),c=i.firstChild,1===i.childNodes.length&&(i=c),c)){for(g=m.map(ub(i,"script"),xb),f=g.length;l>j;j++)d=i,j!==o&&(d=m.clone(d,!0,!0),f&&m.merge(g,ub(d,"script"))),b.call(this[j],d,j);if(f)for(h=g[g.length-1].ownerDocument,m.map(g,yb),j=0;f>j;j++)d=g[j],ob.test(d.type||"")&&!m._data(d,"globalEval")&&m.contains(h,d)&&(d.src?m._evalUrl&&m._evalUrl(d.src):m.globalEval((d.text||d.textContent||d.innerHTML||"").replace(qb,"")));i=c=null}return this}}),m.each({appendTo:"append",prependTo:"prepend",insertBefore:"before",insertAfter:"after",replaceAll:"replaceWith"},function(a,b){m.fn[a]=function(a){for(var c,d=0,e=[],g=m(a),h=g.length-1;h>=d;d++)c=d===h?this:this.clone(!0),m(g[d])[b](c),f.apply(e,c.get());return this.pushStack(e)}});var Cb,Db={};function Eb(b,c){var d,e=m(c.createElement(b)).appendTo(c.body),f=a.getDefaultComputedStyle&&(d=a.getDefaultComputedStyle(e[0]))?d.display:m.css(e[0],"display");return e.detach(),f}function Fb(a){var b=y,c=Db[a];return c||(c=Eb(a,b),"none"!==c&&c||(Cb=(Cb||m("