Merge remote-tracking branch 'origin/raceboard-simulation'

This commit is contained in:
Christopher Ronnewinkel (D036654) committed 2014-11-13 17:09:08 +01:00
commit 0c759f2b78
144 files changed
+6844 -1621

No files matched your search

@@ -30,4 +30,6 @@ public interface PolarSheetGenerationSettings extends Serializable{
boolean splitByWindgauges();
boolean areDefault();
}
@@ -8,10 +8,8 @@ public interface PolarSheetsData extends Serializable {
Number[][] getAveragedPolarDataByWindSpeed();
int getDataCount();
boolean isComplete();
Integer[] getDataCountPerAngleForWindspeed(int beaufort);
Integer[] getDataCountPerAngleForWindspeed(int windIndex);
WindStepping getStepping();
@@ -10,4 +10,14 @@ public interface WindStepping extends Serializable{
Integer[] getRawStepping();
public abstract int getLevelIndexFloorForValue(double speed);
public abstract int getLevelIndexCeilingForValue(double speed);
double getDistanceToLevelFloor(double speed);
int hashCode();
boolean equals(Object obj);
}
@@ -6,8 +6,8 @@ public class PolarSheetGenerationSettingsImpl implements PolarSheetGenerationSet
public static PolarSheetGenerationSettings createStandardPolarSettings() {
Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 };
WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0);
return new PolarSheetGenerationSettingsImpl(200, 0.1, 10, 20, 0.1, true, true, 2, 0.05, true, windStepping,
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);
}
@@ -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;
}
}
@@ -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<Integer,Integer[]> dataCountPerAngleForWindspeed, WindStepping stepping, Map<Integer, Map<Integer, PolarSheetsHistogramData>> histogramDataMap) {
public PolarSheetsDataImpl(Number[][] averagedPolarDataByWindSpeed, int dataCount,
Map<Integer, Integer[]> dataCountPerAngleForWindspeed, WindStepping stepping,
Map<Integer, Map<Integer, PolarSheetsHistogramData>> 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;
@@ -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,64 @@ 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;
}
@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;
}
}
@@ -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;
}
}
+35 -36
View File
@@ -1,36 +1,35 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Domain
Bundle-SymbolicName: com.sap.sailing.domain
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ClassPath: .
Export-Package: com.sap.sailing.domain.base,
com.sap.sailing.domain.base.impl,
com.sap.sailing.domain.confidence,
com.sap.sailing.domain.leaderboard,
com.sap.sailing.domain.leaderboard.caching,
com.sap.sailing.domain.leaderboard.impl,
com.sap.sailing.domain.leaderboard.meta,
com.sap.sailing.domain.markpassingcalculation,
com.sap.sailing.domain.markpassingcalculation.splining,
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.racelog.tracking,
com.sap.sailing.domain.trackfiles,
com.sap.sailing.domain.trackimport,
com.sap.sailing.domain.tracking,
com.sap.sailing.domain.tracking.impl,
com.sap.sailing.util,
com.sap.sailing.util.impl;x-friends:="com.sap.sailing.server.replication,com.sap.sailing.domain.test"
Bundle-ActivationPolicy: lazy
Require-Bundle: com.googlecode.java-diff-utils;bundle-version="1.3.0",
com.sap.sailing.domain.shared.android;bundle-version="1.0.0",
com.sap.sailing.domain.common,
org.json.simple;bundle-version="1.1.0",
com.sap.sailing.geocoding,
com.sap.sse.datamining.shared;bundle-version="1.0.0",
com.sap.sse.common
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Domain
Bundle-SymbolicName: com.sap.sailing.domain
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ClassPath: .
Export-Package: com.sap.sailing.domain.base,
com.sap.sailing.domain.base.impl,
com.sap.sailing.domain.confidence,
com.sap.sailing.domain.leaderboard,
com.sap.sailing.domain.leaderboard.caching,
com.sap.sailing.domain.leaderboard.impl,
com.sap.sailing.domain.leaderboard.meta,
com.sap.sailing.domain.markpassingcalculation,
com.sap.sailing.domain.markpassingcalculation.splining,
com.sap.sailing.domain.masterdataimport,
com.sap.sailing.domain.racelog,
com.sap.sailing.domain.racelog.impl,
com.sap.sailing.domain.racelog.tracking,
com.sap.sailing.domain.trackfiles,
com.sap.sailing.domain.trackimport,
com.sap.sailing.domain.tracking,
com.sap.sailing.domain.tracking.impl,
com.sap.sailing.util,
com.sap.sailing.util.impl;x-friends:="com.sap.sailing.server.replication,com.sap.sailing.domain.test"
Bundle-ActivationPolicy: lazy
Require-Bundle: com.googlecode.java-diff-utils;bundle-version="1.3.0",
com.sap.sailing.domain.shared.android;bundle-version="1.0.0",
com.sap.sailing.domain.common,
org.json.simple;bundle-version="1.1.0",
com.sap.sailing.geocoding,
com.sap.sse.datamining.shared;bundle-version="1.0.0",
com.sap.sse.common
@@ -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;
}
}
@@ -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);
}
@@ -22,8 +22,8 @@ public interface DynamicTrackedRegatta extends TrackedRegatta {
* may be <code>null</code> which means that no update will be fired to any
* {@link DynamicRaceDefinitionSet}.
*/
DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines, WindStore windStore,
GPSFixStore gpsFixStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind, long millisecondsOverWhichToAverageSpeed,
DynamicRaceDefinitionSet raceDefinitionSetToUpdate);
DynamicTrackedRace createTrackedRace(RaceDefinition raceDefinition, Iterable<Sideline> sidelines,
WindStore windStore, GPSFixStore gpsFixStore, long delayToLiveInMillis, long millisecondsOverWhichToAverageWind,
long millisecondsOverWhichToAverageSpeed, DynamicRaceDefinitionSet raceDefinitionSetToUpdate);
}
@@ -81,7 +81,6 @@ public class PolarSheetGenerationServiceTest {
PolarSheetsData results = triggerData.getData();
Assert.assertTrue(results.isComplete());
Assert.assertNotNull(results);
Assert.assertEquals(4, results.getDataCount());
+3 -1
View File
@@ -42,7 +42,9 @@ Require-Bundle: com.sap.sailing.domain,
com.sap.sse.gwt.adminconsole;bundle-version="1.0.0"
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="3.1.0",
javax.servlet.descriptor;version="3.1.0",
javax.servlet.http;version="3.1.0",
+57 -57
View File
@@ -1,57 +1,57 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.gwt.ui"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.googlecode.java-diff-utils/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.json.simple/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.expeditionconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.udpconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.mongodb/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.operationaltransformation/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.geocoding/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server.replication/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.google.gwt.osgi/lib/gwt-user.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.gwt.ui&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/Search.html com.sap.sailing.gwt.ui.Search -startupUrl /security/ui/Login.html com.sap.sse.security.ui.Login -startupUrl /security/ui/EditProfile.html com.sap.sse.security.ui.EditProfile -startupUrl /security/ui/EmailValidation.html com.sap.sse.security.ui.EmailValidation com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.UserManagement com.sap.sailing.gwt.ui.Search com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.Home com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m"/>
</launchConfiguration>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="com.google.gdt.eclipse.suite.webapp">
<booleanAttribute key="com.google.gdt.eclipse.core.RUN_SERVER" value="false"/>
<stringAttribute key="com.google.gdt.eclipse.suiteMainTypeProcessor.PREVIOUSLY_SET_MAIN_TYPE_NAME" value="com.google.gwt.dev.GWTShell"/>
<booleanAttribute key="com.google.gdt.eclipse.suiteWarArgumentProcessor.IS_WAR_FROM_PROJECT_PROPERTIES" value="true"/>
<stringAttribute key="com.google.gwt.eclipse.core.URL" value="/gwt/Home.html"/>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_PATHS">
<listEntry value="/com.sap.sailing.gwt.ui"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.core.MAPPED_RESOURCE_TYPES">
<listEntry value="4"/>
</listAttribute>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<listAttribute key="org.eclipse.jdt.launching.CLASSPATH">
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry containerPath=&quot;org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.6&quot; javaProject=&quot;com.sap.sailing.gwt.ui&quot; path=&quot;1&quot; type=&quot;4&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.gwt.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.googlecode.java-diff-utils/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.json.simple/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.expeditionconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.declination/resources&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.udpconnector/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.mongodb/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.swisstimingadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.operationaltransformation/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.domain.tractracadapter.persistence/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/org.moxieapps.gwt.highcharts/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.geocoding/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.server.replication/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sailing.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.common/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.datamining.shared/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.google.gwt.osgi/lib/gwt-user.jar&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry id=&quot;org.eclipse.jdt.launching.classpathentry.defaultClasspath&quot;&gt;&#13;&#10;&lt;memento exportedEntriesOnly=&quot;false&quot; project=&quot;com.sap.sailing.gwt.ui&quot;/&gt;&#13;&#10;&lt;/runtimeClasspathEntry&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.security.ui/src/main/java&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
<listEntry value="&lt;?xml version=&quot;1.0&quot; encoding=&quot;UTF-8&quot; standalone=&quot;no&quot;?&gt;&#13;&#10;&lt;runtimeClasspathEntry internalArchive=&quot;/com.sap.sse.gwt.adminconsole/src&quot; path=&quot;3&quot; type=&quot;2&quot;/&gt;&#13;&#10;"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.CLASSPATH_PROVIDER" value="com.google.gwt.eclipse.core.moduleClasspathProvider"/>
<booleanAttribute key="org.eclipse.jdt.launching.DEFAULT_CLASSPATH" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.MAIN_TYPE" value="com.google.gwt.dev.DevMode"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9997 -startupUrl /gwt/Home.html com.sap.sailing.gwt.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/UserManagement.html com.sap.sailing.gwt.ui.UserManagement -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/PolarSheets.html com.sap.sailing.gwt.ui.PolarSheets -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.ui.RegattaOverview -startupUrl /gwt/DataMining.html com.sap.sailing.gwt.ui.DataMining -startupUrl /gwt/Simulator.html com.sap.sailing.gwt.ui.Simulator -startupUrl /gwt/VideoPopup.html com.sap.sailing.gwt.ui.VideoPopup -startupUrl /gwt/Search.html com.sap.sailing.gwt.ui.Search -startupUrl /security/ui/Login.html com.sap.sse.security.ui.Login -startupUrl /security/ui/EditProfile.html com.sap.sse.security.ui.EditProfile -startupUrl /security/ui/EmailValidation.html com.sap.sse.security.ui.EmailValidation com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.ui.VideoPopup com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.UserManagement com.sap.sailing.gwt.ui.Search com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.Home com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m"/>
</launchConfiguration>
@@ -0,0 +1,28 @@
package com.sap.sailing.gwt.ui.actions;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMap;
import com.sap.sse.gwt.client.async.AsyncAction;
/**
* An asynchronous action to determine whether a suitable polar diagram is available for the race shown on {@link RaceMap}
*
* @author Christopher Ronnewinkel (D036654)
*
*/
public class GetPolarAction implements AsyncAction<Boolean> {
private final SailingServiceAsync sailingService;
private final RegattaAndRaceIdentifier raceIdentifier;
public GetPolarAction(SailingServiceAsync sailingService, RegattaAndRaceIdentifier raceIdentifier) {
this.sailingService = sailingService;
this.raceIdentifier = raceIdentifier;
}
@Override
public void execute(AsyncCallback<Boolean> callback) {
sailingService.getPolarResults(raceIdentifier, callback);
}
}
@@ -136,6 +136,8 @@ public interface SailingService extends RemoteService {
WindInfoForRaceDTO getAveragedWindInfo(RegattaAndRaceIdentifier raceIdentifier, Date from, long millisecondsStepWidth,
int numberOfFixes, double latDeg, double lngDeg, Collection<String> windSources) throws NoWindException;
Boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier);
SimulatorResultsDTO getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime);
CompactRaceMapDataDTO getRaceMapData(RegattaAndRaceIdentifier raceIdentifier, Date date, Map<String, Date> fromPerCompetitorIdAsString,
@@ -355,10 +357,16 @@ public interface SailingService extends RemoteService {
void createCourseArea(UUID eventId, String courseAreaName);
List<String> getBoatClassNamesWithPolarSheetsAvailable();
void removeCourseArea(UUID eventId, UUID courseAreaId);
List<Util.Pair<String, String>> getLeaderboardsNamesOfMetaLeaderboard(String metaLeaderboardName);
PolarSheetGenerationResponse showCachedPolarSheetForBoatClass(String boatClassName);
Util.Pair<String, LeaderboardType> checkLeaderboardName(String leaderboardName);
/** for backward compatibility with the regatta overview */
@@ -395,16 +403,16 @@ public interface SailingService extends RemoteService {
Iterable<CompetitorDTO> getCompetitors();
/**
*
* @param leaderboardName
* @param lookInRaceLogs If set to {@code true}, the {@link RaceLog}s are checked for the competitor registrations.
* If set to {@code false}, the {@link RaceDefinition}s are checked instead.
* @return
*/
Iterable<CompetitorDTO> getCompetitorsOfLeaderboard(String leaderboardName, boolean lookInRaceLogs);
/**
*
* @param leaderboardName
* @param lookInRaceLogs If set to {@code true}, the {@link RaceLog}s are checked for the competitor registrations.
* If set to {@code false}, the {@link RaceDefinition}s are checked instead.
* @return
*/
Iterable<CompetitorDTO> getCompetitorsOfLeaderboard(String leaderboardName, boolean lookInRaceLogs);
CompetitorDTO addOrUpdateCompetitor(CompetitorDTO competitor);
CompetitorDTO addOrUpdateCompetitor(CompetitorDTO competitor);
void allowCompetitorResetToDefaults(Iterable<CompetitorDTO> competitors);
@@ -429,68 +437,68 @@ public interface SailingService extends RemoteService {
void removeIgtimiAccount(String eMailOfAccountToRemove);
Map<RegattaAndRaceIdentifier, Integer> importWindFromIgtimi(List<RaceDTO> selectedRaces, boolean correctByDeclination) throws Exception;
void denoteForRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName) throws Exception;
/**
* Revoke the {@link DenoteForTrackingEvent}. This does not affect an existing {@code RaceLogRaceTracker}
* or {@link TrackedRace} for this {@code RaceLog}.
*
* @see RaceLogTrackingAdapter#removeDenotationForRaceLogTracking
*/
void removeDenotationForRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName);
void denoteForRaceLogTracking(String leaderboardName) throws Exception;
/**
* Performs all the necessary steps to start tracking the race.
* The {@code RaceLog} needs to be denoted for racelog-tracking beforehand.
*
* @see RaceLogTrackingAdapter#startTracking
*/
void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName)
throws NotDenotedForRaceLogTrackingException, Exception;
void setCompetitorRegistrations(String leaderboardName, String raceColumnName, String fleetName, Set<CompetitorDTO> competitors);
Collection<CompetitorDTO> getCompetitorRegistrations(String leaderboardName, String raceColumnName, String fleetName);
void addMarkToRaceLog(String leaderboardName, String raceColumnName, String fleetName, MarkDTO markDTO);
Collection<MarkDTO> getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName);
/**
* Adds the course definition to the racelog, while trying to reuse existing marks, controlpoints and waypoints
* from the previous course definition in the racelog.
*/
void addCourseDefinitionToRaceLog(String leaderboardName, String raceColumnName, String fleetName, List<Util.Pair<ControlPointDTO, PassingInstruction>> course);
RaceCourseDTO getLastCourseDefinitionInRaceLog(String leaderboardName, String raceColumnName, String fleetName);
/**
* Adds a fix to the {@link GPSFixStore}, and creates a mapping with a virtual device for exactly the current timepoint.
*/
void pingMarkViaRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName, MarkDTO mark, PositionDTO position);
void copyCourseAndCompetitorsToOtherRaceLogs(Util.Triple<String, String, String> raceLogFrom,
Set<Util.Triple<String, String, String>> raceLogsTo);
void addDeviceMappingToRaceLog(String leaderboardName, String raceColumnName, String fleetName, DeviceMappingDTO mapping)
throws TransformationException;
List<DeviceMappingDTO> getDeviceMappingsFromRaceLog(String leaderboardName, String raceColumnName, String fleetName)
throws TransformationException;
List<String> getDeserializableDeviceIdentifierTypes();
void closeOpenEndedDeviceMapping(String leaderboardName, String raceColumnName, String fleetName, DeviceMappingDTO mapping,
Date closingTimePoint) throws NoCorrespondingServiceRegisteredException, TransformationException;
/**
* Revoke the events in the {@code RaceLog} that are identified by the {@code eventIds}.
* This only affects such events that implement {@link Revokable}.
*/
void revokeRaceLogEvents(String leaderboardName, String raceColumnName, String fleetName, List<Serializable> eventIds)
void denoteForRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName) throws Exception;
/**
* Revoke the {@link DenoteForTrackingEvent}. This does not affect an existing {@code RaceLogRaceTracker}
* or {@link TrackedRace} for this {@code RaceLog}.
*
* @see RaceLogTrackingAdapter#removeDenotationForRaceLogTracking
*/
void removeDenotationForRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName);
void denoteForRaceLogTracking(String leaderboardName) throws Exception;
/**
* Performs all the necessary steps to start tracking the race.
* The {@code RaceLog} needs to be denoted for racelog-tracking beforehand.
*
* @see RaceLogTrackingAdapter#startTracking
*/
void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName)
throws NotDenotedForRaceLogTrackingException, Exception;
void setCompetitorRegistrations(String leaderboardName, String raceColumnName, String fleetName, Set<CompetitorDTO> competitors);
Collection<CompetitorDTO> getCompetitorRegistrations(String leaderboardName, String raceColumnName, String fleetName);
void addMarkToRaceLog(String leaderboardName, String raceColumnName, String fleetName, MarkDTO markDTO);
Collection<MarkDTO> getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName);
/**
* Adds the course definition to the racelog, while trying to reuse existing marks, controlpoints and waypoints
* from the previous course definition in the racelog.
*/
void addCourseDefinitionToRaceLog(String leaderboardName, String raceColumnName, String fleetName, List<Util.Pair<ControlPointDTO, PassingInstruction>> course);
RaceCourseDTO getLastCourseDefinitionInRaceLog(String leaderboardName, String raceColumnName, String fleetName);
/**
* Adds a fix to the {@link GPSFixStore}, and creates a mapping with a virtual device for exactly the current timepoint.
*/
void pingMarkViaRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName, MarkDTO mark, PositionDTO position);
void copyCourseAndCompetitorsToOtherRaceLogs(Util.Triple<String, String, String> raceLogFrom,
Set<Util.Triple<String, String, String>> raceLogsTo);
void addDeviceMappingToRaceLog(String leaderboardName, String raceColumnName, String fleetName, DeviceMappingDTO mapping)
throws TransformationException;
List<DeviceMappingDTO> getDeviceMappingsFromRaceLog(String leaderboardName, String raceColumnName, String fleetName)
throws TransformationException;
List<String> getDeserializableDeviceIdentifierTypes();
void closeOpenEndedDeviceMapping(String leaderboardName, String raceColumnName, String fleetName, DeviceMappingDTO mapping,
Date closingTimePoint) throws NoCorrespondingServiceRegisteredException, TransformationException;
/**
* Revoke the events in the {@code RaceLog} that are identified by the {@code eventIds}.
* This only affects such events that implement {@link Revokable}.
*/
void revokeRaceLogEvents(String leaderboardName, String raceColumnName, String fleetName, List<Serializable> eventIds)
throws NotRevokableException;
Collection<String> getGPSFixImporterTypes();
@@ -183,6 +183,8 @@ public interface SailingServiceAsync extends BuildVersionRetriever {
int numberOfFixes, Collection<String> windSourceTypeNames, boolean onlyUpToNewestEvent,
boolean includeCombinedWindForAllLegMiddles, AsyncCallback<WindInfoForRaceDTO> callback);
void getPolarResults(RegattaAndRaceIdentifier raceIdentifier, AsyncCallback<Boolean> callback);
void getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime, AsyncCallback<SimulatorResultsDTO> callback);
void setWind(RegattaAndRaceIdentifier raceIdentifier, WindDTO wind, AsyncCallback<Void> callback);
@@ -515,10 +517,10 @@ public interface SailingServiceAsync extends BuildVersionRetriever {
void getCompetitors(AsyncCallback<Iterable<CompetitorDTO>> asyncCallback);
void getCompetitorsOfLeaderboard(String leaderboardName, boolean lookInRaceLogs,
AsyncCallback<Iterable<CompetitorDTO>> asyncCallback);
void getCompetitorsOfLeaderboard(String leaderboardName, boolean lookInRaceLogs,
AsyncCallback<Iterable<CompetitorDTO>> asyncCallback);
void addOrUpdateCompetitor(CompetitorDTO competitor, AsyncCallback<CompetitorDTO> asyncCallback);
void addOrUpdateCompetitor(CompetitorDTO competitor, AsyncCallback<CompetitorDTO> asyncCallback);
void allowCompetitorResetToDefaults(Iterable<CompetitorDTO> competitors, AsyncCallback<Void> asyncCallback);
@@ -553,60 +555,65 @@ public interface SailingServiceAsync extends BuildVersionRetriever {
void importWindFromIgtimi(List<RaceDTO> selectedRaces, boolean correctByDeclination, AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>> asyncCallback);
void getBoatClassNamesWithPolarSheetsAvailable(AsyncCallback<List<String>> asyncCallback);
void getEventById(UUID id, boolean withStatisticalData, AsyncCallback<EventDTO> callback);
void showCachedPolarSheetForBoatClass(String boatClassName,
AsyncCallback<PolarSheetGenerationResponse> asyncCallback);
void getLeaderboardsByEvent(EventDTO event, AsyncCallback<List<StrippedLeaderboardDTO>> callback);
void denoteForRaceLogTracking(String leaderboardName,
String raceColumnName, String fleetName,
AsyncCallback<Void> callback);
void denoteForRaceLogTracking(String leaderboardName, AsyncCallback<Void> callback);
void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Void> callback);
void setCompetitorRegistrations(String leaderboardName, String raceColumnName, String fleetName,
Set<CompetitorDTO> competitors, AsyncCallback<Void> callback);
void getCompetitorRegistrations(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Collection<CompetitorDTO>> callback);
void addMarkToRaceLog(String leaderboardName, String raceColumnName, String fleetName, MarkDTO markDTO,
AsyncCallback<Void> callback);
void getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Collection<MarkDTO>> callback);
void addCourseDefinitionToRaceLog(String leaderboardName, String raceColumnName, String fleetName,
List<Util.Pair<ControlPointDTO, PassingInstruction>> course, AsyncCallback<Void> callback);
void getLastCourseDefinitionInRaceLog(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<RaceCourseDTO> callback);
void pingMarkViaRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName, MarkDTO mark,
PositionDTO position, AsyncCallback<Void> callback);
void getDeserializableDeviceIdentifierTypes(AsyncCallback<List<String>> callback);
void getDeviceMappingsFromRaceLog(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<List<DeviceMappingDTO>> callback);
void addDeviceMappingToRaceLog(String leaderboardName, String raceColumnName, String fleetName,
DeviceMappingDTO mapping, AsyncCallback<Void> callback);
void closeOpenEndedDeviceMapping(String leaderboardName, String raceColumnName, String fleetName,
DeviceMappingDTO mapping, Date closingTimePoint, AsyncCallback<Void> callback);
void revokeRaceLogEvents(String leaderboardName, String raceColumnName, String fleetName, List<Serializable> eventIds,
AsyncCallback<Void> callback);
void denoteForRaceLogTracking(String leaderboardName,
String raceColumnName, String fleetName,
AsyncCallback<Void> callback);
void denoteForRaceLogTracking(String leaderboardName, AsyncCallback<Void> callback);
void startRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Void> callback);
void setCompetitorRegistrations(String leaderboardName, String raceColumnName, String fleetName,
Set<CompetitorDTO> competitors, AsyncCallback<Void> callback);
void getCompetitorRegistrations(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Collection<CompetitorDTO>> callback);
void addMarkToRaceLog(String leaderboardName, String raceColumnName, String fleetName, MarkDTO markDTO,
AsyncCallback<Void> callback);
void getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Collection<MarkDTO>> callback);
void addCourseDefinitionToRaceLog(String leaderboardName, String raceColumnName, String fleetName,
List<Util.Pair<ControlPointDTO, PassingInstruction>> course, AsyncCallback<Void> callback);
void getLastCourseDefinitionInRaceLog(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<RaceCourseDTO> callback);
void pingMarkViaRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName, MarkDTO mark,
PositionDTO position, AsyncCallback<Void> callback);
void getDeserializableDeviceIdentifierTypes(AsyncCallback<List<String>> callback);
void getDeviceMappingsFromRaceLog(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<List<DeviceMappingDTO>> callback);
void addDeviceMappingToRaceLog(String leaderboardName, String raceColumnName, String fleetName,
DeviceMappingDTO mapping, AsyncCallback<Void> callback);
void closeOpenEndedDeviceMapping(String leaderboardName, String raceColumnName, String fleetName,
DeviceMappingDTO mapping, Date closingTimePoint, AsyncCallback<Void> callback);
void revokeRaceLogEvents(String leaderboardName, String raceColumnName, String fleetName, List<Serializable> eventIds,
AsyncCallback<Void> callback);
void removeSeries(RegattaIdentifier regattaIdentifier, String seriesName, AsyncCallback<Void> callback);
void removeDenotationForRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Void> callback);
void copyCourseAndCompetitorsToOtherRaceLogs(Util.Triple<String, String, String> raceLogFrom,
void removeDenotationForRaceLogTracking(String leaderboardName, String raceColumnName, String fleetName,
AsyncCallback<Void> callback);
void copyCourseAndCompetitorsToOtherRaceLogs(Util.Triple<String, String, String> raceLogFrom,
Set<Util.Triple<String, String, String>> raceLogsTo, AsyncCallback<Void> callback);
void getGPSFixImporterTypes(AsyncCallback<Collection<String>> callback);
@@ -642,4 +649,4 @@ public interface SailingServiceAsync extends BuildVersionRetriever {
AsyncCallback<Iterable<LeaderboardSearchResultDTO>> callback);
void setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived, AsyncCallback<RaceDTO> callback);
}
}
@@ -967,6 +967,10 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages {
String distanceToLine();
String beatAngleTooltip();
String beatAngle();
String showBoatClassChartsLabel();
String showDiagram();
String runAutomaticallyTooltip();
String rerunQueryAfterRefreshTooltip();
String queryDefinitionProvider();
@@ -951,6 +951,8 @@ timeSinceStart={0} since 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
runAutomaticallyTooltip=Run the query automatically, after you changed e.g. the statistic or the grouping.
rerunQueryAfterRefreshTooltip=Reruns the query after the tables have been refreshed.
queryDefinitionProvider=Query Definition Provider
@@ -1092,4 +1094,4 @@ serverError=An error occurred while attempting to contact the server. Please che
remoteProcedureCall=Remote Procedure Call
serverReplies=Server replies
errorCommunicatingWithServer=Error communicating with server
userManagement=User Management
userManagement=User Management
@@ -947,6 +947,8 @@ timeSinceStart={0} seit dem 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
runAutomaticallyTooltip=Führe die Anfrage automatisch durch, wenn zum Beispiel die Statistik oder die Gruppierung geändert wurde.
rerunQueryAfterRefreshTooltip=Wiederholt die Anfrage nachdem die Tabellen aktualisiert wurden.
queryDefinitionProvider=Anfragen-Definitions-Versorger
@@ -81,6 +81,7 @@ import com.sap.sailing.domain.common.impl.BoundsImpl;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.RGBColor;
import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing;
import com.sap.sailing.gwt.ui.actions.GetPolarAction;
import com.sap.sailing.gwt.ui.actions.GetRaceMapDataAction;
import com.sap.sailing.gwt.ui.actions.GetWindInfoAction;
import com.sap.sailing.gwt.ui.client.ClientResources;
@@ -123,6 +124,7 @@ import com.sap.sse.common.filter.Filter;
import com.sap.sse.common.filter.FilterSet;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
import com.sap.sse.gwt.client.player.TimeListener;
import com.sap.sse.gwt.client.player.Timer;
import com.sap.sse.gwt.client.player.Timer.PlayModes;
@@ -309,6 +311,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
private WindStreamletsRaceboardOverlay streamletOverlay;
private final boolean showViewStreamlets;
private final boolean showViewSimulation;
public static final String GET_POLAR_CATEGORY = "getPolar";
private boolean hasPolar;
private final RegattaAndRaceIdentifier raceIdentifier;
public RaceMap(SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor,
@@ -338,6 +342,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
isMapInitialized = false;
this.showViewStreamlets = showViewStreamlets;
this.showViewSimulation = showViewSimulation;
this.hasPolar = false;
headerPanel = new FlowPanel();
headerPanel.setStyleName("RaceMap-HeaderPanel");
panelForLeftHeaderLabels = new AbsolutePanel();
@@ -470,9 +475,12 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
}
if (showViewSimulation) {
// determine availability of polar diagram
hasPolar();
// initialize simulation canvas
simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, timer, raceIdentifier, sailingService, stringMessages, asyncActionsExecutor);
simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, sailingService, stringMessages, asyncActionsExecutor);
simulationOverlay.addToMap();
simulationOverlay.setVisible(false);
}
createHeaderPanel(map);
@@ -486,6 +494,26 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
LoadApi.go(onLoad, loadLibraries, sensor, "key="+GoogleMapAPIKey.V3_APIKey);
}
public void hasPolar() {
GetPolarAction getPolar = new GetPolarAction(sailingService, raceIdentifier);
asyncActionsExecutor.execute(getPolar, GET_POLAR_CATEGORY,
new MarkedAsyncCallback<>(new AsyncCallback<Boolean>() {
@Override
public void onFailure(Throwable caught) {
// TODO: add corresponding message to string-messages
// Window.setStatus(stringMessages.errorFetchingWindStreamletData(caught.getMessage()));
Window.setStatus(GET_POLAR_CATEGORY);
}
@Override
public void onSuccess(Boolean result) {
// store results
hasPolar = result.booleanValue();
}
}));
}
/**
* Creates a header panel where additional information can be displayed by using
@@ -710,6 +738,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
public void onSuccess(RaceMapDataDTO raceMapDataDTO) {
if (map != null && raceMapDataDTO != null) {
quickRanks = raceMapDataDTO.quickRanks;
if (showViewSimulation && settings.isShowSimulationOverlay()) {
simulationOverlay.updateLeg(getCurrentLeg(), newTime, false);
}
// process response only if not received out of order
if (startedProcessingRequestID < requestID) {
startedProcessingRequestID = requestID;
@@ -1918,7 +1949,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
@Override
public SettingsDialogComponent<RaceMapSettings> getSettingsDialogComponent() {
return new RaceMapSettingsDialogComponent(settings, stringMessages, this.showViewSimulation);
return new RaceMapSettingsDialogComponent(settings, stringMessages, this.showViewSimulation && this.hasPolar);
}
@Override
@@ -1981,7 +2012,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
if (newSettings.isShowSimulationOverlay() != settings.isShowSimulationOverlay()) {
settings.setShowSimulationOverlay(newSettings.isShowSimulationOverlay());
simulationOverlay.setVisible(newSettings.isShowSimulationOverlay());
simulationOverlay.timeChanged(timer.getTime(), null);
simulationOverlay.updateLeg(getCurrentLeg(), timer.getTime(), true);
}
if (requiredRedraw) {
redraw();
@@ -2186,6 +2217,15 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
}
return result;
}
public int getCurrentLeg() {
com.sap.sse.common.Util.Pair<Integer, CompetitorDTO> leaderWithLeg = this.getLeadingVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow());
if (leaderWithLeg == null) {
return 0;
} else {
return leaderWithLeg.getA();
}
}
private Image createSAPLogo() {
ImageResource sapLogoResource = resources.sapLogoOverlay();
@@ -0,0 +1,89 @@
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.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<Number> {
private final Chart chart = createPolarChartWidget();
private final Map<String, Series> dataByKeyStringAndAngle = new HashMap<String, Series>();
@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))
.setPolar(true).setHeight100().setWidth100();
polarSheetChart.getYAxis().setMin(0);
return polarSheetChart;
}
@Override
public void showResult(QueryResult<Number> result) {
resetChartData();
Map<GroupKey, Number> results = result.getResults();
if (!results.isEmpty()) {
for (Entry<GroupKey, Number> 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<String> detailedErrors) {
// TODO Auto-generated method stub
}
}
@@ -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<Integer, PolarSheetsHistogramData> histogramDataPerAngle) {
chart.setTitle("");
Series series = chart.createSeries();
Point[] points = toPoints(histogramDataPerAngle);
series.setPoints(points);
chart.addSeries(series);
}
private Point[] toPoints(Map<Integer, PolarSheetsHistogramData> 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();
}
}
@@ -42,10 +42,13 @@ public class PolarSheetsChartPanel extends DockLayoutPanel {
private PolarSheetGenerationSettings settings;
private final Map<String,PolarSheetsData> 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<String, PolarSheetsData>();
setSize("100%", "100%");
chart = createPolarSheetChart();
@@ -121,10 +124,11 @@ 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);
angleOverDataSizeHistogramPanel.addData(result.getHistogramDataMap().get(i));
}
}
}
@@ -154,14 +158,16 @@ public class PolarSheetsChartPanel extends DockLayoutPanel {
Point[] points = new Point[360];
List<Integer> 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;
}
@@ -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;
@@ -58,6 +60,7 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC
private List<RegattaAndRaceIdentifier> selectedRaces;
private PolarSheetsChartPanel chartPanel;
private PolarSheetsHistogramPanel histogramPanel;
private AngleOverDataSizeHistogramPanel angleOverDataSizeHistogramPanel;
private Label polarSheetsGenerationLabel;
private Label dataCountLabel;
@@ -93,8 +96,10 @@ 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();
angleOverDataSizeHistogramPanel = new AngleOverDataSizeHistogramPanel(stringMessages);
PolarSheetsChartPanel polarSheetsChartPanel = createPolarSheetsChartPanel(angleOverDataSizeHistogramPanel);
DockLayoutPanel polarChartAndControlPanel = new DockLayoutPanel(Unit.PCT);
PolarChartControlPanel polarChartControlPanel = new PolarChartControlPanel(stringMessages,
polarSheetsChartPanel);
@@ -108,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);
@@ -119,6 +128,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<PolarSheetGenerationResponse>() {
@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<List<String>>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(caught.getLocalizedMessage());
}
@Override
public void onSuccess(List<String> result) {
for (String item : result) {
boatClassListBox.addItem(item);
}
}
});
return boatClassListBox;
}
private void setEventListenersForPolarSheetChart() {
PointSelectEventHandler pointSelectEventHandler = new PointSelectEventHandler() {
@Override
@@ -144,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;
}
@@ -190,7 +190,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.RaceLogEvent;
import com.sap.sailing.domain.racelog.RaceLogEventFactory;
@@ -332,6 +331,7 @@ 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.polars.PolarDataService;
import com.sap.sailing.resultimport.ResultUrlProvider;
import com.sap.sailing.resultimport.ResultUrlRegistry;
import com.sap.sailing.server.RacingEventService;
@@ -395,7 +395,7 @@ import com.sap.sailing.simulator.PolarDiagram;
import com.sap.sailing.simulator.SailingSimulator;
import com.sap.sailing.simulator.SimulationParameters;
import com.sap.sailing.simulator.TimedPositionWithSpeed;
import com.sap.sailing.simulator.impl.PolarDiagramCSV;
import com.sap.sailing.simulator.impl.PolarDiagramGPS;
import com.sap.sailing.simulator.impl.SailingSimulatorImpl;
import com.sap.sailing.simulator.impl.SimulationParametersImpl;
import com.sap.sailing.simulator.util.SailingSimulatorConstants;
@@ -439,7 +439,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private final ServiceTracker<DeviceIdentifierStringSerializationHandler, DeviceIdentifierStringSerializationHandler>
deviceIdentifierStringSerializationHandlerTracker;
private final com.sap.sailing.domain.tractracadapter.persistence.MongoObjectFactory tractracMongoObjectFactory;
private final DomainObjectFactory domainObjectFactory;
@@ -472,7 +472,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
private final SwissTimingReplayService swissTimingReplayService;
private final QuickRanksLiveCache quickRanksLiveCache;
public SailingServiceImpl() {
BundleContext context = Activator.getDefault();
Activator activator = Activator.getInstance();
@@ -1459,6 +1458,15 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
return result;
}
@Override
public Boolean getPolarResults(RegattaAndRaceIdentifier raceIdentifier) {
TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
BoatClass boatClass = trackedRace.getRace().getBoatClass();
PolarDataService polarData = getService().getPolarDataService();
PolarDiagram polarDiagram = new PolarDiagramGPS(boatClass, polarData);
return new Boolean(polarDiagram != null);
};
@Override
public SimulatorResultsDTO getSimulatorResults(RegattaAndRaceIdentifier raceIdentifier, Date from, Date prevStartTime) {
TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier);
@@ -1472,13 +1480,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
if (trackedLeg == null) {
return result;
}
Waypoint fromWaypoint = trackedLeg.getLeg().getFrom();
Position startPosition = trackedRace.getApproximatePosition(fromWaypoint, fromTimePoint);
Waypoint fromWaypoint = trackedLeg.getLeg().getFrom();
// get next mark as end-position
Waypoint toWaypoint = trackedLeg.getLeg().getTo();
Position endPosition = trackedRace.getApproximatePosition(toWaypoint, fromTimePoint);
TimePoint startTimePoint = null;
TimePoint endTimePoint = null;
@@ -1495,9 +1500,11 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
}
legDuration = endTimePoint.asMillis() - startTimePoint.asMillis();
Position startPosition = trackedRace.getApproximatePosition(fromWaypoint, startTimePoint);
Position endPosition = trackedRace.getApproximatePosition(toWaypoint, endTimePoint);
if (startTimePoint.asDate().equals(prevStartTime)) {
return new SimulatorResultsDTO(startTimePoint.asDate(), 0, null, null, null, null);
return new SimulatorResultsDTO(startTimePoint.asDate(), 0, 0, null, null, null, null);
}
// determine legtype upwind/downwind/reaching
@@ -1518,14 +1525,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
List<Position> course = new ArrayList<Position>();
course.add(startPosition);
course.add(endPosition);
String csvFilePath = "PolarDiagram49STG.csv";
PolarDiagram polarDiagram = null;
try {
polarDiagram = new PolarDiagramCSV(csvFilePath);
} catch (IOException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
BoatClass boatClass = trackedRace.getRace().getBoatClass();
PolarDataService polarData = getService().getPolarDataService();
PolarDiagram polarDiagram = new PolarDiagramGPS(boatClass, polarData);
SimulationParameters simulationPars = new SimulationParametersImpl(course, polarDiagram, windField, SailingSimulatorConstants.ModeEvent, true, true);
// for upwind/downwind, run simulation with start-time, start-position, end-position and windfield
@@ -1559,7 +1561,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
rcDTO.coursePositions.waypointPositions.add(posDTO);
posDTO = SimulatorServiceUtils.toPositionDTO(endPosition);
rcDTO.coursePositions.waypointPositions.add(posDTO);
result = new SimulatorResultsDTO(startTimePoint.asDate(), legDuration, rcDTO, pathDTOs, null, null);
result = new SimulatorResultsDTO(startTimePoint.asDate(), timeStep.asMillis(), legDuration, rcDTO, pathDTOs, null, null);
}
}
@@ -3883,15 +3885,33 @@ 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();
PolarDataService polarDataService = service.getPolarDataService();
PolarSheetsData result = polarDataService.generatePolarSheet(trackedRaces, settings, executor);
return new PolarSheetGenerationResponseImpl(id, name, result);
}
@Override
public List<String> getBoatClassNamesWithPolarSheetsAvailable() {
Set<BoatClass> boatClasses = getService().getPolarDataService().getAllBoatClassesWithPolarSheetsAvailable();
List<String> names = new ArrayList<String>();
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<TrackedRace> trackedRaces) {
BoatClass boatClass = null;
for (TrackedRace race : trackedRaces) {
@@ -5075,4 +5095,4 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
}
return null;
}
}
}
@@ -301,7 +301,7 @@ public class SimulatorServiceImpl extends RemoteServiceServlet implements Simula
windFieldDTO = this.createWindFieldDTO(wf, startTime, endTime, timeStep, params); // params.isShowStreamlets2(), params.isShowLines(), params.getSeedLines());
}
return new SimulatorResultsDTO(null, 0, rcDTO, pathDTOs, windFieldDTO, simulatedPaths.notificationMessage);
return new SimulatorResultsDTO(null, timeStep.asMillis(), 0, rcDTO, pathDTOs, windFieldDTO, simulatedPaths.notificationMessage);
}
@Override
@@ -7,6 +7,7 @@ import com.google.gwt.user.client.rpc.IsSerializable;
public class SimulatorResultsDTO implements IsSerializable {
private Date startTime;
private long timeStep;
private long legDuration;
private RaceMapDataDTO raceCourse;
private WindFieldDTO windField;
@@ -15,6 +16,7 @@ public class SimulatorResultsDTO implements IsSerializable {
public SimulatorResultsDTO(){
this.startTime = null;
this.timeStep = 0;
this.legDuration = 0;
this.raceCourse = null;
this.windField = null;
@@ -22,8 +24,9 @@ public class SimulatorResultsDTO implements IsSerializable {
this.notificationMessage = "";
}
public SimulatorResultsDTO(final Date startTime, final long legDuration, final RaceMapDataDTO raceCourse, final PathDTO[] paths, final WindFieldDTO windField, final String notificationMessage) {
public SimulatorResultsDTO(final Date startTime, final long timeStep, final long legDuration, final RaceMapDataDTO raceCourse, final PathDTO[] paths, final WindFieldDTO windField, final String notificationMessage) {
this.startTime = startTime;
this.timeStep = timeStep;
this.legDuration = legDuration;
this.raceCourse = raceCourse;
this.paths = paths;
@@ -39,6 +42,14 @@ public class SimulatorResultsDTO implements IsSerializable {
this.startTime = startTime;
}
public long getTimeStep() {
return this.timeStep;
}
public void setTimeStep(long timeStep) {
this.timeStep = timeStep;
}
public long getLegDuration() {
return this.legDuration;
}
@@ -28,8 +28,6 @@ import com.sap.sailing.gwt.ui.simulator.util.ColorPalette;
import com.sap.sailing.gwt.ui.simulator.util.ColorPaletteGenerator;
import com.sap.sse.gwt.client.async.AsyncActionsExecutor;
import com.sap.sse.gwt.client.async.MarkedAsyncCallback;
import com.sap.sse.gwt.client.player.TimeListener;
import com.sap.sse.gwt.client.player.Timer;
/**
* A Google Maps overlay based on an HTML5 canvas for drawing simulation results on the {@link RaceMap}.
@@ -37,7 +35,7 @@ import com.sap.sse.gwt.client.player.Timer;
* @author Christopher Ronnewinkel (D036654)
*
*/
public class RaceSimulationOverlay extends FullCanvasOverlay implements TimeListener {
public class RaceSimulationOverlay extends FullCanvasOverlay {
public static final String GET_SIMULATION_CATEGORY = "getSimulation";
private final String textColor = "Black";
@@ -47,21 +45,18 @@ public class RaceSimulationOverlay extends FullCanvasOverlay implements TimeLis
private double rectWidth = 20;
private double rectHeight = 20;
private final StringMessages stringMessages;
private final Timer timer;
private Date lastSimulationTime;
private final long deltaSimulationTime = 15000;
private final RegattaAndRaceIdentifier raceIdentifier;
private final SailingServiceAsync sailingService;
private final AsyncActionsExecutor asyncActionsExecutor;
private final ColorPalette colors;
private SimulatorResultsDTO simulationResult;
private PathDTO racePath;
private int raceLeg = 0;
private Date prevStartTime;
private Canvas simulationLegend;
public RaceSimulationOverlay(MapWidget map, int zIndex, final Timer timer, RegattaAndRaceIdentifier raceIdentifier, SailingServiceAsync sailingService, StringMessages stringMessages, AsyncActionsExecutor asyncActionsExecutor) {
public RaceSimulationOverlay(MapWidget map, int zIndex, RegattaAndRaceIdentifier raceIdentifier, SailingServiceAsync sailingService, StringMessages stringMessages, AsyncActionsExecutor asyncActionsExecutor) {
super(map, zIndex);
this.timer = timer;
this.raceIdentifier = raceIdentifier;
this.sailingService = sailingService;
this.stringMessages = stringMessages;
@@ -69,30 +64,22 @@ public class RaceSimulationOverlay extends FullCanvasOverlay implements TimeLis
this.colors = new ColorPaletteGenerator();
}
@Override
public void timeChanged(final Date newTime, final Date oldTime) {
if (lastSimulationTime == null) {
lastSimulationTime = new Date(newTime.getTime() - deltaSimulationTime);
}
if (Math.abs(newTime.getTime() - lastSimulationTime.getTime()) >= deltaSimulationTime) {
lastSimulationTime = newTime;
this.simulate(newTime);
}
}
@Override
public void addToMap() {
if (timer != null) {
timer.addTimeListener(this);
}
public void updateLeg(int newLeg, Date newTime, boolean clearCanvas) {
if ((newLeg != raceLeg)&&(this.isVisible())) {
raceLeg = newLeg;
if (clearCanvas) {
this.clearCanvas();
}
this.simulate(newTime);
}
}
@Override
public void removeFromMap() {
if (timer != null) {
timer.removeTimeListener(this);
}
this.setVisible(false);
public void setVisible(boolean isVisible) {
super.setVisible(isVisible);
if (simulationLegend != null) {
simulationLegend.setVisible(isVisible);
}
}
@Override
@@ -177,7 +164,20 @@ public class RaceSimulationOverlay extends FullCanvasOverlay implements TimeLis
}
}
ctxt.stroke();
ctxt.setGlobalAlpha(1.0);
SimulatorWindDTO start = points.get(0);
long timeStep = simulationResult.getTimeStep();
for(SimulatorWindDTO point : points) {
if ((point.timepoint - start.timepoint) % (timeStep) != 0) {
continue;
}
Point px = mapProjection.fromLatLngToContainerPixel(LatLng.newInstance(point.position.latDeg, point.position.lngDeg));
ctxt.beginPath();
ctxt.arc(px.getX(), px.getY(), 1.5, 0, 2*Math.PI);
ctxt.closePath();
ctxt.stroke();
}
colorIdx--;
}
+31 -30
View File
@@ -1,30 +1,31 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Test
Bundle-SymbolicName: com.sap.sailing.mongodb.test
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Fragment-Host: com.sap.sse.mongodb
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.shared.android,
com.sap.sailing.domain.test,
com.tractrac.clientmodule;bundle-version="2.0.0",
com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.persistence,
com.sap.sailing.server,
com.sap.sailing.expeditionconnector,
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.hamcrest;bundle-version="1.1.0",
org.mockito.mockito-core;bundle-version="1.9.5",
com.sap.sailing.domain.racelogtrackingadapter.testsupport,
com.sap.sailing.server.gateway;bundle-version="1.0.0",
com.sap.sse.common
Import-Package: com.sap.sailing.domain.common,
com.sap.sailing.domain.common.impl,
com.sap.sailing.domain.common.racelog,
com.sap.sailing.domain.common.racelog.tracking
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Test
Bundle-SymbolicName: com.sap.sailing.mongodb.test
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Fragment-Host: com.sap.sse.mongodb
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.domain.shared.android,
com.sap.sailing.domain.test,
com.tractrac.clientmodule;bundle-version="2.0.0",
com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.persistence,
com.sap.sailing.server,
com.sap.sailing.expeditionconnector,
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.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.domain.racelogtrackingadapter.testsupport,
com.sap.sailing.server.gateway;bundle-version="1.0.0",
com.sap.sse.common
Import-Package: com.sap.sailing.domain.common,
com.sap.sailing.domain.common.impl,
com.sap.sailing.domain.common.racelog,
com.sap.sailing.domain.common.racelog.tracking
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.polars.test</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.pde.PluginNature</nature>
<nature>org.eclipse.jdt.core.javanature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,3 @@
eclipse.preferences.version=1
pluginProject.extensions=false
resolve.requirebundle=false
@@ -0,0 +1,12 @@
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.8
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"
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
@@ -0,0 +1,90 @@
package com.sap.sailing.polars.caching.test;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.notNullValue;
import static org.junit.Assert.assertThat;
import static org.mockito.Mockito.mock;
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.tracking.TrackedRace;
import com.sap.sailing.polars.caching.PolarFixCacheRaceInterval;
import com.sap.sse.common.Util.Pair;
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<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> joinedMap = joined
.getCompetitorAndTimepointsForRace();
Map<Competitor, Pair<TimePoint, TimePoint>> explicitIntervalForRace1 = joinedMap.get(mockedTrackedRace1);
assertThat(explicitIntervalForRace1, notNullValue(Map.class));
Pair<TimePoint, TimePoint> 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<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> competitorAndTimepointsForRace = new HashMap<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>>();
HashMap<Competitor, Pair<TimePoint, TimePoint>> mapForRace1 = new HashMap<Competitor, Pair<TimePoint, TimePoint>>();
mapForRace1.put(mockedCompetitor1, new Pair<TimePoint, TimePoint>(timePoint1, timePoint1));
competitorAndTimepointsForRace.put(mockedTrackedRace1, mapForRace1);
HashMap<Competitor, Pair<TimePoint, TimePoint>> mapForRace2 = new HashMap<Competitor, Pair<TimePoint, TimePoint>>();
mapForRace2.put(mockedCompetitor2, new Pair<TimePoint, TimePoint>(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<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> competitorAndTimepointsForRace = new HashMap<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>>();
HashMap<Competitor, Pair<TimePoint, TimePoint>> mapForRace1 = new HashMap<Competitor, Pair<TimePoint, TimePoint>>();
mapForRace1.put(mockedCompetitor1, new Pair<TimePoint, TimePoint>(
timePoint2, timePoint2));
competitorAndTimepointsForRace.put(mockedTrackedRace1, mapForRace1);
PolarFixCacheRaceInterval interval2 = new PolarFixCacheRaceInterval(
competitorAndTimepointsForRace);
return interval2;
}
}
@@ -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<Speed> clusterForZero = group.getClusterFor(new KnotSpeedImpl(0));
assertThat(clusterForZero, notNullValue());
Cluster<Speed> 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<Speed> clusterForNine = group.getClusterFor(new KnotSpeedImpl(9));
assertThat(clusterForNine, notNullValue());
Cluster<Speed> 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<Speed> clusterForFourteen = group.getClusterFor(new KnotSpeedImpl(14));
assertThat(clusterForFourteen, notNullValue());
Cluster<Speed> 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<Speed> clusterForFourteen = group.getClusterFor(new KnotSpeedImpl(14));
assertThat(clusterForFourteen, notNullValue());
Cluster<Speed> clusterForSeven = group.getClusterFor(new KnotSpeedImpl(7));
assertThat(clusterForSeven, notNullValue());
assertThat(clusterForSeven == clusterForFourteen, equalTo(false));
assertThat(group.getClusterFor(new KnotSpeedImpl(10.5)), nullValue());
}
}
@@ -0,0 +1,209 @@
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.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.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.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.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.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 {
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,
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);
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);
GPSFixMoving fix3_1 = createMockedFix(13, 45, 54.873740, 10.193648, 43.2, 10);
Competitor competitor3 = mock(Competitor.class);
Map<Competitor, Set<GPSFixMoving>> fixesPerCompetitor = new HashMap<Competitor, Set<GPSFixMoving>>();
Set<GPSFixMoving> setForCompetitor1 = new HashSet<GPSFixMoving>();
setForCompetitor1.add(fix1_1);
setForCompetitor1.add(fix1_2);
fixesPerCompetitor.put(competitor1, setForCompetitor1);
Set<GPSFixMoving> setForCompetitor2 = new HashSet<GPSFixMoving>();
setForCompetitor2.add(fix2_1);
setForCompetitor2.add(fix2_2);
fixesPerCompetitor.put(competitor2, setForCompetitor2);
Set<GPSFixMoving> setForCompetitor3 = new HashSet<GPSFixMoving>();
setForCompetitor2.add(fix3_1);
fixesPerCompetitor.put(competitor3, setForCompetitor3);
TrackedRace trackedRace = createMockedTrackedRace(fixesPerCompetitor, mockedBoatClass);
for (Entry<Competitor, Set<GPSFixMoving>> entry : fixesPerCompetitor.entrySet()) {
Competitor competitor = entry.getKey();
Set<GPSFixMoving> fixSet = entry.getValue();
for (GPSFixMoving fix : fixSet) {
miner.addFix(fix, competitor, trackedRace);
}
}
int millisLeft = 500000;
while (miner.isCurrentlyActiveAndOrHasQueue() && millisLeft > 0) {
Thread.sleep(100);
millisLeft = millisLeft - 100;
if (miner.isCurrentlyActiveAndOrHasQueue() && millisLeft <= 0) {
throw new TimeoutException();
}
}
SpeedWithConfidence<Void> 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<Void> estimatedSpeed2 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15),
new DegreeBearingImpl(42));
assertThat(estimatedSpeed2, is(notNullValue()));
assertThat(estimatedSpeed2.getObject().getKnots(), is(closeTo(10.05, EPSILON)));
SpeedWithConfidence<Void> estimatedSpeed3 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15),
new DegreeBearingImpl(42.8));
assertThat(estimatedSpeed3, is(notNullValue()));
assertThat(estimatedSpeed3.getObject().getKnots(), is(closeTo(10, EPSILON)));
}
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(lat, lng));
Calendar cal = Calendar.getInstance();
cal.set(2014, 4, 3, hour, minute);
TimePoint fixTimePoint = new MillisecondsTimePoint(cal.getTime());
when(fix.getTimePoint()).thenReturn(fixTimePoint);
Bearing bearing = new DegreeBearingImpl(bearingRaw);
SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(speed, bearing);
when(fix.getSpeed()).thenReturn(speedWithBearing);
return fix;
}
private TrackedRace createMockedTrackedRace(Map<Competitor, Set<GPSFixMoving>> fixesPerCompetitor,
BoatClass mockedBoatClass) {
TrackedRace trackedRace = mock(TrackedRace.class);
for (Entry<Competitor, Set<GPSFixMoving>> competitorEntry : fixesPerCompetitor.entrySet()) {
Competitor competitor = competitorEntry.getKey();
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = new DynamicGPSFixMovingTrackImpl<Competitor>(
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(mockedRaceDefinition.getBoatClass()).thenReturn(mockedBoatClass);
when(trackedRace.getRace()).thenReturn(mockedRaceDefinition);
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);
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);
Bearing windBearing = new DegreeBearingImpl(180);
SpeedWithBearing windSpeed = new KnotSpeedWithBearingImpl(15, windBearing);
Wind wind = new WindImpl(new DegreePosition(54.431952, 10.186767), startOfRace, windSpeed);
WindWithConfidence<Pair<Position, TimePoint>> 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;
}
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;
}
}
@@ -0,0 +1,233 @@
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;
import org.hamcrest.number.IsCloseTo;
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.IncrementalLeastSquaresProcessor;
public class IncrementalLinearRegressionTest {
private static double EPSILON = 1E-12;
private static double VAGUE_EPSILON = 0.1;
private IncrementalLinearRegressionProcessor createRegressionProcessor() {
IncrementalLinearRegressionProcessor processor = new IncrementalLeastSquaresProcessor();
return processor;
}
@Test
public void testRegressionWithTwoDataPoints() throws NotEnoughDataHasBeenAddedException {
IncrementalLinearRegressionProcessor processor = createRegressionProcessor();
double x1 = 3;
double y1 = 7;
processor.addMeasuredPoint(x1, y1);
double x2 = 6;
double y2 = 14;
processor.addMeasuredPoint(x2, y2);
double y = processor.getEstimatedY(x1);
assertThat(y, new IsCloseTo(y1, EPSILON));
y = processor.getEstimatedY(x2);
assertThat(y, new IsCloseTo(y2, EPSILON));
y = processor.getEstimatedY(4.5);
assertThat(y, new IsCloseTo(10.5, EPSILON));
}
@Test
public void testRegressionWithMultipleDataPoints() throws NotEnoughDataHasBeenAddedException {
IncrementalLinearRegressionProcessor processor = createRegressionProcessor();
for (int i = -50; i < 51; i++) {
processor.addMeasuredPoint(i, i);
}
double y = processor.getEstimatedY(-2);
assertThat(y, new IsCloseTo(-2, EPSILON));
// 2 Points off the line
processor.addMeasuredPoint(-8, -3);
processor.addMeasuredPoint(8, -3);
y = processor.getEstimatedY(-2);
assertThat(y, new IsCloseTo(-2, VAGUE_EPSILON));
y = processor.getEstimatedY(20);
assertThat(y, new IsCloseTo(20, VAGUE_EPSILON));
}
@Test
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.getEstimatedY(-2);
assertThat(y, new IsCloseTo(-2, VAGUE_EPSILON));
y = processor.getEstimatedY(20);
assertThat(y, new IsCloseTo(20, VAGUE_EPSILON));
}
@Test
public void testRegressionWithZeroValueData() throws NotEnoughDataHasBeenAddedException {
IncrementalLinearRegressionProcessor processor = createRegressionProcessor();
double x1 = 0;
double y1 = 0;
processor.addMeasuredPoint(x1, y1);
double x2 = 5;
double y2 = 0;
processor.addMeasuredPoint(x2, y2);
double y = processor.getEstimatedY(x1);
assertThat(y, new IsCloseTo(y1, EPSILON));
y = processor.getEstimatedY(x2);
assertThat(y, new IsCloseTo(y2, EPSILON));
}
@Test
public void testRegressionWithNegativeData() throws NotEnoughDataHasBeenAddedException {
IncrementalLinearRegressionProcessor processor = createRegressionProcessor();
double x1 = -5;
double y1 = -5;
processor.addMeasuredPoint(x1, y1);
double x2 = 5;
double y2 = 5;
processor.addMeasuredPoint(x2, y2);
double y = processor.getEstimatedY(x1);
assertThat(y, new IsCloseTo(y1, EPSILON));
y = processor.getEstimatedY(x2);
assertThat(y, new IsCloseTo(y2, EPSILON));
y = processor.getEstimatedY(0);
assertThat(y, new IsCloseTo(0, EPSILON));
}
@Test
public void testRegressionWithSmallValuedData() throws NotEnoughDataHasBeenAddedException {
IncrementalLinearRegressionProcessor processor = createRegressionProcessor();
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);
if (i > 0) {
double y = processor.getEstimatedY(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.getEstimatedY(0);
assertThat(y, new IsCloseTo(0, EPSILON));
}
@Test(expected = NotEnoughDataHasBeenAddedException.class)
public void testNoDataExceptionThrowing() throws NotEnoughDataHasBeenAddedException {
IncrementalLinearRegressionProcessor processor = createRegressionProcessor();
processor.getEstimatedY(15);
}
/**
* Assert that y is constant for only one added data point
*
* @throws NotEnoughDataHasBeenAddedException
*/
@Test
public void testRegressionWithOneDataPoint() throws NotEnoughDataHasBeenAddedException {
IncrementalLinearRegressionProcessor processor = createRegressionProcessor();
double x1 = 3;
double y1 = 5;
processor.addMeasuredPoint(x1, y1);
double y = processor.getEstimatedY(20);
assertThat(y, is(y1));
}
/*
* 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.getEstimatedY(0), new IsCloseTo(-0.262323073774029, EPSILON));
assertThat(regression.getEstimatedY(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);
}
}
@@ -0,0 +1,183 @@
package com.sap.sailing.polars.test;
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 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.Speed;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
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.regression.NotEnoughDataHasBeenAddedException;
public class PolarSheetAnalyzerTest {
@Test
public void testSpeedAndBearingCalculation() throws NotEnoughDataHasBeenAddedException {
PolarSheetAnalyzer analyzer = new PolarSheetAnalyzerImpl(createMockedPolarDataService());
BoatClass boatClass = mock(BoatClass.class);
SpeedWithBearingWithConfidence<Void> result = analyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass,
new KnotSpeedImpl(14));
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<Void> result2 = analyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass,
new KnotSpeedImpl(14));
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<Void> result3 = analyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass,
new KnotSpeedImpl(14));
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<Void> result4 = analyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass,
new KnotSpeedImpl(14));
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));
}
private PolarDataService createMockedPolarDataService() throws NotEnoughDataHasBeenAddedException {
PolarDataService mockedPolarDataService = mock(PolarDataService.class);
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(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.478048671702888), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(50)))).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.466303997538812), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(51)))).thenReturn(
new SpeedWithConfidenceImpl<Void>(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<Void>(new KnotSpeedImpl(13.794354053931528), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(142)))).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.665782579628802), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(143)))).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(14.169301888320263), 0.5, null));
//Port Upwind
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-48)))).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.445599410456111), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-50)))).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.553274292235153), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-49)))).thenReturn(
new SpeedWithConfidenceImpl<Void>(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<Void>(new KnotSpeedImpl(13.78894715705271), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-144)))).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.420656294986587), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-143)))).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.268607457651942), 0.5, null));
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 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,
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 };
return dataCountsPerAngle;
}
private class SpeedMatcher extends ArgumentMatcher<Speed> {
private final double speedToMatchInKnots;
public SpeedMatcher(double speedToMatchInKnots) {
this.speedToMatchInKnots = speedToMatchInKnots;
}
@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<Bearing> {
private final double bearingToMatchInDegrees;
public BearingMatcher(double bearingToMatchInDegrees) {
this.bearingToMatchInDegrees = bearingToMatchInDegrees;
}
@Override
public boolean matches(Object argument) {
boolean result = false;
if (argument != null) {
Bearing bearing = (Bearing) argument;
if (bearing.getDegrees() > bearingToMatchInDegrees - 0.4999999
&& bearing.getDegrees() < bearingToMatchInDegrees + 0.49999999) {
result = true;
}
}
return result;
}
}
}
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.test;
package com.sap.sailing.polars.test;
import java.util.ArrayList;
import java.util.HashMap;
@@ -9,6 +9,7 @@ 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;
@@ -44,10 +45,6 @@ import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
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;
@@ -61,11 +58,19 @@ 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.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;
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,
@@ -81,27 +86,29 @@ 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<TrackedRace>(),
PolarFixAggregator resultContainer = new PolarFixAggregator(new SimplePolarFixRaceInterval(
new HashSet<TrackedRace>()),
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,
resultContainer, startTime, endTime, competitor, settings);
PolarFixAggregationWorker task = new PolarFixAggregationWorker(race,
resultContainer, startTime, endTime,
competitor, settings, null);
executor.execute(task);
double timeUntilTimeout = 1000;
while (!task.isDone() && timeUntilTimeout > 0) {
Thread.sleep(100);
timeUntilTimeout = timeUntilTimeout - 0.1;
}
Set<PolarFix> fixes = resultContainer.getAggregationResultAsSingleList();
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]);
@@ -119,15 +126,15 @@ public class PolarSheetGenerationTest {
List<DataPointWithOriginInfo> rawData = new ArrayList<DataPointWithOriginInfo>();
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);
@@ -144,49 +151,49 @@ public class PolarSheetGenerationTest {
public void testOutlierNeighborhoodAlgorithm() {
PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings();
List<DataPointWithOriginInfo> values = new ArrayList<DataPointWithOriginInfo>();
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<Integer, List<DataPointWithOriginInfo>> valuesInMap = new HashMap<Integer, List<DataPointWithOriginInfo>>();
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);
+7
View File
@@ -0,0 +1,7 @@
<?xml version="1.0" encoding="UTF-8"?>
<classpath>
<classpathentry kind="src" path="src"/>
<classpathentry kind="con" path="org.eclipse.jdt.launching.JRE_CONTAINER"/>
<classpathentry kind="con" path="org.eclipse.pde.core.requiredPlugins"/>
<classpathentry kind="output" path="bin"/>
</classpath>
+28
View File
@@ -0,0 +1,28 @@
<?xml version="1.0" encoding="UTF-8"?>
<projectDescription>
<name>com.sap.sailing.polars</name>
<comment></comment>
<projects>
</projects>
<buildSpec>
<buildCommand>
<name>org.eclipse.jdt.core.javabuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.ManifestBuilder</name>
<arguments>
</arguments>
</buildCommand>
<buildCommand>
<name>org.eclipse.pde.SchemaBuilder</name>
<arguments>
</arguments>
</buildCommand>
</buildSpec>
<natures>
<nature>org.eclipse.jdt.core.javanature</nature>
<nature>org.eclipse.pde.PluginNature</nature>
</natures>
</projectDescription>
@@ -0,0 +1,23 @@
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.8
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.regression
Bundle-ActivationPolicy: lazy
Require-Bundle:
com.sap.sailing.domain.common;bundle-version="1.0.0",
com.sap.sailing.domain,
com.sap.sse.datamining,
com.sap.sse.datamining.shared;bundle-version="1.0.0"
Import-Package:
com.sap.sailing.util,
com.sap.sse.common
@@ -0,0 +1,4 @@
source.. = src/
output.. = bin/
bin.includes = META-INF/,\
.
+12
View File
@@ -0,0 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?>
<project xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd" xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<modelVersion>4.0.0</modelVersion>
<parent>
<artifactId>root</artifactId>
<groupId>com.sap.sailing</groupId>
<version>1.0.0-SNAPSHOT</version>
</parent>
<artifactId>com.sap.sailing.polars</artifactId>
<packaging>eclipse-plugin</packaging>
</project>
@@ -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());
}
}
@@ -0,0 +1,130 @@
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.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;
import com.sap.sailing.polars.data.PolarFix;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
/**
* 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)}
* which also returns the average angle for the provided parameters.
*
* @author Frederik Petersen (D054528)
*
*/
public interface PolarDataService {
/**
*
* @param boatClass
* @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.
* @throws NotEnoughDataHasBeenAddedException
*/
SpeedWithConfidence<Void> 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<br/>
* A value with zero confidence doesn't have any significance!<br/><br/>
*
* The bearing is somewhere between -179 to +180<br/><br/>
*
* Get the speed using returnValue.getObject()<br/><br/>
*
* 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<Void> getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed,
LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException;
/**
* 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<TrackedRace> trackedRaces, PolarSheetGenerationSettings settings,
Executor executor) throws InterruptedException, ExecutionException;
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<PolarFix> 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}.
*/
PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass);
/**
*
* @return The {@link BoatClass}es for which there are polar sheets available via
* {@link PolarDataService#getPolarSheetForBoatClass(BoatClass)}
*/
Set<BoatClass> getAllBoatClassesWithPolarSheetsAvailable();
void competitorPositionChanged(GPSFixMoving fix, Competitor competitor, TrackedRace createdTrackedRace);
/**
* 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);
}
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.polarsheets;
package com.sap.sailing.polars.aggregation;
import java.util.ArrayList;
import java.util.Collection;
@@ -19,22 +19,22 @@ 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.sse.common.Util;
import com.sap.sailing.polars.data.PolarFix;
import com.sap.sailing.polars.data.impl.PolarFixImpl;
import com.sap.sse.common.Util.Pair;
/**
* 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,9 +50,11 @@ public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable
private PolarSheetGenerationSettings settings;
public PerRaceAndCompetitorPolarSheetGenerationWorker(TrackedRace race,
PolarSheetGenerationWorker polarSheetGenerationWorker, TimePoint startTime, TimePoint endTime,
Competitor competitor, PolarSheetGenerationSettings settings) {
private Pair<TimePoint, TimePoint> intervalBeginningAndEnd;
public PolarFixAggregationWorker(TrackedRace race, PolarFixAggregator polarSheetGenerationWorker,
TimePoint startTime, TimePoint endTime, Competitor competitor, PolarSheetGenerationSettings settings,
Pair<TimePoint, TimePoint> intervalBeginningAndEnd) {
super();
this.race = race;
this.polarSheetGenerationWorker = polarSheetGenerationWorker;
@@ -60,9 +62,24 @@ public class PerRaceAndCompetitorPolarSheetGenerationWorker 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 PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable
GPSFixTrack<Competitor, GPSFixMoving> track = race.getTrack(competitor);
track.lockForRead();
try {
Iterator<GPSFixMoving> fixesIterator = track.getFixesIterator(startTime, true);
Iterator<GPSFixMoving> 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<MarkPassing> markPassings = race.getMarkPassings(competitor);
@@ -136,40 +163,39 @@ public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable
private void addFixIfValid(GPSFixTrack<Competitor, GPSFixMoving> track, GPSFixMoving fix) {
if (!track.hasDirectionChange(fix.getTimePoint(), race.getRace().getBoatClass()
.getManeuverDegreeAngleThreshold())) {
List<Util.Pair<String, WindWithConfidence<Util.Pair<Position, TimePoint>>>> windWithConfidenceList = new ArrayList<Util.Pair<String, WindWithConfidence<Util.Pair<Position, TimePoint>>>>();
List<Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>> windWithConfidenceList = new ArrayList<Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>>();
if (settings.useOnlyWindGaugesForWindSpeed()) {
if (settings.splitByWindgauges()) {
windWithConfidenceList.addAll(addAllWindsOfWindGaugesSplitOneByOne(fix));
} else {
windWithConfidenceList
.add(new Util.Pair<String, WindWithConfidence<Util.Pair<Position, TimePoint>>>(
.add(new Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>(
createWindGaugesString(race), race.getWindWithConfidence(
fix.getPosition(), fix.getTimePoint(),
collectWindSourcesToIgnoreForSpeed())));
}
} else {
windWithConfidenceList.add(new Util.Pair<String, WindWithConfidence<Util.Pair<Position, TimePoint>>>(
windWithConfidenceList.add(new Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>(
"Combined", race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint())));
}
for (Util.Pair<String, WindWithConfidence<Util.Pair<Position, TimePoint>>> windWithSourceIdStringPair : windWithConfidenceList) {
WindWithConfidence<Util.Pair<Position, TimePoint>> windWithConfidence = windWithSourceIdStringPair
for (Pair<String, WindWithConfidence<Pair<Position, TimePoint>>> windWithSourceIdStringPair : windWithConfidenceList) {
WindWithConfidence<Pair<Position, TimePoint>> windWithConfidence = windWithSourceIdStringPair
.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(race.getRaceIdentifier(), polarFix);
}
}
}
}
private Collection<? extends Util.Pair<String, WindWithConfidence<Util.Pair<Position, TimePoint>>>> addAllWindsOfWindGaugesSplitOneByOne(
private Collection<? extends Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>> addAllWindsOfWindGaugesSplitOneByOne(
GPSFixMoving fix) {
Iterable<WindSource> windGaugeSources = race.getWindSources(WindSourceType.EXPEDITION);
List<Util.Pair<String, WindWithConfidence<Util.Pair<Position, TimePoint>>>> windWithConfidenceList
= new ArrayList<Util.Pair<String, WindWithConfidence<Util.Pair<Position, TimePoint>>>>();
List<Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>> windWithConfidenceList = new ArrayList<Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>>();
for (WindSource windGaugeSource : windGaugeSources) {
Iterable<WindSource> allSources = race.getWindSources();
Set<WindSource> allSourcesButTheSingleWindGaugeSource = new HashSet<WindSource>();
@@ -178,7 +204,7 @@ public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable
allSourcesButTheSingleWindGaugeSource.add(windSource);
}
}
windWithConfidenceList.add(new Util.Pair<String, WindWithConfidence<Util.Pair<Position, TimePoint>>>(
windWithConfidenceList.add(new Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>(
windGaugeSource.getId().toString(), race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(),
allSourcesButTheSingleWindGaugeSource)));
}
@@ -0,0 +1,171 @@
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;
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.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.data.PolarFix;
import com.sap.sse.common.Util.Pair;
/**
* 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<Map<RegattaAndRaceIdentifier, List<PolarFix>>> {
private final Set<PolarFixAggregationWorker> workers;
private final Executor executor;
private final Map<RegattaAndRaceIdentifier, List<PolarFix>> fixes = new HashMap<RegattaAndRaceIdentifier, List<PolarFix>>();
/**
* Will prepare the {@link PolarFixAggregationWorker}s per race & per competitor. This includes determining start
* and end time.
*
* @param updateInterval
* from which the data is to be collected
* @param settings
* @param executor
* executes the tasks upon {@link #startPolarFixAggregation()}
*/
public PolarFixAggregator(PolarFixRaceInterval updateInterval, PolarSheetGenerationSettings settings,
Executor executor) {
this.executor = executor;
workers = new HashSet<PolarFixAggregationWorker>();
for (Entry<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> 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();
Map<Competitor, Pair<TimePoint, TimePoint>> detailedInterval = entry.getValue();
if (detailedInterval == null) {
Iterable<Competitor> competitors = raceDefinition.getCompetitors();
for (Competitor competitor : competitors) {
PolarFixAggregationWorker task = new PolarFixAggregationWorker(race, this, startTime, endTime,
competitor, settings, null);
workers.add(task);
}
} else {
for (Entry<Competitor, Pair<TimePoint, TimePoint>> competitorEntry : detailedInterval.entrySet()) {
Competitor competitor = competitorEntry.getKey();
Pair<TimePoint, TimePoint> intervalBeginningAndEnd = competitorEntry.getValue();
PolarFixAggregationWorker task = new PolarFixAggregationWorker(race, this, startTime, endTime,
competitor, settings, intervalBeginningAndEnd);
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(RegattaAndRaceIdentifier raceId, PolarFix polarFix) {
synchronized (fixes) {
List<PolarFix> existingList = fixes.get(raceId);
if (existingList == null) {
existingList = new ArrayList<PolarFix>();
fixes.put(raceId, existingList);
}
existingList.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 Map<RegattaAndRaceIdentifier, List<PolarFix>> get() throws InterruptedException, ExecutionException {
while (!isDone()) {
Thread.sleep(100);
}
return fixes;
}
@Override
public Map<RegattaAndRaceIdentifier, List<PolarFix>> 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;
}
public Set<PolarFix> getAggregationResultAsSingleList() throws InterruptedException,
ExecutionException {
Set<PolarFix> fixes = new HashSet<PolarFix>();
Map<RegattaAndRaceIdentifier, List<PolarFix>> aggregationResult = get();
for (List<PolarFix> list : aggregationResult.values()) {
fixes.addAll(list);
}
return fixes;
}
}
@@ -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.tracking.TrackedRace;
import com.sap.sse.common.Util.Pair;
public interface PolarFixRaceInterval {
Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> getCompetitorAndTimepointsForRace();
}
@@ -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.tracking.TrackedRace;
import com.sap.sse.common.Util.Pair;
public class SimplePolarFixRaceInterval implements PolarFixRaceInterval {
private final Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> intervalMap;
public SimplePolarFixRaceInterval(Set<TrackedRace> races) {
intervalMap = new HashMap<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>>();
for (TrackedRace race : races) {
intervalMap.put(race, null);
}
}
@Override
public Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> getCompetitorAndTimepointsForRace() {
return intervalMap;
}
}
@@ -0,0 +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.polars.regression.NotEnoughDataHasBeenAddedException;
public interface PolarSheetAnalyzer {
SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass,
Speed windSpeed) throws NotEnoughDataHasBeenAddedException;
SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass,
Speed windSpeed) throws NotEnoughDataHasBeenAddedException;
SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass,
Speed windSpeed) throws NotEnoughDataHasBeenAddedException;
SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass,
Speed windSpeed) throws NotEnoughDataHasBeenAddedException;
}
@@ -0,0 +1,161 @@
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.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.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;
import com.sap.sailing.polars.analysis.PolarSheetAnalyzer;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
/**
* Extracts typical measures from polar sheets.
*
* @author Frederik Petersen (D054528)
*
*/
public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer {
private static final int MINIMUM_DATA_COUNT_FOR_ONE_ANGLE = 5;
private PolarDataService polarDataService;
public PolarSheetAnalyzerImpl(PolarDataService polarDataService) {
this.polarDataService = polarDataService;
}
@Override
public SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed)
throws NotEnoughDataHasBeenAddedException {
int startAngleInclusive = 1;
int endAngleExclusive = 90;
SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive,
endAngleExclusive);
return speedWithBearing;
}
@Override
public SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed)
throws NotEnoughDataHasBeenAddedException {
int startAngleInclusive = 91;
int endAngleExclusive = 180;
SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive,
endAngleExclusive);
return speedWithBearing;
}
@Override
public SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed)
throws NotEnoughDataHasBeenAddedException {
int startAngleInclusive = 181;
int endAngleExclusive = 270;
SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive,
endAngleExclusive);
return speedWithBearing;
}
@Override
public SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed)
throws NotEnoughDataHasBeenAddedException {
int startAngleInclusive = 271;
int endAngleExclusive = 360;
SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive,
endAngleExclusive);
return speedWithBearing;
}
private SpeedWithBearingWithConfidence<Void> estimateAnglePeakAndAverageSpeed(BoatClass boatClass, Speed windSpeed,
int startAngleInclusive, int endAngleExclusive) throws NotEnoughDataHasBeenAddedException {
Integer[] dataCountPerAngle = getDataCountArray(boatClass, windSpeed, startAngleInclusive, endAngleExclusive);
double estimatedPeak = estimatePeak(boatClass, windSpeed, startAngleInclusive, endAngleExclusive, dataCountPerAngle);
double convertedAngleIfOver180 = convertAngleIfNecessary(estimatedPeak);
SpeedWithConfidence<Void> 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(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, int startAngleInclusive, int endAngleExclusive) {
Integer[] dataCountPerAngle = polarDataService.getDataCountsForWindSpeed(boatClass, windSpeed, startAngleInclusive, endAngleExclusive);
return dataCountPerAngle;
}
private double convertAngleIfNecessary(double estimatedPeak) {
double convertedAngleIfOver180 = estimatedPeak;
if (estimatedPeak > 180) {
convertedAngleIfOver180 = estimatedPeak - 360;
}
return convertedAngleIfOver180;
}
private SpeedWithConfidence<Void> estimatedSpeed(BoatClass boatClass, Speed windSpeed, double estimatedPeak)
throws NotEnoughDataHasBeenAddedException {
SpeedWithConfidence<Void> speed = polarDataService.getSpeed(boatClass, windSpeed, new DegreeBearingImpl(
estimatedPeak));
return speed;
}
private double estimatePeak(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive, Integer[] dataCountPerAngle)
throws NotEnoughDataHasBeenAddedException {
// Find peak by averaging the angles that have at least 50% of the max datacount
List<Integer> 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
// significance.
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++) {
Integer dataCount = dataCountPerAngle[i];
if (dataCount > maxDataCount / 2) {
sumOfAllUpperHalfAngles = sumOfAllUpperHalfAngles + i * dataCount;
numberOfAnglesAdded = numberOfAnglesAdded + dataCount;
}
}
double estimatedPeak = (double) sumOfAllUpperHalfAngles / (double) numberOfAnglesAdded;
return estimatedPeak;
}
}
@@ -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<TrackedRace> notCached;
private final Set<PolarFix> resultList;
public NoCacheEntryException(Set<TrackedRace> notCached, Set<PolarFix> resultList) {
this.notCached = notCached;
this.resultList = resultList;
}
public Set<TrackedRace> getNotCached() {
return notCached;
}
public Set<PolarFix> getCachedResultList() {
return resultList;
}
}
@@ -0,0 +1,67 @@
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;
public class PolarFixCache extends
SmartFutureCache<BoatClass, Map<RegattaAndRaceIdentifier, List<PolarFix>>, PolarFixCacheRaceInterval> {
private final List<PolarFixCacheUpdateDoneListener> cacheUpdateDoneListeners = new ArrayList<PolarFixCacheUpdateDoneListener>();
public PolarFixCache(Executor executor) {
super(new PolarFixCacheUpdater(executor), "polarFixCache");
}
@Override
protected void cache(BoatClass key, Map<RegattaAndRaceIdentifier, List<PolarFix>> 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);
}
}
}
public Set<PolarFix> getFixesForTrackedRaces(Set<TrackedRace> trackedRaces) throws NoCacheEntryException {
Set<PolarFix> resultList = new HashSet<PolarFix>();
Set<TrackedRace> notCached = new HashSet<TrackedRace>();
for (TrackedRace trackedRace : trackedRaces) {
Map<RegattaAndRaceIdentifier, List<PolarFix>> result = get(trackedRace.getRace().getBoatClass(), false);
List<PolarFix> resultForRace = result.get(trackedRace.getRaceIdentifier());
if (resultForRace == null) {
notCached.add(trackedRace);
} else {
resultList.addAll(resultForRace);
}
}
if (notCached.size() > 0) {
throw new NoCacheEntryException(notCached, resultList);
}
return resultList;
}
}
@@ -0,0 +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.tracking.TrackedRace;
import com.sap.sailing.polars.aggregation.PolarFixRaceInterval;
import com.sap.sailing.util.SmartFutureCache.UpdateInterval;
import com.sap.sse.common.Util.Pair;
public class PolarFixCacheRaceInterval implements UpdateInterval<PolarFixCacheRaceInterval>, PolarFixRaceInterval {
private final Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> competitorAndTimepointsForRace;
public PolarFixCacheRaceInterval(
Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> competitorAndTimepointsForRace) {
this.competitorAndTimepointsForRace = competitorAndTimepointsForRace;
}
@Override
public Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> getCompetitorAndTimepointsForRace() {
return competitorAndTimepointsForRace;
}
@Override
public PolarFixCacheRaceInterval join(PolarFixCacheRaceInterval otherUpdateInterval) {
Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> joinedInterval = new HashMap<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>>();
Set<TrackedRace> racesContainedInEitherMap = new HashSet<TrackedRace>();
for (TrackedRace race : competitorAndTimepointsForRace.keySet()) {
racesContainedInEitherMap.add(race);
}
Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> otherUpdateIntervalInExplicitForm = otherUpdateInterval
.getCompetitorAndTimepointsForRace();
for (TrackedRace race : otherUpdateIntervalInExplicitForm.keySet()) {
racesContainedInEitherMap.add(race);
}
for (TrackedRace race : racesContainedInEitherMap) {
HashMap<Competitor, Pair<TimePoint, TimePoint>> joinedMap = new HashMap<Competitor, Pair<TimePoint, TimePoint>>();
Map<Competitor, Pair<TimePoint, TimePoint>> mapOfTimpointCompetitorPairs = competitorAndTimepointsForRace
.get(race);
if (mapOfTimpointCompetitorPairs != null) {
joinedMap.putAll(mapOfTimpointCompetitorPairs);
}
Map<Competitor, Pair<TimePoint, TimePoint>> mapOfOtherTimpointCompetitorPairs = otherUpdateIntervalInExplicitForm
.get(race);
if (mapOfOtherTimpointCompetitorPairs != null) {
mergeMap(joinedMap, mapOfOtherTimpointCompetitorPairs);
}
joinedInterval.put(race, joinedMap);
}
return new PolarFixCacheRaceInterval(joinedInterval);
}
private void mergeMap(HashMap<Competitor, Pair<TimePoint, TimePoint>> joinedMap,
Map<Competitor, Pair<TimePoint, TimePoint>> mapOfOtherTimpointCompetitorPairs) {
for (Entry<Competitor, Pair<TimePoint, TimePoint>> otherEntry : mapOfOtherTimpointCompetitorPairs.entrySet()) {
Competitor competitor = otherEntry.getKey();
Pair<TimePoint, TimePoint> otherValue = otherEntry.getValue();
Pair<TimePoint, TimePoint> existingValue = joinedMap.get(competitor);
if (existingValue != null) {
Pair<TimePoint, TimePoint> joinedTimepointInterval = joinTimepointIntervals(existingValue, otherValue);
joinedMap.put(competitor, joinedTimepointInterval);
} else {
joinedMap.put(competitor, otherValue);
}
}
}
/**
* This method ignores a possible gap between the two intervals.
*
* @param existingValue
* @param otherValue
* @return
*/
private Pair<TimePoint, TimePoint> joinTimepointIntervals(Pair<TimePoint, TimePoint> existingValue,
Pair<TimePoint, TimePoint> otherValue) {
TimePoint startExisting = existingValue.getA();
TimePoint endExisting = existingValue.getB();
TimePoint startOther = otherValue.getA();
TimePoint endOther = otherValue.getB();
TimePoint startNew = startExisting.before(startOther) ? startExisting : startOther;
TimePoint endNew = endExisting.after(endOther) ? endExisting : endOther;
return new Pair<TimePoint, TimePoint>(startNew, endNew);
}
}
@@ -0,0 +1,9 @@
package com.sap.sailing.polars.caching;
import com.sap.sailing.domain.base.BoatClass;
public interface PolarFixCacheUpdateDoneListener {
void cacheUpdateDoneForBoatClass(BoatClass key);
}
@@ -0,0 +1,56 @@
package com.sap.sailing.polars.caching;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
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.polars.aggregation.PolarFixAggregator;
import com.sap.sailing.polars.data.PolarFix;
import com.sap.sailing.util.SmartFutureCache.CacheUpdater;
public class PolarFixCacheUpdater implements
CacheUpdater<BoatClass, Map<RegattaAndRaceIdentifier, List<PolarFix>>, PolarFixCacheRaceInterval> {
private final Executor executor;
public PolarFixCacheUpdater(Executor executor) {
this.executor = executor;
}
@Override
public Map<RegattaAndRaceIdentifier, List<PolarFix>> computeCacheUpdate(BoatClass key,
PolarFixCacheRaceInterval updateInterval) throws Exception {
PolarFixAggregator aggregator = new PolarFixAggregator(updateInterval,
PolarSheetGenerationSettingsImpl.createStandardPolarSettings(), executor);
Thread.sleep(updateInterval.getCompetitorAndTimepointsForRace().keySet().iterator().next()
.getMillisecondsOverWhichToAverageSpeed() / 2);
aggregator.startPolarFixAggregation();
return aggregator.get();
}
@Override
public Map<RegattaAndRaceIdentifier, List<PolarFix>> provideNewCacheValue(BoatClass key,
Map<RegattaAndRaceIdentifier, List<PolarFix>> oldCacheValue,
Map<RegattaAndRaceIdentifier, List<PolarFix>> computedCacheUpdate, PolarFixCacheRaceInterval updateInterval) {
Map<RegattaAndRaceIdentifier, List<PolarFix>> newCacheValue = new HashMap<RegattaAndRaceIdentifier, List<PolarFix>>();
if (oldCacheValue != null) {
newCacheValue.putAll(oldCacheValue);
}
for (Entry<RegattaAndRaceIdentifier, List<PolarFix>> newEntry : computedCacheUpdate.entrySet()) {
List<PolarFix> oldList = newCacheValue.get(newEntry.getKey());
if (oldList != null) {
oldList.addAll(newEntry.getValue());
} else {
newCacheValue.put(newEntry.getKey(), newEntry.getValue());
}
}
return newCacheValue;
}
}
@@ -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<BoatClass, PolarSheetsData, com.sap.sailing.util.SmartFutureCache.EmptyUpdateInterval>
implements PolarFixCacheUpdateDoneListener {
public PolarSheetPerBoatClassCache(PolarDataService polarDataService) {
super(new PolarSheetPerBoatClassCacheUpdater(polarDataService), "PolarSheetPerBoatClassCache");
}
@Override
public void cacheUpdateDoneForBoatClass(BoatClass key) {
triggerUpdate(key, new EmptyUpdateInterval());
}
}
@@ -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<BoatClass, PolarSheetsData, EmptyUpdateInterval> {
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;
}
}
@@ -0,0 +1,83 @@
package com.sap.sailing.polars.clusters;
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.ClusterWithLowerAndUpperBoundaries;
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 SpeedClusterGroup extends FixClusterGroup<Speed> {
/**
* 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->
*/
public SpeedClusterGroup(String name, double[] levelMidsInKnots, double maxDistanceInKnots) {
super(name, createClustersForLevelMids(levelMidsInKnots, maxDistanceInKnots));
}
private static Collection<Cluster<Speed>> createClustersForLevelMids(double[] levelMidsInKnots,
double maxDistanceInKnots) {
ArrayList<Cluster<Speed>> clusterList = new ArrayList<Cluster<Speed>>();
for (int index = 0; index < levelMidsInKnots.length; index++) {
ClusterBoundary<Speed> lowerBoundary = createLowerBoundary(levelMidsInKnots, maxDistanceInKnots, index);
ClusterBoundary<Speed> upperBoundary = createUpperBoundary(levelMidsInKnots, maxDistanceInKnots, index);
Cluster<Speed> cluster = new ClusterWithLowerAndUpperBoundaries<Speed>(levelMidsInKnots[index] + "kn", lowerBoundary,
upperBoundary);
clusterList.add(cluster);
}
return clusterList;
}
private static ClusterBoundary<Speed> createUpperBoundary(double[] levelMidsInKnots, double maxDistanceInKnots,
int index) {
ClusterBoundary<Speed> 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<Speed>(new KnotSpeedImpl(upperBoundaryValue),
ComparisonStrategy.LOWER_THAN);
return upperBoundary;
}
private static ClusterBoundary<Speed> createLowerBoundary(double[] levelMidsInKnots, double maxDistanceInKnots,
int index) {
ClusterBoundary<Speed> 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<Speed>(new KnotSpeedImpl(lowerBoundaryValue),
ComparisonStrategy.GREATER_EQUALS_THAN);
return lowerBoundary;
}
}
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.polarsheets;
package com.sap.sailing.polars.data;
import com.sap.sailing.domain.common.Speed;
@@ -0,0 +1,11 @@
package com.sap.sailing.polars.data;
public interface DataPointWithOriginInfo extends Comparable<DataPointWithOriginInfo> {
public abstract Double getRawData();
public abstract String getWindGaugeIdString();
public abstract String getDayString();
}
@@ -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();
}
@@ -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 {
@@ -1,6 +1,8 @@
package com.sap.sailing.domain.polarsheets;
package com.sap.sailing.polars.data.impl;
public class DataPointWithOriginInfo implements Comparable<DataPointWithOriginInfo>{
import com.sap.sailing.polars.data.DataPointWithOriginInfo;
public class DataPointWithOriginInfoImpl implements DataPointWithOriginInfo {
private Double rawData;
@@ -8,20 +10,23 @@ public class DataPointWithOriginInfo implements Comparable<DataPointWithOriginIn
private String dayString;
public DataPointWithOriginInfo(Double rawData, String windGaugeIdString, String dayString) {
public DataPointWithOriginInfoImpl(Double rawData, String windGaugeIdString, String dayString) {
this.rawData = rawData;
this.windGaugeIdString = windGaugeIdString;
this.dayString = dayString;
}
@Override
public Double getRawData() {
return rawData;
}
@Override
public String getWindGaugeIdString() {
return windGaugeIdString;
}
@Override
public String getDayString() {
return dayString;
}
@@ -1,4 +1,4 @@
package com.sap.sailing.domain.polarsheets;
package com.sap.sailing.polars.data.impl;
import java.text.SimpleDateFormat;
import java.util.HashSet;
@@ -17,8 +17,9 @@ 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.polars.data.PolarFix;
public class PolarFix {
public class PolarFixImpl implements PolarFix {
private SpeedWithBearing boatSpeed;
private Speed windSpeed;
@@ -26,7 +27,7 @@ public class PolarFix {
private String gaugeIdString;
private String dayString;
public PolarFix(GPSFixMoving fix, TrackedRace race, GPSFixTrack<Competitor, GPSFixMoving> track, Wind windSpeed,
public PolarFixImpl(GPSFixMoving fix, TrackedRace race, GPSFixTrack<Competitor, GPSFixMoving> 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;
}
@@ -0,0 +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(Executor executor) {
PolarDataServiceFactory factory = new PolarDataServiceImplFactory();
return factory.createPolarDataService(executor);
}
protected abstract PolarDataService createPolarDataService(Executor executor);
}
@@ -0,0 +1,16 @@
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;
public class PolarDataServiceImplFactory extends PolarDataServiceFactory {
@Override
public PolarDataService createPolarDataService(Executor executor) {
return new PolarDataServiceImpl(executor);
}
}
@@ -1,387 +1,277 @@
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<PolarSheetsData>{
private final Set<PerRaceAndCompetitorPolarSheetGenerationWorker> workers;
private final List<List<BoatAndWindSpeedWithOriginInfo>> 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<TrackedRace> trackedRaces, PolarSheetGenerationSettings settings,
Executor executor) {
this.settings = settings;
stepping = settings.getWindStepping();;
polarData = initializePolarDataContainer();
this.executor = executor;
workers = new HashSet<PerRaceAndCompetitorPolarSheetGenerationWorker>();
for (TrackedRace race : trackedRaces) {
TimePoint startTime = race.getStartOfRace();
TimePoint endTime = race.getEndOfRace();
if (endTime == null) {
endTime = race.getTimePointOfNewestEvent();
}
RaceDefinition raceDefinition = race.getRace();
Iterable<Competitor> competitors = raceDefinition.getCompetitors();
for (Competitor competitor : competitors) {
PerRaceAndCompetitorPolarSheetGenerationWorker task = new PerRaceAndCompetitorPolarSheetGenerationWorker(
race, this, startTime, endTime, competitor, settings);
workers.add(task);
}
}
}
private List<List<BoatAndWindSpeedWithOriginInfo>> initializePolarDataContainer() {
List<List<BoatAndWindSpeedWithOriginInfo>> container = new ArrayList<List<BoatAndWindSpeedWithOriginInfo>>();
for (int i = 0; i < 360; i++) {
container.add(new ArrayList<BoatAndWindSpeedWithOriginInfo>());
}
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<Integer, Integer[]> dataCountPerAngleForWindspeed = new HashMap<Integer, Integer[]>();
Map<Integer, Map<Integer, PolarSheetsHistogramData>> histogramDataMap = new HashMap<Integer, Map<Integer, PolarSheetsHistogramData>>();
Map<Integer, Integer> totalDataCountPerWindSpeed = new HashMap<Integer, Integer>();
for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) {
dataCountPerAngleForWindspeed.put(levelIndex, new Integer[360]);
histogramDataMap.put(levelIndex, new HashMap<Integer, PolarSheetsHistogramData>());
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<Integer, List<DataPointWithOriginInfo>> dataSetForWindLevel = new HashMap<Integer, List<DataPointWithOriginInfo>>();
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<Integer, List<DataPointWithOriginInfo>> 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<DataPointWithOriginInfo>());
}
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<Integer, Integer[]> dataCountPerAngleForWindspeed,
Map<Integer, Map<Integer, PolarSheetsHistogramData>> histogramDataMap,
Map<Integer, Integer> totalDataCountPerWindSpeed, int angleIndex, double[] sumsPerWindSpeed,
int[] dataCountPerWindSpeed, Map<Integer, List<DataPointWithOriginInfo>> dataSetForWindLevel) {
for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) {
if (settings.shouldRemoveOutliers() && dataSetForWindLevel.get(levelIndex) != null) {
List<DataPointWithOriginInfo> withoutOutliers = new ArrayList<DataPointWithOriginInfo>();
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<DataPointWithOriginInfo>());
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<DataPointWithOriginInfo> 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<Integer, List<DataPointWithOriginInfo>> dataSetForWindLevel, int levelIndex,
List<DataPointWithOriginInfo> 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<Integer, List<DataPointWithOriginInfo>> 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<List<BoatAndWindSpeedWithOriginInfo>> 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<List<BoatAndWindSpeedWithOriginInfo>> polarData;
private WindSteppingWithMaxDistance stepping;
private PolarSheetGenerationSettings settings;
private final Set<PolarFix> fixes;
public PolarSheetGenerator(Set<PolarFix> fixes, PolarSheetGenerationSettings settings) {
this.settings = settings;
this.fixes = fixes;
stepping = settings.getWindStepping();
polarData = initializePolarDataContainer();
}
private List<List<BoatAndWindSpeedWithOriginInfo>> initializePolarDataContainer() {
List<List<BoatAndWindSpeedWithOriginInfo>> container = new ArrayList<List<BoatAndWindSpeedWithOriginInfo>>();
for (int i = 0; i < 360; i++) {
container.add(new ArrayList<BoatAndWindSpeedWithOriginInfo>());
}
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;
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 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<Integer, Integer[]> dataCountPerAngleForWindspeed = new HashMap<Integer, Integer[]>();
Map<Integer, Map<Integer, PolarSheetsHistogramData>> histogramDataMap = new HashMap<Integer, Map<Integer, PolarSheetsHistogramData>>();
Map<Integer, Integer> totalDataCountPerWindSpeed = new HashMap<Integer, Integer>();
for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) {
dataCountPerAngleForWindspeed.put(levelIndex, new Integer[360]);
histogramDataMap.put(levelIndex, new HashMap<Integer, PolarSheetsHistogramData>());
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<Integer, List<DataPointWithOriginInfo>> dataSetForWindLevel = new HashMap<Integer, List<DataPointWithOriginInfo>>();
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<Integer, List<DataPointWithOriginInfo>> 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<DataPointWithOriginInfo>());
}
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<Integer, Integer[]> dataCountPerAngleForWindspeed,
Map<Integer, Map<Integer, PolarSheetsHistogramData>> histogramDataMap,
Map<Integer, Integer> totalDataCountPerWindSpeed, int angleIndex, double[] sumsPerWindSpeed,
int[] dataCountPerWindSpeed, Map<Integer, List<DataPointWithOriginInfo>> dataSetForWindLevel) {
for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) {
if (settings.shouldRemoveOutliers() && dataSetForWindLevel.get(levelIndex) != null) {
List<DataPointWithOriginInfo> withoutOutliers = new ArrayList<DataPointWithOriginInfo>();
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<DataPointWithOriginInfo>());
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<DataPointWithOriginInfo> 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<Integer, List<DataPointWithOriginInfo>> dataSetForWindLevel, int levelIndex,
List<DataPointWithOriginInfo> 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<Integer, List<DataPointWithOriginInfo>> 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<List<BoatAndWindSpeedWithOriginInfo>> getCompleteData() {
return polarData;
}
public WindSteppingWithMaxDistance getStepping() {
return stepping;
}
}
@@ -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<DataPointWithOriginInfo> dataPointsWithOriginInfo, int angleIndex,
public PolarSheetsHistogramData build(List<DataPointWithOriginInfo> 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;
}
@@ -0,0 +1,173 @@
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;
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.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;
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.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;
/**
* 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;
private final PolarSheetPerBoatClassCache polarSheetPerBoatClassCache;
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
public SpeedWithConfidence<Void> getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind)
throws NotEnoughDataHasBeenAddedException {
return polarDataMiner.estimateBoatSpeed(boatClass, windSpeed, bearingToTheWind);
}
@Override
public SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed,
LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException {
SpeedWithBearingWithConfidence<Void> speedWithBearing = null;
if (tack.equals(Tack.STARBOARD)) {
if (legType.equals(LegType.UPWIND)) {
speedWithBearing = polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed);
} else if (legType.equals(LegType.DOWNWIND)) {
speedWithBearing = polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed);
}
} else if (tack.equals(Tack.PORT)) {
if (legType.equals(LegType.UPWIND)) {
return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed);
} else if (legType.equals(LegType.DOWNWIND)) {
return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed);
}
}
return speedWithBearing;
}
@Override
public PolarSheetsData generatePolarSheet(Set<TrackedRace> trackedRaces, PolarSheetGenerationSettings settings,
Executor executor) throws InterruptedException, ExecutionException {
Set<PolarFix> 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();
// }
PolarSheetGenerator generator = new PolarSheetGenerator(fixes, settings);
return generator.generate();
}
@Override
public void newRaceFinishedTracking(TrackedRace trackedRace) {
// TODO add a similar listening method, that deletes all cached data on removal of race
// HashSet<TrackedRace> set = new HashSet<TrackedRace>();
// set.add(trackedRace);
// polarFixCache.triggerUpdate(trackedRace.getRace().getBoatClass(), new PolarFixCacheRaceInterval(set));
}
@Override
public Set<PolarFix> getPolarFixesForBoatClass(BoatClass key) {
Map<RegattaAndRaceIdentifier, List<PolarFix>> fixesInMap = polarFixCache.get(key, false);
Set<PolarFix> resultSet = new HashSet<PolarFix>();
for (List<PolarFix> value : fixesInMap.values()) {
resultSet.addAll(value);
}
return resultSet;
}
@Override
public PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass) {
return polarDataMiner.createFullSheetForBoatClass(boatClass);
//return polarSheetPerBoatClassCache.get(boatClass, false);
}
@Override
public Set<BoatClass> getAllBoatClassesWithPolarSheetsAvailable() {
return polarDataMiner.getAvailableBoatClasses();
//return polarSheetPerBoatClassCache.keySet();
}
@Override
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(
// createdTrackedRace, competitor, timePoint));
// polarFixCache.triggerUpdate(createdTrackedRace.getRace().getBoatClass(), interval);
}
@Override
public Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) {
return polarDataMiner.getDataCountsForWindSpeed(boatClass, windSpeed, startAngleInclusive, endAngleExclusive);
}
// private Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> createIntervalSpecification(
// TrackedRace createdTrackedRace, Competitor competitor, TimePoint timePoint) {
// HashMap<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> result = new HashMap<TrackedRace,
// Map<Competitor, Pair<TimePoint, TimePoint>>>();
// HashMap<Competitor, Pair<TimePoint, TimePoint>> competitorMap = new HashMap<Competitor, Pair<TimePoint,
// TimePoint>>();
// competitorMap.put(competitor, new Pair<TimePoint, TimePoint>(timePoint, timePoint));
// result.put(createdTrackedRace, competitorMap);
// return result;
// }
}
@@ -0,0 +1,36 @@
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<InputType, ResultType> extends
AbstractSimpleParallelProcessor<InputType, ResultType> {
public AbstractEnrichingProcessor(Class<InputType> inputType, Class<ResultType> resultType,
ExecutorService executor, Collection<Processor<ResultType, ?>> resultReceivers) {
super(inputType, resultType, executor, resultReceivers);
}
@Override
protected Callable<ResultType> createInstruction(final InputType element) {
return new Callable<ResultType>() {
@Override
public ResultType call() throws Exception {
return enrich(element);
}
};
}
protected abstract ResultType enrich(InputType element);
@Override
protected void setAdditionalData(AdditionalResultDataBuilder additionalDataBuilder) {
}
}
@@ -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;
}
}
@@ -0,0 +1,150 @@
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.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.WindWithConfidence;
import com.sap.sse.common.Util.Pair;
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 ClusterGroup<Speed> windSpeedClusterGroup;
private final Set<WindSource> windSourcesToExcludeForBearing;
private final Set<WindSource> windSourcesToExcludeForSpeed;
public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor,
ClusterGroup<Speed> windSpeedClusterGroup) {
this.fix = fix;
this.race = race;
this.competitor = competitor;
this.windSpeedClusterGroup = windSpeedClusterGroup;
this.windSourcesToExcludeForBearing = collectWindSourcesToIgnoreForBearing();
this.windSourcesToExcludeForSpeed = collectWindSourcesToIgnoreForSpeed();
}
public GPSFixMoving getFix() {
return fix;
}
public TrackedRace getRace() {
return race;
}
public Competitor getCompetitor() {
return competitor;
}
@Override
public RoundedAngleToTheWind getRoundedAngleToTheWind() {
return new RoundedAngleToTheWind(getAngleToTheWind().getObject());
}
public BearingWithConfidence<Integer> getAngleToTheWind() {
SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint());
WindWithConfidence<Pair<Position, TimePoint>> wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForBearing);
BearingWithConfidenceImpl<Integer> result = null;
if (wind != null && boatSpeed != null) {
Bearing bearing = boatSpeed.getBearing();
Bearing difference = wind.getObject().getFrom().getDifferenceTo(bearing);
result = new BearingWithConfidenceImpl<Integer>(difference, wind.getConfidence(), 0);
}
return result;
}
@Override
public Cluster<Speed> getWindSpeedCluster() {
Speed windSpeed = getWindSpeed().getObject();
return windSpeedClusterGroup.getClusterFor(windSpeed);
}
public WindWithConfidence<Pair<Position, TimePoint>> getWindSpeed() {
WindWithConfidence<Pair<Position, TimePoint>> wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForSpeed);
return wind;
}
public SpeedWithBearingWithConfidence<TimePoint> getBoatSpeed() {
GPSFixTrack<Competitor, GPSFixMoving> 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
public BoatClass getBoatClass() {
return race.getRace().getBoatClass();
}
private Set<WindSource> collectWindSourcesToIgnoreForBearing() {
Set<WindSource> windSourcesToExclude = new HashSet<WindSource>();
Iterable<WindSource> combinedSources = race.getWindSources(WindSourceType.COMBINED);
for (WindSource combinedSource : combinedSources) {
windSourcesToExclude.add(combinedSource);
}
Iterable<WindSource> courseSources = race.getWindSources(WindSourceType.COURSE_BASED);
for (WindSource courseSource : courseSources) {
windSourcesToExclude.add(courseSource);
}
Iterable<WindSource> expSources = race.getWindSources(WindSourceType.EXPEDITION);
for (WindSource expSource : expSources) {
windSourcesToExclude.add(expSource);
}
Iterable<WindSource> rcSources = race.getWindSources(WindSourceType.RACECOMMITTEE);
for (WindSource rcSource : rcSources) {
windSourcesToExclude.add(rcSource);
}
Iterable<WindSource> webSources = race.getWindSources(WindSourceType.WEB);
for (WindSource webSource : webSources) {
windSourcesToExclude.add(webSource);
}
return windSourcesToExclude;
}
private Set<WindSource> collectWindSourcesToIgnoreForSpeed() {
Set<WindSource> windSourcesToExclude = new HashSet<WindSource>();
Iterable<WindSource> combinedSources = race.getWindSources(WindSourceType.COMBINED);
for (WindSource combinedSource : combinedSources) {
windSourcesToExclude.add(combinedSource);
}
Iterable<WindSource> courseSources = race.getWindSources(WindSourceType.COURSE_BASED);
for (WindSource courseSource : courseSources) {
windSourcesToExclude.add(courseSource);
}
Iterable<WindSource> trackBasedSources = race.getWindSources(WindSourceType.TRACK_BASED_ESTIMATION);
for (WindSource trackBasedSource : trackBasedSources) {
windSourcesToExclude.add(trackBasedSource);
}
Iterable<WindSource> rcSources = race.getWindSources(WindSourceType.RACECOMMITTEE);
for (WindSource rcSource : rcSources) {
windSourcesToExclude.add(rcSource);
}
Iterable<WindSource> webSources = race.getWindSources(WindSourceType.WEB);
for (WindSource webSource : webSources) {
windSourcesToExclude.add(webSource);
}
return windSourcesToExclude;
}
}
@@ -0,0 +1,154 @@
package com.sap.sailing.polars.mining;
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.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;
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;
public class IncrementalRegressionProcessor implements Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>,Void> {
private static final Logger logger = Logger.getLogger(IncrementalRegressionProcessor.class.getName());
private final Map<GroupKey, BoatSpeedEstimator> boatSpeedEstimators = new HashMap<GroupKey, BoatSpeedEstimator>();
private final ClusterGroup<Speed> speedClusterGroup;
private final Set<BoatClass> availableBoatClasses = new HashSet<>();
public IncrementalRegressionProcessor(ClusterGroup<Speed> speedClusterGroup) {
this.speedClusterGroup = speedClusterGroup;
}
@Override
public void processElement(GroupedDataEntry<GPSFixMovingWithPolarContext> element) {
GroupKey key = element.getKey();
BoatSpeedEstimator boatSpeedEstimator;
synchronized (boatSpeedEstimators) {
if (!boatSpeedEstimators.containsKey(key)) {
boatSpeedEstimators.put(key, new BoatSpeedEstimator());
availableBoatClasses.add(element.getDataEntry().getBoatClass());
}
boatSpeedEstimator = boatSpeedEstimators.get(key);
}
GPSFixMovingWithPolarContext fix = element.getDataEntry();
BearingWithConfidence<Integer> angleToTheWind = fix.getAngleToTheWind();
if (angleToTheWind != null) {
WindWithConfidence<Pair<Position, TimePoint>> windWithConfidenceForSpeed = fix.getWindSpeed();
SpeedWithBearingWithConfidence<TimePoint> 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);
}
}
@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;
}
public Pair<SpeedWithConfidence<Void>, Integer> estimateBoatSpeed(final BoatClass boatClass, final Speed windSpeed,
final Bearing angleToTheWind)
throws NotEnoughDataHasBeenAddedException {
int dataCount = 0;
PolarClusterKey key = new PolarClusterKey() {
@Override
public RoundedAngleToTheWind getRoundedAngleToTheWind() {
return new RoundedAngleToTheWind(angleToTheWind);
}
@Override
public BoatClass getBoatClass() {
return boatClass;
}
@Override
public Cluster<Speed> getWindSpeedCluster() {
return speedClusterGroup.getClusterFor(windSpeed);
}
};
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();
}
KnotSpeedImpl speedWithoutConfidence = new KnotSpeedImpl(boatSpeedEstimator.estimateSpeed(windSpeed.getKnots(),
angleToTheWind.getDegrees()));
dataCount = boatSpeedEstimator.getDataCount();
double confidence = boatSpeedEstimator.getConfidence();
return new Pair<SpeedWithConfidence<Void>, Integer>(new SpeedWithConfidenceImpl<Void>(
speedWithoutConfidence, confidence, null), dataCount);
}
@Override
public void onFailure(Throwable failure) {
logger.severe("Polar Data Mining Pipe failed.");
throw new RuntimeException("Polar Data Miner failed.", failure);
}
public Set<BoatClass> getAvailableBoatClasses() {
return availableBoatClasses;
}
@Override
public Class<GroupedDataEntry<GPSFixMovingWithPolarContext>> getInputType() {
// TODO Auto-generated method stub
return null;
}
@Override
public Class<Void> 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;
}
}
@@ -0,0 +1,27 @@
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 {
/**
*
* @return degrees boat to windDirection in DEG
*/
@Dimension(messageKey = "angleToWind")
RoundedAngleToTheWind getRoundedAngleToTheWind();
/**
*
* @return wind speed cluster
*/
@Dimension(messageKey = "windSpeed")
Cluster<Speed> getWindSpeedCluster();
@Dimension(messageKey = "boatClass")
BoatClass getBoatClass();
}
@@ -0,0 +1,29 @@
package com.sap.sailing.polars.mining;
import java.util.ArrayList;
import java.util.Collection;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sse.datamining.factories.FunctionFactory;
import com.sap.sse.datamining.functions.Function;
public class PolarDataDimensionCollectionFactory {
public static Collection<Function<?>> getClusterKeyDimensions() throws NoSuchMethodException {
Collection<Function<?>> dimensions = new ArrayList<>();
FunctionFactory functionFactory = new FunctionFactory();
Function<RoundedAngleToTheWind> angleFunction = functionFactory
.createMethodWrappingFunction(PolarClusterKey.class.getMethod("getRoundedAngleToTheWind",
new Class<?>[0]));
Function<WindSpeedLevel> windSpeedFunction = functionFactory.createMethodWrappingFunction(PolarClusterKey.class
.getMethod("getWindSpeedCluster", new Class<?>[0]));
Function<BoatClass> boatClassFunction = functionFactory.createMethodWrappingFunction(PolarClusterKey.class
.getMethod("getBoatClass", new Class<?>[0]));
dimensions.add(boatClassFunction);
dimensions.add(angleFunction);
dimensions.add(windSpeedFunction);
return dimensions;
}
}
@@ -0,0 +1,220 @@
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.Map;
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.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;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sse.common.Util.Pair;
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;
import com.sap.sse.datamining.impl.components.ParallelMultiDimensionsValueNestingGroupingProcessor;
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,
new LinkedBlockingQueue<Runnable>());
}
private AbstractEnrichingProcessor<GPSFixMovingWithOriginInfo, GPSFixMovingWithPolarContext> enrichingProcessor;
private IncrementalRegressionProcessor incrementalRegressionProcessor;
public PolarDataMiner() {
defaultPolarSheetGenerationSettings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings();
try {
setUpWorkflow();
} catch (ClassCastException | NoSuchMethodException | SecurityException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
private void setUpWorkflow() throws ClassCastException, NoSuchMethodException,
SecurityException {
WindSteppingWithMaxDistance stepping = defaultPolarSheetGenerationSettings.getWindStepping();
final ClusterGroup<Speed> speedClusterGroup = SpeedClusterGroupFromWindSteppingCreator
.createSpeedClusterGroupFrom(stepping);
incrementalRegressionProcessor = new IncrementalRegressionProcessor(speedClusterGroup);
Collection<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>> grouperResultReceivers = new ArrayList<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, ?>>();
grouperResultReceivers.add(incrementalRegressionProcessor);
Collection<Function<?>> dimensions = PolarDataDimensionCollectionFactory.getClusterKeyDimensions();
Processor<GPSFixMovingWithPolarContext, GroupedDataEntry<GPSFixMovingWithPolarContext>> groupingProcessor = new ParallelMultiDimensionsValueNestingGroupingProcessor<GPSFixMovingWithPolarContext>(
GPSFixMovingWithPolarContext.class, executor, grouperResultReceivers, dimensions);
Collection<Processor<GPSFixMovingWithPolarContext, ?>> filteringResultReceivers = Arrays.asList(groupingProcessor);
Processor<GPSFixMovingWithPolarContext, GPSFixMovingWithPolarContext> filteringProcessor = new ParallelFilteringProcessor<GPSFixMovingWithPolarContext>(
GPSFixMovingWithPolarContext.class, executor, filteringResultReceivers, new PolarFixFilterCriteria());
Collection<Processor<GPSFixMovingWithPolarContext, ?>> enrichingResultReceivers = Arrays
.asList(filteringProcessor);
enrichingProcessor = new AbstractEnrichingProcessor<GPSFixMovingWithOriginInfo, GPSFixMovingWithPolarContext>(
GPSFixMovingWithOriginInfo.class, GPSFixMovingWithPolarContext.class, executor, enrichingResultReceivers) {
@Override
protected GPSFixMovingWithPolarContext enrich(GPSFixMovingWithOriginInfo element) {
return new GPSFixMovingWithPolarContext(element.getFix(), element.getTrackedRace(), element.getCompetitor(),
speedClusterGroup);
}
};
}
public void addFix(GPSFixMoving fix, Competitor competitor, TrackedRace trackedRace) {
enrichingProcessor.processElement(new GPSFixMovingWithOriginInfo(fix, trackedRace,
competitor));
}
public boolean isCurrentlyActiveAndOrHasQueue() {
boolean isActive = executor.getActiveCount() > 0;
boolean hasQueue = executor.getQueue().size() > 0;
return isActive || hasQueue;
}
public SpeedWithConfidence<Void> estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind)
throws NotEnoughDataHasBeenAddedException {
return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind).getA();
}
public PolarSheetsData createFullSheetForBoatClass(BoatClass boatClass) {
Integer[] defaultWindSpeeds = defaultPolarSheetGenerationSettings.getWindStepping().getRawStepping();
Number[][] averagedPolarDataByWindSpeed = new Number[defaultWindSpeeds.length][360];
Map<Integer, Integer[]> dataCountPerAngleForWindspeed = new HashMap<>();
Map<Integer, Map<Integer, PolarSheetsHistogramData>> histogramDataMap = new HashMap<>();
int totalDataCount = 0;
for (int windIndex = 0; windIndex < defaultWindSpeeds.length; windIndex++) {
Integer windSpeed = defaultWindSpeeds[windIndex];
Integer[] perAngle = new Integer[360];
Map<Integer, PolarSheetsHistogramData> perWindSpeed = new HashMap<>();
for (int angle = 0; angle < 360; angle++) {
Pair<SpeedWithConfidence<Void>, Integer> speedWithConfidenceAndDataCount;
try {
int convertedAngle = convertAngleIfNecessary(angle);
speedWithConfidenceAndDataCount = incrementalRegressionProcessor.estimateBoatSpeed(boatClass,
new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle));
} catch (NotEnoughDataHasBeenAddedException e) {
// No data so put in a 0 speed with 0 confidence
speedWithConfidenceAndDataCount = new Pair<SpeedWithConfidence<Void>, Integer>(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(0), 0, null), 0);
}
averagedPolarDataByWindSpeed[windIndex][angle] = speedWithConfidenceAndDataCount.getA().getObject().getKnots();
int dataCount = speedWithConfidenceAndDataCount.getB();
totalDataCount = totalDataCount + dataCount;
//FIXME hard coded
double coefficiantOfVariation = 0.8;
double confidenceMeasure = 0.5;
PolarSheetsHistogramDataImpl polarSheetsHistogramDataImpl = createEmptyHistogramData(perAngle, angle,
dataCount, coefficiantOfVariation, confidenceMeasure);
perWindSpeed.put(angle, polarSheetsHistogramDataImpl);
}
histogramDataMap.put(windIndex, perWindSpeed);
dataCountPerAngleForWindspeed.put(windIndex, perAngle);
}
PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, totalDataCount,
dataCountPerAngleForWindspeed, defaultPolarSheetGenerationSettings.getWindStepping(), histogramDataMap);
return data;
}
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;
Number[] xValues = {};
Number[] yValues = {};;
Map<String, Integer[]> yValuesByGaugeIds = new HashMap<>();
Map<String, Integer[]> yValuesByDay = new HashMap<>();
Map<String, Integer[]> yValuesByDayAndGaugeId = new HashMap<>();
PolarSheetsHistogramDataImpl polarSheetsHistogramDataImpl = new PolarSheetsHistogramDataImpl(angle, xValues, yValues, yValuesByGaugeIds,
yValuesByDay, yValuesByDayAndGaugeId, dataCount, coefficiantOfVariation);
polarSheetsHistogramDataImpl.setConfidenceMeasure(confidenceMeasure);
return polarSheetsHistogramDataImpl;
}
public Set<BoatClass> getAvailableBoatClasses() {
return incrementalRegressionProcessor.getAvailableBoatClasses();
}
public Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive) {
Integer[] dataCounts = new Integer[360];
for (int angle = 0; angle < 360; angle++) {
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;
}
}
@@ -0,0 +1,88 @@
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;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.datamining.components.FilterCriterion;
public class PolarFixFilterCriteria implements FilterCriterion<GPSFixMovingWithPolarContext> {
@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<Competitor, GPSFixMoving> 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();
Course course = race.getRace().getCourse();
if (course.getLastWaypoint() != course.getFirstWaypoint()) {
MarkPassing finishPassing = race.getMarkPassing(competitor, course.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;
}
@Override
public Class<GPSFixMovingWithPolarContext> getElementType() {
return GPSFixMovingWithPolarContext.class;
}
}
@@ -0,0 +1,40 @@
package com.sap.sailing.polars.mining;
import com.sap.sailing.domain.common.Bearing;
public class RoundedAngleToTheWind {
private final int angle;
public RoundedAngleToTheWind(Bearing angleToTheWind) {
this.angle = (int) Math.round(angleToTheWind.getDegrees());
}
@Override
public String toString() {
return "" + 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;
}
}
@@ -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<Speed> 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);
}
}
@@ -0,0 +1,52 @@
package com.sap.sailing.polars.mining;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.WindStepping;
public class WindSpeedLevel {
private final int level;
private final WindStepping stepping;
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;
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;
}
}
@@ -0,0 +1,64 @@
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);
private double confidenceSum = 0;
private int dataCount = 0;
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, 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 dataCount;
}
public double getConfidence() {
return confidenceSum / dataCount;
}
}
@@ -0,0 +1,15 @@
package com.sap.sailing.polars.regression;
public interface IncrementalLinearRegressionProcessor {
double getEstimatedY(double x) throws NotEnoughDataHasBeenAddedException;
void addMeasuredPoint(double x, double y);
double getSlope();
double getIntercept();
int getDataCount();
}
@@ -0,0 +1,15 @@
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.");
}
public NotEnoughDataHasBeenAddedException(String string) {
super(string);
}
}
@@ -0,0 +1,122 @@
package com.sap.sailing.polars.regression.impl;
import com.sap.sailing.polars.regression.IncrementalLinearRegressionProcessor;
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)
*
* The algorithm and variable naming is similar to the <a
* href="https://de.wikipedia.org/wiki/Methode_der_kleinsten_Quadrate#Lineare_Modellfunktion">german wikipedia
* article</a>
*
* 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 getEstimatedY(double x) throws NotEnoughDataHasBeenAddedException {
if (numberOfPointsAdded < 1) {
throw new NotEnoughDataHasBeenAddedException();
}
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
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;
}
@Override
public double getSlope() {
return alpha1;
}
@Override
public double getIntercept() {
return alpha0;
}
@Override
public int getDataCount() {
return numberOfPointsAdded;
}
}
@@ -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<SpeedWithConfidence<Integer>> {
@Override
public SpeedWithConfidence<Integer> 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<Integer>(speed, confidence, relativeToValue);
}
}
@@ -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<SpeedWithConfidence<Integer>> {
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<Integer> 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;
}
}
@@ -1,48 +1,50 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Sailing Server Gateway
Bundle-SymbolicName: com.sap.sailing.server.gateway
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.sap.sailing.server,
com.sap.sailing.server.operationaltransformation,
com.sap.sailing.server.racelog.tracking,
javax.ws.rs;version="1.1.1",
javax.ws.rs.core;version="1.1.1",
javax.ws.rs.ext;version="1.1.1",
org.apache.commons.lang,
org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.3.1"
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.expeditionconnector,
com.sap.sailing.declination,
com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.persistence,
com.sap.sse.mongodb,
com.mongodb.driver;bundle-version="2.6.2",
javax.servlet;bundle-version="2.6.0",
com.sap.sailing.domain.common,
com.sap.sailing.udpconnector,
org.jdom;bundle-version="1.0.0",
com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0",
com.sap.sailing.domain.shared.android;bundle-version="1.0.0",
com.sap.sailing.server.trackfiles;bundle-version="1.0.0",
org.eclipse.jetty.websocket.api;bundle-version="9.0.4",
org.eclipse.jetty.websocket.server;bundle-version="9.0.4",
org.eclipse.jetty.websocket.servlet;bundle-version="9.0.4",
org.eclipse.jetty.websocket.client;bundle-version="9.0.4",
org.eclipse.jetty.websocket.common;bundle-version="9.0.4",
javax.validation;bundle-version="1.0.0",
com.sun.jersey;bundle-version="1.17.0",
org.objectweb.asm;bundle-version="3.3.1",
org.apache.commons.fileupload;bundle-version="1.2.2",
com.sap.sailing.domain.igtimiadapter;bundle-version="1.0.0",
com.sap.sse.common
Bundle-ClassPath: .
Web-ContextPath: /sailingserver
Export-Package: com.sap.sailing.server.gateway,
com.sap.sailing.server.gateway.jaxrs,
com.sap.sailing.server.gateway.jaxrs.spi
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Sailing Server Gateway
Bundle-SymbolicName: com.sap.sailing.server.gateway
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.sap.sailing.polars,
com.sap.sailing.polars.regression,
com.sap.sailing.server,
com.sap.sailing.server.operationaltransformation,
com.sap.sailing.server.racelog.tracking,
javax.ws.rs;version="1.1.1",
javax.ws.rs.core;version="1.1.1",
javax.ws.rs.ext;version="1.1.1",
org.apache.commons.lang,
org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.3.1"
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.expeditionconnector,
com.sap.sailing.declination,
com.sap.sailing.domain.tractracadapter,
com.sap.sailing.domain.persistence,
com.sap.sse.mongodb,
com.mongodb.driver;bundle-version="2.6.2",
javax.servlet;bundle-version="2.6.0",
com.sap.sailing.domain.common,
com.sap.sailing.udpconnector,
org.jdom;bundle-version="1.0.0",
com.sap.sailing.server.gateway.serialization;bundle-version="1.0.0",
com.sap.sailing.domain.shared.android;bundle-version="1.0.0",
com.sap.sailing.server.trackfiles;bundle-version="1.0.0",
org.eclipse.jetty.websocket.api;bundle-version="9.0.4",
org.eclipse.jetty.websocket.server;bundle-version="9.0.4",
org.eclipse.jetty.websocket.servlet;bundle-version="9.0.4",
org.eclipse.jetty.websocket.client;bundle-version="9.0.4",
org.eclipse.jetty.websocket.common;bundle-version="9.0.4",
javax.validation;bundle-version="1.0.0",
com.sun.jersey;bundle-version="1.17.0",
org.objectweb.asm;bundle-version="3.3.1",
org.apache.commons.fileupload;bundle-version="1.2.2",
com.sap.sailing.domain.igtimiadapter;bundle-version="1.0.0",
com.sap.sse.common
Bundle-ClassPath: .
Web-ContextPath: /sailingserver
Export-Package: com.sap.sailing.server.gateway,
com.sap.sailing.server.gateway.jaxrs,
com.sap.sailing.server.gateway.jaxrs.spi
@@ -0,0 +1,80 @@
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 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;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.server.gateway.jaxrs.AbstractSailingServerResource;
/**
* 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("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<Void> speedWithConfidence;
ResponseBuilder responseBuilder;
try {
speedWithConfidence = getService().getPolarDataService().getSpeed(boatClass, new KnotSpeedImpl(windSpeed),
new DegreeBearingImpl(angle));
String resultString = "Speed: " + speedWithConfidence.getObject().getKnots() + "kn; Confidence: "
+ speedWithConfidence.getConfidence();
responseBuilder = Response.ok(resultString, MediaType.TEXT_PLAIN);
} catch (NotEnoughDataHasBeenAddedException e) {
responseBuilder = Response.noContent();
}
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("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<Void> speedWithBearing = service.getAverageSpeedWithBearing(boatClass,
windSpeed, legType, tack);
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();
}
return responseBuilder.build();
}
}
@@ -13,6 +13,7 @@ public class RestApiApplication extends Application {
classes.add(EventsResource.class);
classes.add(RegattasResource.class);
classes.add(LeaderboardsResource.class);
classes.add(PolarResource.class);
classes.add(SearchResource.class);
return classes;
}
@@ -1,25 +1,26 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Server Replication Test
Bundle-SymbolicName: com.sap.sailing.server.replication.test
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Fragment-Host: com.sap.sailing.server.replication
Import-Package: com.rabbitmq.client;version="2.8.4"
Require-Bundle: com.sap.sailing.server,
com.sap.sailing.domain.common,
com.sap.sailing.domain,
com.sap.sailing.domain.test,
com.sap.sse.mongodb,
com.mongodb.driver;bundle-version="2.6.2",
com.sap.sailing.domain.persistence,
com.sap.sailing.domain.tractracadapter,
com.tractrac.clientmodule;bundle-version="2.0.0",
com.sap.sailing.expeditionconnector,
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",
com.sap.sse.common,
org.mockito.mockito-core;bundle-version="1.9.5"
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Server Replication Test
Bundle-SymbolicName: com.sap.sailing.server.replication.test
Bundle-Version: 1.0.0.qualifier
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Fragment-Host: com.sap.sailing.server.replication
Import-Package: com.rabbitmq.client;version="2.8.4"
Require-Bundle: com.sap.sailing.server,
com.sap.sailing.domain.common,
com.sap.sailing.domain,
com.sap.sailing.domain.test,
com.sap.sse.mongodb,
com.mongodb.driver;bundle-version="2.6.2",
com.sap.sailing.domain.persistence,
com.sap.sailing.domain.tractracadapter,
com.tractrac.clientmodule;bundle-version="2.0.0",
com.sap.sailing.expeditionconnector,
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",
com.sap.sailing.polars;bundle-version="1.0.0",
com.sap.sse.common,
org.mockito.mockito-core;bundle-version="1.9.5"
@@ -137,12 +137,12 @@ public class MasterDataImportTest {
private Set<Serializable> storedLogUUIDs = new HashSet<Serializable>();
@After
public void tearDown() throws MalformedURLException, IOException, InterruptedException {
public void tearDown() {
deleteAllDataFromDatabase();
}
@Before
public void setUp() throws MalformedURLException, IOException, InterruptedException {
public void setUp() {
deleteAllDataFromDatabase();
}
@@ -153,7 +153,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();
+45 -42
View File
@@ -1,42 +1,45 @@
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Sailing Server
Bundle-SymbolicName: com.sap.sailing.server
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.sap.sailing.server.impl.Activator
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.sap.sailing.server.gateway.deserialization,
org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.3.1"
Bundle-ActivationPolicy: lazy
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.expeditionconnector,
com.sap.sailing.declination,
com.sap.sse.mongodb,
com.mongodb.driver;bundle-version="2.6.2",
com.sap.sailing.domain.persistence,
com.sap.sailing.domain.common,
com.sap.sailing.udpconnector,
com.sap.sailing.operationaltransformation,
com.sap.sailing.domain.shared.android,
com.sap.sailing.server.gateway.serialization,
com.sap.sse.common
Bundle-ClassPath: .
Export-Package: com.sap.sailing.server,
com.sap.sailing.server.impl;
x-friends:="com.sap.sailing.mongodb.test,
com.sap.sailing.server.test,
com.sap.sailing.server.gateway.test,
com.sap.sailing.domain.swisstimingadapter.test,
com.sap.sailing.gwt.ui.test,
com.sap.sailing.server.replication.test,
com.sap.sailing.domain.racelogtrackingadapter.test",
com.sap.sailing.server.masterdata,
com.sap.sailing.server.operationaltransformation,
com.sap.sailing.server.racelog.tracking,
com.sap.sailing.server.test.support;
x-friends:="com.sap.sailing.server.gateway.test.support"
Manifest-Version: 1.0
Bundle-ManifestVersion: 2
Bundle-Name: Sailing Server
Bundle-SymbolicName: com.sap.sailing.server
Bundle-Version: 1.0.0.qualifier
Bundle-Activator: com.sap.sailing.server.impl.Activator
Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Import-Package: com.sap.sailing.polars,
com.sap.sailing.polars.factory,
com.sap.sailing.server.gateway.deserialization,
org.json.simple,
org.json.simple.parser,
org.osgi.framework;version="1.3.0",
org.osgi.util.tracker;version="1.3.1"
Bundle-ActivationPolicy: lazy
Require-Bundle: com.sap.sailing.domain,
com.sap.sailing.expeditionconnector,
com.sap.sailing.declination,
com.sap.sse.mongodb,
com.mongodb.driver;bundle-version="2.6.2",
com.sap.sailing.domain.persistence,
com.sap.sailing.domain.common,
com.sap.sailing.udpconnector,
com.sap.sailing.operationaltransformation,
com.sap.sailing.domain.shared.android,
com.sap.sailing.server.gateway.serialization,
com.sap.sailing.polars,
com.sap.sse.common
Bundle-ClassPath: .
Export-Package: com.sap.sailing.server,
com.sap.sailing.server.impl;
x-friends:="com.sap.sailing.mongodb.test,
com.sap.sailing.server.test,
com.sap.sailing.server.gateway.test,
com.sap.sailing.domain.swisstimingadapter.test,
com.sap.sailing.gwt.ui.test,
com.sap.sailing.server.replication.test,
com.sap.sailing.domain.racelogtrackingadapter.test",
com.sap.sailing.server.masterdata,
com.sap.sailing.server.operationaltransformation,
com.sap.sailing.server.racelog.tracking,
com.sap.sailing.server.test.support;
x-friends:="com.sap.sailing.server.gateway.test.support"
@@ -33,5 +33,5 @@
<booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfigArea" value="false"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:default,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@5:true,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.nmeaconnector@default:default,com.sap.sailing.operationaltransformation@default:default,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.ess40@4:true,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@5:true,com.sap.sailing.server.replication@4:true,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www.events@4:true,com.sap.sailing.www@4:true,com.sap.sailing.xrr.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mongodb@default:default,com.sap.sse.security.ui@6:true,com.sap.sse.security.userstore.mongodb@4:true,com.sap.sse.security@5:true,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:default,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@5:true,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.nmeaconnector@default:default,com.sap.sailing.operationaltransformation@default:default,com.sap.sailing.polars@default:default,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.ess40@4:true,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@5:true,com.sap.sailing.server.replication@4:true,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www.events@4:true,com.sap.sailing.www@4:true,com.sap.sailing.xrr.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mongodb@default:default,com.sap.sse.security.ui@6:true,com.sap.sse.security.userstore.mongodb@4:true,com.sap.sse.security@5:true,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
</launchConfiguration>
@@ -1,36 +1,36 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.pde.ui.EquinoxLauncher">
<booleanAttribute key="append.args" value="true"/>
<booleanAttribute key="automaticAdd" value="false"/>
<booleanAttribute key="automaticValidate" value="false"/>
<stringAttribute key="bootstrap" value=""/>
<stringAttribute key="checked" value="[NONE]"/>
<booleanAttribute key="clearConfig" value="false"/>
<stringAttribute key="configLocation" value="${workspace_loc}/target"/>
<booleanAttribute key="default" value="true"/>
<booleanAttribute key="default_auto_start" value="true"/>
<intAttribute key="default_start_level" value="4"/>
<booleanAttribute key="includeOptional" value="true"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dexpedition.udp.port=2010 -Xmx4096m -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -Dkiwo.results=${project_loc:com.sap.sailing.kiworesultimport.test}/resources -Dpersistentcompetitors.clear=false -XX:+UseMembar -Digtimi.client.id=7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6 -Digtimi.client.secret=aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a -Digtimi.client.redirecturi=http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback -Dtractrac.mtb.cache.dir=/Users/Lennart/Projects/SAP_Sailing/tmp/mtb"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
<stringAttribute key="pde.version" value="3.3"/>
<stringAttribute key="profilingTraceType-ALLOCATION_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%INCREASE_COUNT%CTX_KEY%8192%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ADAPTIVE%CTX_KEY%false%CTX_ENTRY%KEY_MIN_SIZE%CTX_KEY%32%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%CLASS_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_MAX_SIZE%CTX_KEY%65536%CTX_ENTRY%KEY_INC_LINE_NRS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-IO_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-METHOD_PARAMETER_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-NETWORK_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_RESOLVE_ALL_HOSTS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-PERFORMANCE_HOTSPOT_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_IGNORE_SLEEPING_THREADS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-SYNCHRONIZATION_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<booleanAttribute key="show_selected_only" value="false"/>
<stringAttribute key="target_bundles" value="com.rabbitmq.client@default:default,com.sun.jersey@default:default,com.sun.mail.javax.mail@default:default,javax.servlet@default:default,javax.validation@default:default,javax.ws.rs@default:default,jcl.over.slf4j@default:default,org.apache.commons.codec@default:default,org.apache.commons.fileupload@default:default,org.apache.commons.io@default:default,org.apache.commons.lang@default:default,org.apache.commons.logging@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.apache.geronimo.specs.geronimo-annotation_1.0_spec@default:default,org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default,org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default,org.apache.httpcomponents.httpclient@default:default,org.apache.httpcomponents.httpcore@default:default,org.apache.servicemix.bundles.ehcache@default:default,org.apache.servicemix.bundles.scribe@default:default,org.apache.shiro.core@default:default,org.apache.shiro.ehcache@default:default,org.apache.shiro.web@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.console@default:default,org.eclipse.jetty.continuation@default:default,org.eclipse.jetty.deploy@default:default,org.eclipse.jetty.http@default:default,org.eclipse.jetty.io@default:default,org.eclipse.jetty.jmx@default:default,org.eclipse.jetty.osgi.boot@3:default,org.eclipse.jetty.security@default:default,org.eclipse.jetty.server@default:default,org.eclipse.jetty.servlet@default:default,org.eclipse.jetty.util@default:default,org.eclipse.jetty.webapp@default:default,org.eclipse.jetty.websocket.api@default:default,org.eclipse.jetty.websocket.client@default:default,org.eclipse.jetty.websocket.common@default:default,org.eclipse.jetty.websocket.server@default:default,org.eclipse.jetty.websocket.servlet@default:default,org.eclipse.jetty.xml@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.jdom@default:default,org.objectweb.asm@default:default,rome@default:default,routeconverter@default:default,slf4j.api@default:default,slf4j.jdk14@default:false"/>
<booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfigArea" value="false"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:default,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@5:true,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.nmeaconnector@default:default,com.sap.sailing.operationaltransformation@default:default,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.ess40@4:true,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@5:true,com.sap.sailing.server.replication@4:true,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www.events@4:true,com.sap.sailing.www@4:true,com.sap.sailing.xrr.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mongodb@default:default,com.sap.sse.security.ui@default:default,com.sap.sse.security.userstore.mongodb@default:default,com.sap.sse.security@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
</launchConfiguration>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.pde.ui.EquinoxLauncher">
<booleanAttribute key="append.args" value="true"/>
<booleanAttribute key="automaticAdd" value="false"/>
<booleanAttribute key="automaticValidate" value="false"/>
<stringAttribute key="bootstrap" value=""/>
<stringAttribute key="checked" value="[NONE]"/>
<booleanAttribute key="clearConfig" value="false"/>
<stringAttribute key="configLocation" value="${workspace_loc}/target"/>
<booleanAttribute key="default" value="true"/>
<booleanAttribute key="default_auto_start" value="true"/>
<intAttribute key="default_start_level" value="4"/>
<booleanAttribute key="includeOptional" value="true"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-ea -Declipse.ignoreApp=true -Dosgi.noShutdown=true -Dexpedition.udp.port=2010 -Xmx4096m -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -Dkiwo.results=${project_loc:com.sap.sailing.kiworesultimport.test}/resources -Dpersistentcompetitors.clear=false -XX:+UseMembar -Digtimi.client.id=7fcdd217e0aa16090edb4ad55b09ec43b2021090e209541fc9b7003c2a2b70c6 -Digtimi.client.secret=aa569cf4909bdc7b0e04b11873f3c4ea20687421e010fcc25b771cca9e6f3f9a -Digtimi.client.redirecturi=http://127.0.0.1:8888/igtimi/oauth/v1/authorizationcallback -Dtractrac.mtb.cache.dir=/Users/Lennart/Projects/SAP_Sailing/tmp/mtb"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
<stringAttribute key="pde.version" value="3.3"/>
<stringAttribute key="profilingTraceType-ALLOCATION_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%INCREASE_COUNT%CTX_KEY%8192%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ADAPTIVE%CTX_KEY%false%CTX_ENTRY%KEY_MIN_SIZE%CTX_KEY%32%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%CLASS_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_MAX_SIZE%CTX_KEY%65536%CTX_ENTRY%KEY_INC_LINE_NRS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-IO_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-METHOD_PARAMETER_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-NETWORK_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_RESOLVE_ALL_HOSTS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-PERFORMANCE_HOTSPOT_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_IGNORE_SLEEPING_THREADS%CTX_KEY%true%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<stringAttribute key="profilingTraceType-SYNCHRONIZATION_TRACE" value="KEY_SESSION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_USER_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_REQUEST_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_APPLICATION_FILTER%CTX_KEY%*%CTX_ENTRY%KEY_ENABLEMENT%CTX_KEY%false%CTX_ENTRY%"/>
<booleanAttribute key="show_selected_only" value="false"/>
<stringAttribute key="target_bundles" value="com.rabbitmq.client@default:default,com.sun.jersey@default:default,com.sun.mail.javax.mail@default:default,javax.servlet@default:default,javax.validation@default:default,javax.ws.rs@default:default,jcl.over.slf4j@default:default,org.apache.commons.codec@default:default,org.apache.commons.fileupload@default:default,org.apache.commons.io@default:default,org.apache.commons.lang@default:default,org.apache.commons.logging@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.apache.geronimo.specs.geronimo-annotation_1.0_spec@default:default,org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default,org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default,org.apache.httpcomponents.httpclient@default:default,org.apache.httpcomponents.httpcore@default:default,org.apache.servicemix.bundles.ehcache@default:default,org.apache.servicemix.bundles.scribe@default:default,org.apache.shiro.core@default:default,org.apache.shiro.ehcache@default:default,org.apache.shiro.web@default:default,org.eclipse.equinox.common@2:true,org.eclipse.equinox.console@default:default,org.eclipse.jetty.continuation@default:default,org.eclipse.jetty.deploy@default:default,org.eclipse.jetty.http@default:default,org.eclipse.jetty.io@default:default,org.eclipse.jetty.jmx@default:default,org.eclipse.jetty.osgi.boot@3:default,org.eclipse.jetty.security@default:default,org.eclipse.jetty.server@default:default,org.eclipse.jetty.servlet@default:default,org.eclipse.jetty.util@default:default,org.eclipse.jetty.webapp@default:default,org.eclipse.jetty.websocket.api@default:default,org.eclipse.jetty.websocket.client@default:default,org.eclipse.jetty.websocket.common@default:default,org.eclipse.jetty.websocket.server@default:default,org.eclipse.jetty.websocket.servlet@default:default,org.eclipse.jetty.xml@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.jdom@default:default,org.objectweb.asm@default:default,rome@default:default,routeconverter@default:default,slf4j.api@default:default,slf4j.jdk14@default:false"/>
<booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfigArea" value="false"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:default,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@4:true,com.sap.sailing.domain.swisstimingreplayadapter@4:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@4:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@5:true,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.nmeaconnector@default:default,com.sap.sailing.operationaltransformation@default:default,com.sap.sailing.polars@default:default,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.ess40@4:true,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@5:true,com.sap.sailing.server.replication@4:true,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www.events@4:true,com.sap.sailing.www@4:true,com.sap.sailing.xrr.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mongodb@default:default,com.sap.sse.security.ui@default:default,com.sap.sse.security.userstore.mongodb@default:default,com.sap.sse.security@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
</launchConfiguration>
@@ -1,31 +1,31 @@
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.pde.ui.EquinoxLauncher">
<booleanAttribute key="append.args" value="true"/>
<booleanAttribute key="automaticAdd" value="false"/>
<booleanAttribute key="automaticValidate" value="false"/>
<stringAttribute key="bootstrap" value=""/>
<stringAttribute key="checked" value="[NONE]"/>
<booleanAttribute key="clearConfig" value="false"/>
<stringAttribute key="configLocation" value="${workspace_loc}/target"/>
<booleanAttribute key="default" value="true"/>
<booleanAttribute key="default_auto_start" value="true"/>
<intAttribute key="default_start_level" value="4"/>
<booleanAttribute key="includeOptional" value="true"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djetty.port=8889 -Dreplication.exchangeName=sapsailinganalytics-replica -Declipse.ignoreApp=true -Dosgi.noShutdown=true&#13;&#10;-Dexpedition.udp.port=2011 -Xmx6000m -XX:+UseG1GC -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -XX:+UseMembar"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
<stringAttribute key="pde.version" value="3.3"/>
<booleanAttribute key="show_selected_only" value="false"/>
<stringAttribute key="target_bundles" value="com.rabbitmq.client@default:default,com.sun.jersey@default:default,com.sun.mail.javax.mail@default:default,javax.servlet@default:default,javax.validation@default:default,javax.ws.rs@default:default,jcl.over.slf4j@default:default,org.apache.commons.codec@default:default,org.apache.commons.fileupload@default:default,org.apache.commons.io@default:default,org.apache.commons.lang@default:default,org.apache.commons.logging@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.apache.felix.shell.remote@default:default,org.apache.felix.shell@default:default,org.apache.geronimo.specs.geronimo-annotation_1.0_spec@default:default,org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default,org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default,org.apache.httpcomponents.httpclient@default:default,org.apache.httpcomponents.httpcore@default:default,org.apache.servicemix.bundles.ehcache@default:default,org.apache.servicemix.bundles.scribe@default:default,org.apache.shiro.core@default:default,org.apache.shiro.ehcache@default:default,org.apache.shiro.web@default:default,org.eclipse.equinox.console@default:default,org.eclipse.jetty.continuation@default:default,org.eclipse.jetty.deploy@default:default,org.eclipse.jetty.http@default:default,org.eclipse.jetty.io@default:default,org.eclipse.jetty.jmx@default:default,org.eclipse.jetty.osgi.boot@default:default,org.eclipse.jetty.security@default:default,org.eclipse.jetty.server@default:default,org.eclipse.jetty.servlet@default:default,org.eclipse.jetty.util@default:default,org.eclipse.jetty.webapp@default:default,org.eclipse.jetty.websocket.api@default:default,org.eclipse.jetty.websocket.client@default:default,org.eclipse.jetty.websocket.common@default:default,org.eclipse.jetty.websocket.server@default:default,org.eclipse.jetty.websocket.servlet@default:default,org.eclipse.jetty.xml@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.hamcrest.core@default:default,org.hamcrest.integration@default:default,org.hamcrest.library@default:default,org.hamcrest.text@default:default,org.hamcrest@default:default,org.jdom@default:default,org.junit4@default:default,org.mockito.mockito-core@default:default,org.objectweb.asm@default:default,org.objenesis@default:default,rome@default:default,routeconverter@default:default,slf4j.api@default:default,slf4j.jdk14@default:false"/>
<booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfigArea" value="false"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:true,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@5:true,com.sap.sailing.domain.swisstimingreplayadapter@5:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@5:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@default:default,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.operationaltransformation@default:default,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.ess40@default:default,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@default:default,com.sap.sailing.server.replication@default:default,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www.events@default:default,com.sap.sailing.www@default:default,com.sap.sailing.xmlexport@default:default,com.sap.sailing.xrr.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mongodb@default:default,com.sap.sse.security.ui@default:default,com.sap.sse.security.userstore.mongodb@default:default,com.sap.sse.security@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
<stringAttribute key="yk-options" value="&#13;&#10;additional-options2=onexit\=snapshot&#13;&#10;"/>
</launchConfiguration>
<?xml version="1.0" encoding="UTF-8" standalone="no"?>
<launchConfiguration type="org.eclipse.pde.ui.EquinoxLauncher">
<booleanAttribute key="append.args" value="true"/>
<booleanAttribute key="automaticAdd" value="false"/>
<booleanAttribute key="automaticValidate" value="false"/>
<stringAttribute key="bootstrap" value=""/>
<stringAttribute key="checked" value="[NONE]"/>
<booleanAttribute key="clearConfig" value="false"/>
<stringAttribute key="configLocation" value="${workspace_loc}/target"/>
<booleanAttribute key="default" value="true"/>
<booleanAttribute key="default_auto_start" value="true"/>
<intAttribute key="default_start_level" value="4"/>
<booleanAttribute key="includeOptional" value="true"/>
<listAttribute key="org.eclipse.debug.ui.favoriteGroups">
<listEntry value="org.eclipse.debug.ui.launchGroup.debug"/>
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/JavaSE-1.8"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djetty.port=8889 -Dreplication.exchangeName=sapsailinganalytics-replica -Declipse.ignoreApp=true -Dosgi.noShutdown=true&#13;&#10;-Dexpedition.udp.port=2011 -Xmx6000m -XX:+UseG1GC -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -XX:+UseMembar"/>
<stringAttribute key="org.eclipse.jdt.launching.WORKING_DIRECTORY" value="${workspace_loc}"/>
<stringAttribute key="pde.version" value="3.3"/>
<booleanAttribute key="show_selected_only" value="false"/>
<stringAttribute key="target_bundles" value="com.rabbitmq.client@default:default,com.sun.jersey@default:default,com.sun.mail.javax.mail@default:default,javax.servlet@default:default,javax.validation@default:default,javax.ws.rs@default:default,jcl.over.slf4j@default:default,org.apache.commons.codec@default:default,org.apache.commons.fileupload@default:default,org.apache.commons.io@default:default,org.apache.commons.lang@default:default,org.apache.commons.logging@default:default,org.apache.felix.gogo.command@default:default,org.apache.felix.gogo.runtime@default:default,org.apache.felix.gogo.shell@default:default,org.apache.felix.shell.remote@default:default,org.apache.felix.shell@default:default,org.apache.geronimo.specs.geronimo-annotation_1.0_spec@default:default,org.apache.geronimo.specs.geronimo-j2ee-management_1.1_spec@default:default,org.apache.geronimo.specs.geronimo-jms_1.1_spec@default:default,org.apache.httpcomponents.httpclient@default:default,org.apache.httpcomponents.httpcore@default:default,org.apache.servicemix.bundles.ehcache@default:default,org.apache.servicemix.bundles.scribe@default:default,org.apache.shiro.core@default:default,org.apache.shiro.ehcache@default:default,org.apache.shiro.web@default:default,org.eclipse.equinox.console@default:default,org.eclipse.jetty.continuation@default:default,org.eclipse.jetty.deploy@default:default,org.eclipse.jetty.http@default:default,org.eclipse.jetty.io@default:default,org.eclipse.jetty.jmx@default:default,org.eclipse.jetty.osgi.boot@default:default,org.eclipse.jetty.security@default:default,org.eclipse.jetty.server@default:default,org.eclipse.jetty.servlet@default:default,org.eclipse.jetty.util@default:default,org.eclipse.jetty.webapp@default:default,org.eclipse.jetty.websocket.api@default:default,org.eclipse.jetty.websocket.client@default:default,org.eclipse.jetty.websocket.common@default:default,org.eclipse.jetty.websocket.server@default:default,org.eclipse.jetty.websocket.servlet@default:default,org.eclipse.jetty.xml@default:default,org.eclipse.osgi.services@default:default,org.eclipse.osgi@-1:true,org.hamcrest.core@default:default,org.hamcrest.integration@default:default,org.hamcrest.library@default:default,org.hamcrest.text@default:default,org.hamcrest@default:default,org.jdom@default:default,org.junit4@default:default,org.mockito.mockito-core@default:default,org.objectweb.asm@default:default,org.objenesis@default:default,rome@default:default,routeconverter@default:default,slf4j.api@default:default,slf4j.jdk14@default:false"/>
<booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfigArea" value="false"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:true,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@5:true,com.sap.sailing.domain.swisstimingreplayadapter@5:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@5:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@default:default,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.operationaltransformation@default:default,com.sap.sailing.polars@default:default,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.ess40@default:default,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@default:default,com.sap.sailing.server.replication@default:default,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www.events@default:default,com.sap.sailing.www@default:default,com.sap.sailing.xmlexport@default:default,com.sap.sailing.xrr.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mongodb@default:default,com.sap.sse.security.ui@default:default,com.sap.sse.security.userstore.mongodb@default:default,com.sap.sse.security@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
<stringAttribute key="yk-options" value="&#13;&#10;additional-options2=onexit\=snapshot&#13;&#10;"/>
</launchConfiguration>
@@ -16,7 +16,7 @@
<listEntry value="org.eclipse.debug.ui.launchGroup.run"/>
</listAttribute>
<booleanAttribute key="org.eclipse.jdt.launching.ATTR_USE_START_ON_FIRST_THREAD" value="false"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jdk1.8.0_20"/>
<stringAttribute key="org.eclipse.jdt.launching.JRE_CONTAINER" value="org.eclipse.jdt.launching.JRE_CONTAINER/org.eclipse.jdt.internal.debug.ui.launcher.StandardVMType/jre1.8.0_20"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-os ${target.os} -ws ${target.ws} -arch ${target.arch} -nl ${target.nl} -consoleLog -console -clean"/>
<stringAttribute key="org.eclipse.jdt.launching.SOURCE_PATH_PROVIDER" value="org.eclipse.pde.ui.workbenchClasspathProvider"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Djetty.port=8889 -Dreplication.exchangeName=sapsailinganalytics-replica -Declipse.ignoreApp=true -Dosgi.noShutdown=true&#13;&#10;-Dexpedition.udp.port=2011 -Xmx6000m -Djetty.home=${project_loc:com.sap.sailing.server}/../target/configuration/jetty -Djava.util.logging.config.file=${project_loc:com.sap.sailing.server}/../target/configuration/logging_debug.properties -XX:+UseMembar -Djava.library.path=${project_loc:com.sap.sailing.monitoring}/../../configuration/native-libraries -Dtractrac.mtb.cache.dir=/Users/spamies/Projects/sailing/tmp/mtb"/>
@@ -27,6 +27,6 @@
<booleanAttribute key="tracing" value="false"/>
<booleanAttribute key="useCustomFeatures" value="false"/>
<booleanAttribute key="useDefaultConfigArea" value="false"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.autoload@5:true,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:true,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@5:true,com.sap.sailing.domain.swisstimingreplayadapter@5:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@5:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@default:default,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.operationaltransformation@default:default,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.ess40@default:default,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@default:default,com.sap.sailing.server.replication@default:default,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www.events@default:default,com.sap.sailing.www@default:default,com.sap.sailing.xmlexport@default:default,com.sap.sailing.xrr.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mongodb@default:default,com.sap.sse.security.ui@default:default,com.sap.sse.security.userstore.mongodb@default:default,com.sap.sse.security@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
<stringAttribute key="workspace_bundles" value="com.google.gwt.osgi@default:default,com.googlecode.java-diff-utils@default:default,com.mongodb.driver@default:default,com.sap.sailing.autoload@5:true,com.sap.sailing.barbados.resultimport@default:default,com.sap.sailing.datamining.shared@default:default,com.sap.sailing.datamining@default:default,com.sap.sailing.declination@default:default,com.sap.sailing.domain.common@default:default,com.sap.sailing.domain.deckmanadapter@4:true,com.sap.sailing.domain.igtimiadapter.persistence@default:default,com.sap.sailing.domain.igtimiadapter@default:default,com.sap.sailing.domain.persistence@default:default,com.sap.sailing.domain.racelogtrackingadapter@3:true,com.sap.sailing.domain.shared.android@default:default,com.sap.sailing.domain.swisstimingadapter.persistence@default:default,com.sap.sailing.domain.swisstimingadapter@5:true,com.sap.sailing.domain.swisstimingreplayadapter@5:true,com.sap.sailing.domain.tractracadapter.persistence@default:default,com.sap.sailing.domain.tractracadapter@5:true,com.sap.sailing.domain@default:default,com.sap.sailing.ess40.resultimport@default:default,com.sap.sailing.expeditionconnector@default:default,com.sap.sailing.freg.resultimport@default:default,com.sap.sailing.geocoding@default:default,com.sap.sailing.gwt.ui@default:default,com.sap.sailing.kiworesultimport@default:default,com.sap.sailing.manage2sail.resultimport@default:default,com.sap.sailing.manage2sail@default:default,com.sap.sailing.operationaltransformation@default:default,com.sap.sailing.polars@default:default,com.sap.sailing.resultimport@default:default,com.sap.sailing.sailwave.resultimport@default:default,com.sap.sailing.server.gateway.ess40@default:default,com.sap.sailing.server.gateway.serialization@default:default,com.sap.sailing.server.gateway@default:default,com.sap.sailing.server.replication@default:default,com.sap.sailing.server.trackfiles@default:default,com.sap.sailing.server@default:default,com.sap.sailing.simulator@default:default,com.sap.sailing.udpconnector@default:default,com.sap.sailing.velum.resultimport@default:default,com.sap.sailing.www.events@default:default,com.sap.sailing.www@default:default,com.sap.sailing.xmlexport@default:default,com.sap.sailing.xrr.resultimport@default:default,com.sap.sse.common@default:default,com.sap.sse.datamining.shared@default:default,com.sap.sse.datamining@default:default,com.sap.sse.gwt.adminconsole@default:default,com.sap.sse.gwt@default:default,com.sap.sse.mongodb@default:default,com.sap.sse.security.ui@default:default,com.sap.sse.security.userstore.mongodb@default:default,com.sap.sse.security@default:default,com.tractrac.clientmodule@default:default,net.sf.marineapi@default:default,org.json.simple@default:default,org.moxieapps.gwt.highcharts@default:default"/>
<stringAttribute key="yk-options" value="&#13;&#10;additional-options2=onexit\=snapshot&#13;&#10;"/>
</launchConfiguration>
Loaded 100 of 144 files, more files were not shown because too many files have changed in this diff. Show more