Removed old PolarSheets.html and its backend functionality

replaced by datamining ui approach to polar generation.
see wiki soon...
This commit is contained in:
Frederik Petersen committed 2015-09-22 16:28:30 +02:00
1 parent 286b434844
commit dfea54d398
50 files changed
+6 -4163

No files matched your search

@@ -1,8 +1,6 @@
package com.sap.sailing.domain.polars;
import java.util.Set;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
@@ -14,8 +12,6 @@ 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.ManeuverType;
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.common.confidence.BearingWithConfidence;
@@ -51,30 +47,6 @@ public interface PolarDataService {
*/
SpeedWithConfidence<Void> getSpeed(BoatClass boatClass, Speed windSpeed, Bearing trueWindAngle)
throws NotEnoughDataHasBeenAddedException;
/**
* Generates a polar sheet for given 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;
/**
*
* @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);
/**
*
@@ -1,239 +0,0 @@
package com.sap.sailing.gwt.ui.test;
import java.util.ArrayList;
import java.util.HashSet;
import java.util.List;
import java.util.NavigableSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentSkipListSet;
import junit.framework.Assert;
import org.junit.Test;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.impl.WindSpeedSteppingWithMaxDistance;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.MarkPassingByTimeComparator;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl;
import com.sap.sailing.gwt.ui.client.SailingService;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.server.impl.RacingEventServiceImpl;
import com.sap.sse.common.Color;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class PolarSheetGenerationServiceTest {
private static final String BOAT_CLASS = "Forelle";
@Test
public void testPolarSheetGenerationService() throws Exception {
SailingService service = new MockSailingServiceForPolarSheetGeneration();
double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. };
WindSpeedSteppingWithMaxDistance windStepping = new WindSpeedSteppingWithMaxDistance(levels, 2.0);
PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 20, 0, false, true, 5,
0.05, false, windStepping, false, 0);
List<RegattaAndRaceIdentifier> idList = new ArrayList<RegattaAndRaceIdentifier>();
idList.add(new RegattaNameAndRaceName("IrgendeineRegatta", "IrgendeinRennen"));
PolarSheetGenerationResponse triggerData = service.generatePolarSheetForRaces(idList, settings, "");
Assert.assertNotNull(triggerData);
Assert.assertEquals(BOAT_CLASS, triggerData.getName());
Assert.assertNotNull(triggerData.getId());
PolarSheetsData results = triggerData.getData();
Assert.assertNotNull(results);
Assert.assertEquals(4, results.getDataCount());
Assert.assertEquals(4.0, results.getAveragedPolarDataByWindSpeed()[0][45]);
Assert.assertEquals(2.0, results.getAveragedPolarDataByWindSpeed()[0][55]);
Assert.assertEquals(6.0, results.getAveragedPolarDataByWindSpeed()[0][35]);
}
@SuppressWarnings("serial")
private class MockSailingServiceForPolarSheetGeneration extends SailingServiceImplMock {
@Override
protected RacingEventService getService() {
RacingEventService service = new MockRacingEventServiceForPolarSheetGeneration();
return service;
}
}
private class MockRacingEventServiceForPolarSheetGeneration extends RacingEventServiceImpl {
@Override
public DynamicTrackedRace getTrackedRace(RegattaAndRaceIdentifier raceIdentifier) {
MockTrackedRaceForPolarSheetGeneration trackedRace = new MockTrackedRaceForPolarSheetGeneration();
return trackedRace;
}
}
@SuppressWarnings("serial")
private class MockTrackedRaceForPolarSheetGeneration extends MockedTrackedRace {
@Override
public DynamicGPSFixTrack<Competitor, GPSFixMoving> getTrack(Competitor competitor) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = new MockDynamicGPSFixMovinTrackForPolarSheetGeneration<Competitor>(competitor, 0);
//Before start
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(0.009, 0.009), new MillisecondsTimePoint(3), new KnotSpeedWithBearingImpl(12.0, new DegreeBearingImpl(-45.0))));
//Race
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.0, 1.0), new MillisecondsTimePoint(10), new KnotSpeedWithBearingImpl(3.0, new DegreeBearingImpl(-45.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.001, 1.001), new MillisecondsTimePoint(11), new KnotSpeedWithBearingImpl(5.0, new DegreeBearingImpl(-45.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.002, 1.002), new MillisecondsTimePoint(12), new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(-55.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.003, 1.003), new MillisecondsTimePoint(13), new KnotSpeedWithBearingImpl(6.0, new DegreeBearingImpl(-35.0))));
//After Finish
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.01, 1.01), new MillisecondsTimePoint(75), new KnotSpeedWithBearingImpl(12.0, new DegreeBearingImpl(-45.0))));
return track;
}
@Override
public Wind getWind(Position p, TimePoint at) {
Wind wind = new WindImpl(p, at, new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(180.0)));
return wind;
}
@Override
public Wind getWind(Position p, TimePoint at, Set<WindSource> windSourcesToExclude) {
return getWind(p, at);
}
@Override
public WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at) {
return new WindWithConfidenceImpl<com.sap.sse.common.Util.Pair<Position, TimePoint>>(new WindImpl(p, at,
new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(180.0))), 0.9,
new com.sap.sse.common.Util.Pair<Position, TimePoint>(p, at), true);
}
@Override
public WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at,
Set<WindSource> windSourcesToExclude) {
return getWindWithConfidence(p, at);
}
@Override
public RaceDefinition getRace() {
BoatClass forelle = new BoatClassImpl("Forelle", true);
ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>();
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
ArrayList<Competitor> competitors = new ArrayList<Competitor>();
Competitor competitor = new CompetitorImpl(UUID.randomUUID(), "Hans Frantz", Color.RED, null, null,
new TeamImpl("SAP", null, null), new BoatImpl("Schnelle Forelle", forelle, "GER000"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
competitors.add(competitor);
RaceDefinition race = new RaceDefinitionImpl("Forelle1", new CourseImpl("ForelleCourse", waypoints), forelle, competitors);
return race;
}
@Override
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
NavigableSet<MarkPassing> passings = new ConcurrentSkipListSet<MarkPassing>(
MarkPassingByTimeComparator.INSTANCE);
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(10), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(20), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(30), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(40), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(50), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(60), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(70), null, competitor));
return passings;
}
@Override
public Set<WindSource> getWindSources(WindSourceType type) {
Set<WindSource> sources = new HashSet<WindSource>();
sources.add(new WindSourceImpl(type));
return sources;
}
@Override
public TimePoint getStartOfRace() {
return new MillisecondsTimePoint(9);
}
@Override
public TimePoint getEndOfRace() {
return new MillisecondsTimePoint(80);
};
}
@SuppressWarnings("serial")
private class MockGPSFixMovingForPolarSheetGeneration extends GPSFixMovingImpl {
public MockGPSFixMovingForPolarSheetGeneration(Position position, TimePoint timePoint, SpeedWithBearing speed) {
super(position, timePoint, speed);
}
@Override
public boolean isValidCached() {
return true;
}
}
@SuppressWarnings("serial")
private class MockDynamicGPSFixMovinTrackForPolarSheetGeneration<ItemType> extends DynamicGPSFixMovingTrackImpl<ItemType> {
public MockDynamicGPSFixMovinTrackForPolarSheetGeneration(ItemType trackedItem,
long millisecondsOverWhichToAverage) {
super(trackedItem, millisecondsOverWhichToAverage);
}
@Override
protected boolean isValid(NavigableSet<GPSFixMoving> rawFixes, GPSFixMoving e) {
return true;
}
@Override
public SpeedWithBearing getEstimatedSpeed(TimePoint at) {
return this.getFirstFixAtOrAfter(at).getSpeed();
}
}
}
-128
View File
@@ -1,128 +0,0 @@
/** Add css rules here for your application. */
/*
body #calendar:after
content: " ";
display: block;
height: 0;
clear: both;
visibility: hidden;
*/
.clear {
clear: both;
font-size: 1px;
height: 0px;
line-height: 0px;
margin: -1px 0 0;
width: 100%; }
.cover {
left: -1000px;
overflow: hidden;
position: absolute;
top: -1000px; }
body {
background: #fff;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
line-height: 1;
font-size: 14px;
font-weight: 400;
padding: 85px 0 120px 0;
margin: 0;
}
/** Example rules used by the template application (remove for your app) */
h1 {
font-size: 2em;
font-weight: bold;
color: #777777;
margin: 40px 0px 70px;
text-align: center;
}
table {
border-collapse: separate; }
.leaderboardMargin {
margin: 5px;
}
.leaderboardContent {
}
.leaderboardContent td, .leaderboardContent th {
vertical-align: bottom;
line-height: 1.3;
padding-top: 2px;
}
.leaderboardContent-toolbar {
}
.leaderboardLabel {
border-bottom: 2px solid #007dc0;
font-weight: bold;
margin: 5px;
}
.refreshAndSettings {
background: #f2f2f2;
border-bottom: 2px solid #fff;
border-top: 2px solid #fff;
height: 30px; }
.refreshAndSettings table td {
padding: 0 10px 0 0;}
.refreshAndSettings table table td{
padding: 0 0 0 0;}
input.magnifier{
display: block;
margin: 3px auto 0 auto !important;
}
body, table td, select, button {
font-family: 'Open Sans', Arial, Verdana, sans-serif;
font-weight: 300;
font-size: 14px;
}
.errorLabel {
color: #FF0000;
}
.abstractChartPanel-importantMessageOfChart {
font-family: 'Open Sans',Arial,Verdana,sans-serif;
font-weight: 600;
font-size: 20px;
height: 70px;
margin: 0 auto;
padding: 49px 0 0 80px;
width: 300px;
color: #7c7c7c;
background: url(images/important-message.png) left center no-repeat;
}
.LeaderboardHeader {
position: relative;
margin: 0 auto;
width: 400px;
text-align: center;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
font-size: 18px;
color: #fff;
margin-top: 22px;
}
.leaderboardHeading {
font-size: 16px;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
font-weight: bold;
}
.gwt-DialogBox {
z-index: 20;
}
@@ -1,33 +0,0 @@
<!doctype html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8">
<title>SAP Sailing Analytics Polar Sheets</title>
<meta content='width = device-width, initial-scale = 1.0, user-scalable = yes' name='viewport'>
<meta content='yes' name='apple-mobile-web-app-capable'>
<meta content='black' name='apple-mobile-web-app-status-bar-style'>
<!-- <link href='iphone-splash-screen.png' rel='apple-touch-startup-image'> -->
<link href='images/sap-sailing-app-icon.png' rel='apple-touch-icon'>
<link rel="shortcut icon" type="image/x-icon" href="images/sap.ico" />
<link rel="stylesheet" type="text/css" href="/sailing-fontface-1.0.cache.css">
<link rel="stylesheet" type="text/css" href="PolarSheets.css">
<script type="text/javascript" src="js/jquery-1.5.2.min.js"></script>
<script type="text/javascript" src="com.sap.sailing.gwt.ui.PolarSheets/com.sap.sailing.gwt.ui.PolarSheets.nocache.js"></script>
</head>
<body>
<iframe src="javascript:''" id="__gwt_historyFrame" tabIndex='-1' style="position:absolute;width:0;height:0;border:0"></iframe>
<!-- RECOMMENDED if your web app will not function without JavaScript enabled -->
<noscript>
<div style="width: 22em; position: absolute; left: 50%; margin-left: -11em; color: red; background-color: white; border: 1px solid red; padding: 4px; font-family: sans-serif">
Your web browser must have JavaScript enabled
in order for this application to display correctly.
</div>
</noscript>
</body>
</html>
@@ -56,7 +56,7 @@
<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="-nosuperDevMode -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.ui.Search -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/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart.html com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -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 com.sap.sailing.gwt.ui.VideoPopup"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -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.ui.Search -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/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart.html com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -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 com.sap.sailing.gwt.ui.VideoPopup"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xmx2048m -XX:MaxPermSize=512m -Xdebug -Xrunjdwp:transport=dt_socket,address=7999,server=y"/>
</launchConfiguration>
@@ -11,7 +11,6 @@
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.regattaoverview.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.ui.PolarSheets"/>
<listEntry value="com.sap.sailing.gwt.ui.Leaderboard"/>
<listEntry value="com.sap.sailing.gwt.ui.Simulator"/>
<listEntry value="com.sap.sailing.gwt.ui.YoutubePopup"/>
@@ -74,7 +73,7 @@
<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="-superDevMode -incremental -workDir &quot;${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work&quot; -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -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.regattaoverview.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 com.sap.sailing.gwt.home.Desktop com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-superDevMode -incremental -workDir &quot;${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work&quot; -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort 9876 -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -startupUrl /gwt/Leaderboard.html com.sap.sailing.gwt.ui.Leaderboard -startupUrl /gwt/Spectator.html com.sap.sailing.gwt.ui.Spectator -startupUrl /gwt/EmbeddedMapAndWindChart com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart -startupUrl /gwt/RaceBoard.html com.sap.sailing.gwt.ui.RaceBoard -startupUrl /gwt/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.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 com.sap.sailing.gwt.home.Desktop com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard com.sap.sailing.gwt.ui.EmbeddedMapAndWindChart"/>
<stringAttribute key="org.eclipse.jdt.launching.PROJECT_ATTR" value="com.sap.sailing.gwt.ui"/>
<stringAttribute key="org.eclipse.jdt.launching.VM_ARGUMENTS" value="-Xms1024m -Xmx3072m"/>
</launchConfiguration>
@@ -10,7 +10,6 @@
<listEntry value="com.sap.sailing.gwt.ui.LeaderboardEditing"/>
<listEntry value="com.sap.sailing.gwt.ui.Spectator"/>
<listEntry value="com.sap.sailing.gwt.regattaoverview.RegattaOverview"/>
<listEntry value="com.sap.sailing.gwt.ui.PolarSheets"/>
<listEntry value="com.sap.sailing.gwt.ui.Leaderboard"/>
<listEntry value="com.sap.sailing.gwt.ui.Simulator"/>
<listEntry value="com.sap.sailing.gwt.ui.YoutubePopup"/>
@@ -72,7 +71,7 @@
<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="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -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.regattaoverview.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 com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.ui.PolarSheets com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining com.sap.sailing.gwt.ui.RaceBoard"/>
<stringAttribute key="org.eclipse.jdt.launching.PROGRAM_ARGUMENTS" value="-nosuperDevMode -incremental -war &quot;${project_loc:com.sap.sailing.gwt.ui}&quot; -noserver -remoteUI &quot;${gwt_remote_ui_server_port}:${unique_id}&quot; -logLevel INFO -codeServerPort auto -startupUrl /gwt/Home.html com.sap.sailing.gwt.home.Home -startupUrl /gwt/AdminConsole.html com.sap.sailing.gwt.ui.AdminConsole -startupUrl /gwt/LeaderboardEditing.html com.sap.sailing.gwt.ui.LeaderboardEditing -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/RegattaOverview.html com.sap.sailing.gwt.regattaoverview.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 com.sap.sailing.gwt.home.Home com.sap.sailing.gwt.ui.AdminConsole com.sap.sailing.gwt.AutoPlay com.sap.sailing.gwt.ui.YoutubePopup com.sap.sailing.gwt.ui.Simulator com.sap.sailing.gwt.ui.Leaderboard com.sap.sailing.gwt.regattaoverview.RegattaOverview com.sap.sailing.gwt.ui.Spectator com.sap.sailing.gwt.ui.LeaderboardEditing com.sap.sailing.gwt.ui.DataMining 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 -Dgwt-usearchives=false -Dgwt.persistentunitcache=false"/>
</launchConfiguration>
@@ -21,7 +21,6 @@ bin.includes = META-INF/,\
RaceBoard.html,\
Spectator.html,\
com.sap.sailing.gwt.ui.Spectator/,\
com.sap.sailing.gwt.ui.PolarSheets/,\
com.sap.sailing.gwt.ui.RaceBoard/,\
Spectator.css,\
com.sap.sailing.gwt.ui.Simulator/,\
@@ -35,8 +34,6 @@ bin.includes = META-INF/,\
com.sap.sailing.gwt.ui.YoutubePopup/,\
YoutubePopup.html,\
BrowserUnsupported.html,\
PolarSheets.css,\
PolarSheets.html,\
com.sap.sailing.gwt.ui.DataMining/,\
DataMining.css,\
DataMining.html,\
-1
View File
@@ -163,7 +163,6 @@
<module>com.sap.sailing.gwt.ui.YoutubePopup</module>
<module>com.sap.sailing.gwt.ui.Simulator</module>
<module>com.sap.sailing.gwt.regattaoverview.RegattaOverview</module>
<module>com.sap.sailing.gwt.ui.PolarSheets</module>
</modules>
<!-- uncomment the following for PRETTY-printed, non-obfuscated output format
<style>PRETTY</style>
@@ -21,8 +21,6 @@ import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsXYDiagramData;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RaceIdentifier;
@@ -369,22 +367,13 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
List<SwissTimingArchiveConfigurationDTO> getPreviousSwissTimingArchiveConfigurations();
void storeSwissTimingArchiveConfiguration(String swissTimingUrl);
PolarSheetGenerationResponse generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces,
PolarSheetGenerationSettings settings, String name) throws Exception;
void createCourseAreas(UUID eventId, String[] courseAreaNames);
List<String> getBoatClassNamesWithPolarSheetsAvailable();
void removeCourseAreas(UUID eventId, UUID[] courseAreaIds);
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 */
@@ -16,8 +16,6 @@ import com.sap.sailing.domain.common.LeaderboardType;
import com.sap.sailing.domain.common.LegIdentifier;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsXYDiagramData;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RaceIdentifier;
@@ -473,10 +471,6 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
void storeSwissTimingArchiveConfiguration(String swissTimingUrl, AsyncCallback<Void> asyncCallback);
void generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces,
PolarSheetGenerationSettings settings, String name, AsyncCallback<PolarSheetGenerationResponse> asyncCallback);
void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, UUID defaultCourseAreaUuid,
RegattaConfigurationDTO regattaConfiguration, boolean useStartTimeInference, AsyncCallback<Void> callback);
@@ -569,14 +563,9 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
void removeIgtimiAccount(String eMailOfAccountToRemove, AsyncCallback<Void> asyncCallback);
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,
@@ -8,7 +8,6 @@ import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.polarsheets.WindSpeedSteppingConfiguratorPanel;
import com.sap.sailing.polars.datamining.shared.PolarDataMiningSettings;
import com.sap.sailing.polars.datamining.shared.PolarDataMiningSettingsImpl;
import com.sap.sse.gwt.client.controls.IntegerBox;
@@ -1,4 +1,4 @@
package com.sap.sailing.gwt.ui.polarsheets;
package com.sap.sailing.gwt.ui.polarmining;
import java.util.ArrayList;
import java.util.Collections;
@@ -1,32 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.gwt.ui.adminconsole.AbstractTrackedRacesListComposite;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.gwt.client.ErrorReporter;
public abstract class AbstractFilteredTrackedRacesList extends AbstractTrackedRacesListComposite {
private RaceFilter filter;
public AbstractFilteredTrackedRacesList(SailingServiceAsync sailingService, ErrorReporter errorReporter,
RegattaRefresher regattaRefresher, RaceSelectionProvider raceSelectionProvider,
StringMessages stringMessages, boolean hasMultiSelection, RaceFilter filter) {
super(sailingService, errorReporter, regattaRefresher, raceSelectionProvider, stringMessages, hasMultiSelection);
this.filter = filter;
createUI();
}
@Override
protected boolean raceIsToBeAddedToList(RaceDTO race) {
if (filter!=null) {
return filter.compliesToFilter(race);
}
return true;
}
}
@@ -1,160 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import org.moxieapps.gwt.highcharts.client.AxisTitle;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.Legend;
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 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;
private final Map<String, Set<Series>> seriesForId;
private final Map<String, Series> seriesForName;
public AngleOverDataSizeHistogramPanel(StringMessages stringMessages) {
super(Unit.PX);
this.seriesForId = new HashMap<>();
this.seriesForName = new HashMap<>();
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().setLineColor("#666666")
.setLineWidth(1).setMarker(new Marker().setLineWidth(1).setLineColor("#666666")));
histogramChart.setLegend(new Legend().setEnabled(false));
return histogramChart;
}
public void addData(Map<Integer, PolarSheetsHistogramData> histogramDataPerAngle, String seriesId, String actualSeriesName, boolean setVisible) {
chart.setTitle("");
Series series = chart.createSeries();
Point[] points = toPoints(histogramDataPerAngle);
series.setPoints(points, false);
series.setName(actualSeriesName);
chart.addSeries(series, false, false);
series.setVisible(setVisible, false);
if (!seriesForId.containsKey(seriesId)) {
seriesForId.put(seriesId, new HashSet<Series>());
}
Set<Series> seriesSet = seriesForId.get(seriesId);
seriesSet.add(series);
seriesForName.put(actualSeriesName, series);
}
private Point[] toPoints(Map<Integer, PolarSheetsHistogramData> histogramDataPerAngle) {
List<Number> xValues = new ArrayList<>();
List<Number> yValues = new ArrayList<>();
for (int i = 0; i < 360; i++) {
if (histogramDataPerAngle.get(i) != null) {
int convertedAngle = i > 180 ? i - 360 : i;
xValues.add(convertedAngle);
yValues.add(histogramDataPerAngle.get(i).getDataCount());
}
}
return toPoints(xValues, yValues);
}
private Point[] toPoints(Iterable<Number> xValues, Iterable<Number> yValues) {
List<Point> points = new ArrayList<>();
Iterator<Number> x=xValues.iterator();
Iterator<Number> y=yValues.iterator();
while (x.hasNext()) {
points.add(new Point(x.next(), y.next()));
}
return points.toArray(new Point[points.size()]);
}
@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();
}
public void removeSeries(String seriesId) {
for (Series series : seriesForId.get(seriesId)) {
chart.removeSeries(series);
seriesForName.remove(series.getName());
}
seriesForId.remove(seriesId);
}
public void removeAllSeries() {
chart.removeAllSeries();
seriesForId.clear();
}
public void redrawChart() {
chart.redraw();
}
public void showSeries(String name) {
if (seriesForName.containsKey(name)) {
seriesForName.get(name).show();
}
}
public void hideSeries(String name) {
if (seriesForName.containsKey(name)) {
seriesForName.get(name).hide();
}
}
}
@@ -1,117 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.ArrayList;
import java.util.List;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.FlexTable;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
import com.sap.sailing.gwt.ui.client.StringMessages;
public class PolarChartControlPanel extends FlexTable implements PolarSheetListChangeListener {
private final StringMessages stringMessages;
private final PolarSheetsChartPanel chartPanel;
private final ListBox nameListBox;
private final List<Button> buttons;
public PolarChartControlPanel(final StringMessages stringMessages, final PolarSheetsChartPanel chartPanel) {
this.stringMessages = stringMessages;
this.chartPanel = chartPanel;
nameListBox = new ListBox();
buttons = new ArrayList<Button>();
setChangeHandlerForNameListBox();
createRemoveAllRow();
createPickIndividualSheetRow();
createIndividualRemoveRow();
}
private void setChangeHandlerForNameListBox() {
nameListBox.addChangeHandler(new ChangeHandler() {
@Override
public void onChange(ChangeEvent event) {
onListBoxChange();
}
});
}
private void createIndividualRemoveRow() {
Button removeButton = new Button(stringMessages.remove());
buttons.add(removeButton);
removeButton.setEnabled(false);
removeButton.addClickHandler(createRemoveIndividualHandler());
this.setWidget(2, 1, removeButton);
}
private ClickHandler createRemoveIndividualHandler() {
return new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (nameListBox.getItemCount() > 0) {
int selectedIndex = nameListBox.getSelectedIndex();
String name = nameListBox.getItemText(selectedIndex);
if (name != null && !name.isEmpty()) {
chartPanel.removeSeries(name);
nameListBox.removeItem(selectedIndex);
onListBoxChange();
}
}
}
};
}
private void createPickIndividualSheetRow() {
Label pickSheetLabel = new Label(stringMessages.selectSheet() + ":");
this.setWidget(1, 0, pickSheetLabel);
this.setWidget(1, 1, nameListBox);
}
private void createRemoveAllRow() {
Label removeAllLabel = new Label(stringMessages.removeAllSheets() + ":");
this.setWidget(0, 0, removeAllLabel);
this.getFlexCellFormatter().setHeight(0, 0, "100px");
Button clearAllButton = new Button(stringMessages.removeAll());
buttons.add(clearAllButton);
clearAllButton.setEnabled(false);
clearAllButton.addClickHandler(createRemoveAllButtonHandler());
this.setWidget(0, 1, clearAllButton);
}
private ClickHandler createRemoveAllButtonHandler() {
return new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
nameListBox.clear();
onListBoxChange();
chartPanel.removeAllSeries();
}
};
}
@Override
public void polarSheetAdded(String name) {
nameListBox.addItem(name);
onListBoxChange();
}
private void onListBoxChange() {
if (nameListBox.getItemCount() < 1) {
for (Button button : buttons) {
button.setEnabled(false);
}
} else {
for (Button button : buttons) {
button.setEnabled(true);
}
}
}
}
@@ -1,178 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import com.google.gwt.event.logical.shared.ValueChangeEvent;
import com.google.gwt.event.logical.shared.ValueChangeHandler;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DoubleBox;
import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.gwt.client.controls.IntegerBox;
import com.sap.sse.gwt.client.dialog.DataEntryDialog;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator;
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
public class PolarSheetGenerationSettingsDialogComponent implements SettingsDialogComponent<PolarSheetGenerationSettings> {
private PolarSheetGenerationSettings settings;
private StringMessages stringMessages;
private IntegerBox minimumGraphDataSizeBox;
private DoubleBox minimumWindConfidenceBox;
private IntegerBox minimumDataCountPerAngleBox;
private IntegerBox numberOfHistogramColumnsBox;
private DoubleBox minimumConfidenceMeasureBox;
private CheckBox useOnlyWindGaugesForWindSpeedBox;
private CheckBox shouldRemoveOutliersBox;
private DoubleBox outlierRadiusBox;
private DoubleBox outlierNeighborhoodPctBox;
private CheckBox useOnlyEstimationForWindDirectionBox;
private WindSpeedSteppingConfiguratorPanel windSteppingBox;
private DoubleBox windSteppingMaxDistanceBox;
private CheckBox splitByWindGaugesBox;
public PolarSheetGenerationSettingsDialogComponent(PolarSheetGenerationSettings settings) {
this.settings = settings;
this.stringMessages = StringMessages.INSTANCE;
}
@Override
public Widget getAdditionalWidget(DataEntryDialog<?> dialog) {
VerticalPanel vp = new VerticalPanel();
Grid grid = new Grid(14,2);
grid.setCellPadding(5);
vp.add(grid);
setupGrid(grid, dialog);
return vp;
}
private void setupGrid(Grid grid, DataEntryDialog<?> dialog) {
Label minimumGraphDataSizeLabel = new Label(stringMessages.polarSheetMinimumDataSizePerGraph() + ":");
minimumGraphDataSizeLabel.setTitle(stringMessages.polarSheetMinimumDataSizePerGraphTooltip());
grid.setWidget(0, 0, minimumGraphDataSizeLabel);
minimumGraphDataSizeBox = dialog.createIntegerBox(settings.getMinimumDataCountPerGraph(), 6);
grid.setWidget(0, 1, minimumGraphDataSizeBox);
Label minimumDataSizePerAngleLabel = new Label(stringMessages.polarSheetMinimumDataSizePerAngle() + ":");
minimumDataSizePerAngleLabel.setTitle(stringMessages.polarSheetMinimumDataSizePerAngleTooltip());
grid.setWidget(1, 0, minimumDataSizePerAngleLabel);
minimumDataCountPerAngleBox = dialog.createIntegerBox(settings.getMinimumDataCountPerAngle(), 6);
grid.setWidget(1, 1, minimumDataCountPerAngleBox);
Label minimumWindConfidenceLabel = new Label(stringMessages.polarSheetMinimumWindConfidence() + ":");
minimumWindConfidenceLabel.setTitle(stringMessages.polarSheetMinimumWindConfidenceTooltip());
grid.setWidget(2, 0, minimumWindConfidenceLabel);
minimumWindConfidenceBox = dialog.createDoubleBox(settings.getMinimumWindConfidence(), 6);
grid.setWidget(2, 1, minimumWindConfidenceBox);
Label minimumConfidenceMeasureLabel = new Label(stringMessages.polarSheetMinimumConfidenceMeasure() + ":");
minimumConfidenceMeasureLabel.setTitle(stringMessages.polarSheetMinimumConfidenceMeasureTooltip());
grid.setWidget(3, 0, minimumConfidenceMeasureLabel);
minimumConfidenceMeasureBox = dialog.createDoubleBox(settings.getMinimumConfidenceMeasure(), 6);
grid.setWidget(3, 1, minimumConfidenceMeasureBox);
Label useOnlyWindGaugesForWindSpeedLabel = new Label(stringMessages.polarSheetUseOnlyWindGaugeData() + ":");
useOnlyWindGaugesForWindSpeedLabel.setTitle(stringMessages.polarSheetUseOnlyWindGaugeDataTooltip());
grid.setWidget(4, 0, useOnlyWindGaugesForWindSpeedLabel);
useOnlyWindGaugesForWindSpeedBox = dialog.createCheckbox("");
useOnlyWindGaugesForWindSpeedBox.setValue(settings.useOnlyWindGaugesForWindSpeed());
grid.setWidget(4, 1, useOnlyWindGaugesForWindSpeedBox);
Label splitByWindGaugesLabel = new Label(stringMessages.polarSheetSplitByWindGauges() + ":");
splitByWindGaugesLabel.setTitle(stringMessages.polarSheetSplitByWindGaugesTooltip());
grid.setWidget(5, 0, splitByWindGaugesLabel);
splitByWindGaugesBox = dialog.createCheckbox("");
splitByWindGaugesBox.setValue(settings.splitByWindgauges());
grid.setWidget(5, 1, splitByWindGaugesBox);
Label useOnlyEstimationForWindDirectionLabel = new Label(stringMessages.polarSheetUseOnlyEstimationData() + ":");
useOnlyEstimationForWindDirectionLabel.setTitle(stringMessages.polarSheetUseOnlyEstimationDataTooltip());
grid.setWidget(6, 0, useOnlyEstimationForWindDirectionLabel);
useOnlyEstimationForWindDirectionBox = dialog.createCheckbox("");
useOnlyEstimationForWindDirectionBox.setValue(settings.useOnlyEstimatedForWindDirection());
grid.setWidget(6, 1, useOnlyEstimationForWindDirectionBox);
Label shouldRemoveOutliersLabel = new Label(stringMessages.polarSheetRemoveOutliers() + ":");
shouldRemoveOutliersLabel.setTitle(stringMessages.polarSheetRemoveOutliersTooltip());
grid.setWidget(7, 0, shouldRemoveOutliersLabel);
shouldRemoveOutliersBox = dialog.createCheckbox("");
shouldRemoveOutliersBox.setValue(settings.shouldRemoveOutliers());
grid.setWidget(7, 1, shouldRemoveOutliersBox);
Label outlierRadiusLabel = new Label(stringMessages.polarSheetOutlierDetectionRadius() + ":");
outlierRadiusLabel.setTitle(stringMessages.polarSheetOutlierDetectionRadiusTooltip());
grid.setWidget(8, 0, outlierRadiusLabel);
outlierRadiusBox = dialog.createDoubleBox(settings.getOutlierDetectionNeighborhoodRadius(), 6);
grid.setWidget(8, 1, outlierRadiusBox);
Label outlierNeighborhoodPctLabel = new Label(stringMessages.polarSheetOutlierDetectionMinimumPerc() + ":");
outlierNeighborhoodPctLabel.setTitle(stringMessages.polarSheetOutlierDetectionMinimumPercTooltip());
grid.setWidget(9, 0, outlierNeighborhoodPctLabel);
outlierNeighborhoodPctBox = dialog.createDoubleBox(settings.getOutlierMinimumNeighborhoodPct(), 6);
if (!settings.shouldRemoveOutliers()) {
outlierNeighborhoodPctBox.setEnabled(false);
outlierRadiusBox.setEnabled(false);
}
grid.setWidget(9, 1, outlierNeighborhoodPctBox);
grid.setWidget(10, 0, new Label(stringMessages.polarSheetNumberOfHistogramColumns() + ":"));
numberOfHistogramColumnsBox = dialog.createIntegerBox(settings.getNumberOfHistogramColumns(), 3);
grid.setWidget(10, 1, numberOfHistogramColumnsBox);
grid.setWidget(11, 0, new Label(stringMessages.polarSheetWindSteppingInKnots() + ":"));
windSteppingBox = new WindSpeedSteppingConfiguratorPanel(settings.getWindSpeedStepping());
grid.setWidget(11, 1, windSteppingBox);
Label windSteppingMaxDistanceLabel = new Label(stringMessages.polarSheetWindSteppingMaxDistance() + ":");
windSteppingMaxDistanceLabel.setTitle(stringMessages.polarSheetWindSteppingMaxDistanceTooltip());
grid.setWidget(12, 0, windSteppingMaxDistanceLabel);
windSteppingMaxDistanceBox = dialog.createDoubleBox(settings.getWindSpeedStepping().getMaxDistance(), 6);
grid.setWidget(12, 1, windSteppingMaxDistanceBox);
grid.setWidget(13, 0, new Label(stringMessages.pleaseSeeToolTips()));
setValueChangeListenerForOnlyWindGaugesForSpeed();
setValueChangeListenerForOutlierRemoval();
}
private void setValueChangeListenerForOutlierRemoval() {
shouldRemoveOutliersBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
boolean isChecked = event.getValue();
outlierRadiusBox.setEnabled(isChecked);
outlierNeighborhoodPctBox.setEnabled(isChecked);
}
});
}
private void setValueChangeListenerForOnlyWindGaugesForSpeed() {
useOnlyWindGaugesForWindSpeedBox.addValueChangeHandler(new ValueChangeHandler<Boolean>() {
@Override
public void onValueChange(ValueChangeEvent<Boolean> event) {
if (event.getValue()) {
splitByWindGaugesBox.setEnabled(true);
} else {
splitByWindGaugesBox.setEnabled(false);
splitByWindGaugesBox.setValue(false);
}
}
});
}
@Override
public PolarSheetGenerationSettings getResult() {
return new PolarSheetGenerationSettingsImpl(minimumGraphDataSizeBox.getValue(),
minimumWindConfidenceBox.getValue(), minimumDataCountPerAngleBox.getValue(),
numberOfHistogramColumnsBox.getValue(), minimumConfidenceMeasureBox.getValue(),
useOnlyWindGaugesForWindSpeedBox.getValue(), shouldRemoveOutliersBox.getValue(),
outlierRadiusBox.getValue(), outlierNeighborhoodPctBox.getValue(),
useOnlyEstimationForWindDirectionBox.getValue(), windSteppingBox.getStepping(windSteppingMaxDistanceBox
.getValue()), splitByWindGaugesBox.getValue(), 0 /*TODO*/);
}
@Override
public Validator<PolarSheetGenerationSettings> getValidator() {
return new PolarSheetGenerationSettingsValidator(stringMessages);
}
@Override
public FocusWidget getFocusWidget() {
return minimumGraphDataSizeBox;
}
}
@@ -1,106 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.gwt.client.dialog.DataEntryDialog.Validator;
public class PolarSheetGenerationSettingsValidator implements Validator<PolarSheetGenerationSettings> {
private final StringMessages stringMessages;
public PolarSheetGenerationSettingsValidator(StringMessages stringMessages) {
this.stringMessages = stringMessages;
}
@Override
public String getErrorMessage(PolarSheetGenerationSettings valueToValidate) {
String errorMessage = null;
if (oneOrMoreParametersAreNull(valueToValidate)) {
errorMessage = stringMessages.oneOrMoreParametersAreEmpty();
} else {
errorMessage = validateConfidenceValues(valueToValidate, errorMessage);
errorMessage = validateMinDataValues(valueToValidate, errorMessage);
errorMessage = validateNumberOfColumns(valueToValidate.getNumberOfHistogramColumns(), errorMessage);
if (valueToValidate.shouldRemoveOutliers()) {
errorMessage = validateOutlierRadius(valueToValidate.getOutlierDetectionNeighborhoodRadius(),
errorMessage);
errorMessage = validateOutlierNeighborhoodPct(valueToValidate.getOutlierMinimumNeighborhoodPct(),
errorMessage);
}
}
return errorMessage;
}
private boolean oneOrMoreParametersAreNull(PolarSheetGenerationSettings valueToValidate) {
boolean oneOrMoreParametersAreNull = false;
if (valueToValidate.getMinimumDataCountPerAngle() == null ||
valueToValidate.getMinimumDataCountPerGraph() == null) {
oneOrMoreParametersAreNull = true;
}
return oneOrMoreParametersAreNull;
}
private String validateOutlierNeighborhoodPct(double outlierMinimumNeighborhoodPct, String errorMessage) {
if (outlierMinimumNeighborhoodPct <= 0 && outlierMinimumNeighborhoodPct > 1) {
String outlierMinimumNeighborhoodPctString = stringMessages.outlierMinimumNeighborhoodPctString();
if (errorMessage == null) {
errorMessage = outlierMinimumNeighborhoodPctString;
} else {
errorMessage = errorMessage + "\n" + outlierMinimumNeighborhoodPctString;
}
}
return errorMessage;
}
private String validateOutlierRadius(double outlierDetectionNeighborhoodRadius, String errorMessage) {
if (outlierDetectionNeighborhoodRadius <= 0) {
String outlierRadiusNeedsToBePositiveString = stringMessages.outlierRadiusNeedsToBePositiveString();
if (errorMessage == null) {
errorMessage = outlierRadiusNeedsToBePositiveString;
} else {
errorMessage = errorMessage + "\n" + outlierRadiusNeedsToBePositiveString;
}
}
return errorMessage;
}
private String validateNumberOfColumns(int numberOfHistogramColumns, String errorMessage) {
if (numberOfHistogramColumns < 2) {
String numberOfColumnsAtLeast2 = stringMessages.numberOfColumnsAtLeast2();
if (errorMessage == null) {
errorMessage = numberOfColumnsAtLeast2;
} else {
errorMessage = errorMessage + "\n" + numberOfColumnsAtLeast2;
}
}
return errorMessage;
}
private String validateMinDataValues(PolarSheetGenerationSettings valueToValidate, String errorMessage) {
Integer minDataPerAngle = valueToValidate.getMinimumDataCountPerAngle();
Integer minDataPerGraph = valueToValidate.getMinimumDataCountPerGraph();
if (minDataPerAngle < 0 || minDataPerGraph < 0) {
String minDataValuesNeedToBeAtLeastZero = stringMessages.minDataValuesNeedToBeAtLeastZero();
if (errorMessage == null) {
errorMessage = minDataValuesNeedToBeAtLeastZero;
} else {
errorMessage = errorMessage + "\n" + minDataValuesNeedToBeAtLeastZero;
}
}
return errorMessage;
}
private String validateConfidenceValues(PolarSheetGenerationSettings valueToValidate, String errorMessage) {
double minimumConfidenceMeasure = valueToValidate.getMinimumConfidenceMeasure();
double minimumWindConfidence = valueToValidate.getMinimumWindConfidence();
if (minimumConfidenceMeasure < 0 || minimumConfidenceMeasure > 1 ||
minimumWindConfidence < 0 || minimumWindConfidence > 1) {
errorMessage = stringMessages.confidenceShouldBeBetween();
}
return errorMessage;
}
}
@@ -1,7 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
public interface PolarSheetListChangeListener {
void polarSheetAdded(String name);
}
@@ -1,333 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import org.moxieapps.gwt.highcharts.client.BaseChart;
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.events.PointSelectEventHandler;
import org.moxieapps.gwt.highcharts.client.events.SeriesHideEvent;
import org.moxieapps.gwt.highcharts.client.events.SeriesHideEventHandler;
import org.moxieapps.gwt.highcharts.client.events.SeriesShowEvent;
import org.moxieapps.gwt.highcharts.client.events.SeriesShowEventHandler;
import org.moxieapps.gwt.highcharts.client.plotOptions.LinePlotOptions;
import org.moxieapps.gwt.highcharts.client.plotOptions.SeriesPlotOptions;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.DockLayoutPanel;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.gwt.ui.client.StringMessages;
/**
* Highcharts-driven chart for displaying polar sheets.
*
* @author d054528 Frederik Petersen
*
*/
public class PolarSheetsChartPanel extends DockLayoutPanel {
private StringMessages stringMessages;
private Chart chart;
private Map<String, Series[]> seriesMap;
private Map<Series, String> nameForSeries = new HashMap<Series, String>();
private PolarSheetGenerationSettings settings;
private final Map<String,PolarSheetsData> polarSheetsDataMap;
private final AngleOverDataSizeHistogramPanel angleOverDataSizeHistogramPanel;
private final SeriesPlotOptions seriesPlotOptions;
public PolarSheetsChartPanel(StringMessages stringMessages, AngleOverDataSizeHistogramPanel angleOverDataSizeHistogramPanel) {
super(Unit.PCT);
seriesPlotOptions = new SeriesPlotOptions();
this.stringMessages = stringMessages;
this.angleOverDataSizeHistogramPanel = angleOverDataSizeHistogramPanel;
polarSheetsDataMap = new HashMap<String, PolarSheetsData>();
setSize("100%", "100%");
chart = createPolarSheetChart();
setSeriesShowHandler(createSeriesShowEventHandler());
setSeriesHideHandler(createSeriesHideEventHandler());
seriesMap = new HashMap<String, Series[]>();
add(chart);
}
private void setSeriesHideHandler(SeriesHideEventHandler seriesHideEventHandler) {
chart.setSeriesPlotOptions(seriesPlotOptions.setSeriesHideEventHandler(seriesHideEventHandler));
}
private SeriesShowEventHandler createSeriesShowEventHandler() {
return new SeriesShowEventHandler() {
@Override
public boolean onShow(SeriesShowEvent seriesShowEvent) {
String name = seriesShowEvent.getSeriesName();
angleOverDataSizeHistogramPanel.showSeries(name);
return true;
}
};
}
private SeriesHideEventHandler createSeriesHideEventHandler() {
return new SeriesHideEventHandler() {
@Override
public boolean onHide(SeriesHideEvent seriesHideEvent) {
String name = seriesHideEvent.getSeriesName();
angleOverDataSizeHistogramPanel.hideSeries(name);
return true;
}
};
}
/**
* Creates a polar diagram chart of the Line type.
*/
private Chart createPolarSheetChart() {
Chart polarSheetChart = new Chart().setType(Series.Type.LINE)
.setLinePlotOptions(new LinePlotOptions().setLineWidth(1)).setZoomType(BaseChart.ZoomType.X_AND_Y)
.setPolar(true).setHeight100().setWidth100();
polarSheetChart.setChartTitleText(stringMessages.polarSheetChart());
polarSheetChart.getYAxis().setMin(0);
polarSheetChart.getXAxis().setMin(-179).setMax(180).setTickInterval(45);
polarSheetChart.setOption("/pane/startAngle", 180);
return polarSheetChart;
}
/**
* Adds a new Array of series to the map. Each Array represents one set of generated polar diagrams The array is
* empty and needs to be filled.
*
* @param name
* Unique name of the set of diagrams.
* @param steppingCount
* Number of different wind speeds, the polar sheets are generated for.
*/
private void newSeriesArray(String name, int steppingCount) {
if (!seriesMap.containsKey(name)) {
Series[] seriesPerWindSpeed = new Series[steppingCount];
seriesMap.put(name, seriesPerWindSpeed);
} else {
Window.alert(stringMessages.errorWhileAddingSeriesToChart());
}
}
/**
* Creates a series for a specific windspeed and also creates the unique name needed to identify the series.
*
* @param name Unique name of the set of diagrams.
* @param windSpeedLevel The id of the windspeed level in the windspeed steppings
* @param windSpeed The actual windspeed
*/
private void createSeriesForWindspeed(String name, int windSpeedLevel, String seriesName) {
Series[] seriesPerWindSpeed = seriesMap.get(name);
seriesPerWindSpeed[windSpeedLevel] = chart.createSeries();
seriesPerWindSpeed[windSpeedLevel].setName(seriesName);
nameForSeries.put(seriesPerWindSpeed[windSpeedLevel],seriesName);
chart.addSeries(seriesPerWindSpeed[windSpeedLevel]);
}
/**
* Adds the actual data to the series for each windspeed index. Series array needs to be initialized before calling
* this method.
*/
private void addValuesToSeries(String seriesId, PolarSheetsData result) {
int stepCount = result.getStepping().getRawStepping().length;
boolean oneSeriesSetToVisible = false;
if (seriesMap.containsKey(seriesId)) {
for (int i = 0; i < stepCount; i++) {
if (hasSufficientDataForWindspeed(result.getDataCountPerAngleForWindspeed(i))) {
Point[] points = createPoints(result, i);
if (points != null) {
String actualSeriesName = seriesId + "-" + result.getStepping().getRawStepping()[i];
if (seriesMap.get(seriesId)[i] == null) {
createSeriesForWindspeed(seriesId, i, actualSeriesName);
}
Series series = seriesMap.get(seriesId)[i];
// series.setPoints(result.getAveragedPolarDataByWindSpeed()[i], false);
series.setPoints(points, false);
angleOverDataSizeHistogramPanel.addData(result.getHistogramDataMap().get(i), seriesId,
actualSeriesName, !oneSeriesSetToVisible);
angleOverDataSizeHistogramPanel.redrawChart();
if (!oneSeriesSetToVisible) {
oneSeriesSetToVisible = true;
} else {
series.setVisible(false, false);
}
}
}
}
}
}
/**
* @return true if the minimum amount of datapoints is reached as defined in the settings.
* (for the complete graph)
*/
private boolean hasSufficientDataForWindspeed(Integer[] dataCountPerAngleForWindspeed) {
int sum = 0;
for (int count : dataCountPerAngleForWindspeed) {
sum = sum + count;
}
if (sum >= settings.getMinimumDataCountPerGraph()) {
return true;
}
return false;
}
/**
* Points are created using the polar sheets generation results. Points color and alpha in the graph is calculated
* using statistical measures.
*/
private Point[] createPoints(PolarSheetsData result, int windspeed) {
Point[] points = new Point[360];
List<Integer> dataCountList = Arrays.asList(result.getDataCountPerAngleForWindspeed(windspeed));
Integer max = Collections.max(dataCountList);
if (max < settings.getMinimumDataCountPerAngle()) {
return null;
}
for (int i = 0; i < 360; i++) {
int convertedAngle = i > 180 ? 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()
|| result.getHistogramDataMap().get(windspeed).get(i).getDataCount() < settings
.getMinimumDataCountPerAngle()) {
points[i] = new Point(convertedAngle, 0);
continue;
}
if (points[i] == null) {
points[i] = new Point(convertedAngle, result.getAveragedPolarDataByWindSpeed()[windspeed][i]);
}
}
return points;
}
/**
* Removes the set of series from the chart and the id-series[] map
*
* @param seriesId Identifier for the set of series for one polar sheet generation result.
*/
public void removeSeries(String seriesId) {
if (seriesMap.containsKey(seriesId)) {
Series[] seriesPerWindSpeed = seriesMap.get(seriesId);
for (Series series : seriesPerWindSpeed) {
chart.removeSeries(series);
}
seriesMap.remove(seriesId);
polarSheetsDataMap.remove(seriesId);
angleOverDataSizeHistogramPanel.removeSeries(seriesId);
}
}
/**
* Remove every series from the chart and the id-series[] map.
*/
public void removeAllSeries() {
chart.removeAllSeries();
seriesMap.clear();
polarSheetsDataMap.clear();
angleOverDataSizeHistogramPanel.clear();
}
/**
* Adds the results of a polar sheet generation to the chart panel. Performs all necessary steps including adding
* the set of series to the map and redrawing the chart.
*
* @param id Set of series identifier.
* @param result Generation results.
*/
public void setData(String id, PolarSheetsData result) {
if (id == null) {
stringMessages.errorWhileAddingSeriesToChart();
return;
} else {
if (!seriesMap.containsKey(id)) {
newSeriesArray(id, result.getStepping().getRawStepping().length);
}
addValuesToSeries(id, result);
polarSheetsDataMap.put(id, result);
chart.redraw();
}
}
/**
* Allows setting the point select handler for the chart.
*/
public void setPointSelectHandler(PointSelectEventHandler pointSelectEventHandler) {
chart.setSeriesPlotOptions(seriesPlotOptions.setAllowPointSelect(true).setPointSelectEventHandler(
pointSelectEventHandler));
}
private void setSeriesShowHandler(SeriesShowEventHandler eventHandler) {
chart.setSeriesPlotOptions(seriesPlotOptions.setSeriesShowEventHandler(eventHandler));
}
public Series[] getSeriesPerWindspeedForName(String name) {
return seriesMap.get(name);
}
public String getNameForSeries(Series series) {
return nameForSeries.get(series);
}
@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();
}
public void showLoadingInfo() {
chart.showLoading(stringMessages.generatingPolarSheet());
}
public void hideLoadingInfo() {
chart.hideLoading();
}
public void setSettings(PolarSheetGenerationSettings newSettings) {
settings = newSettings;
}
public List<String> getAllSeriesNames() {
List<String> result = new ArrayList<String>();
for (Entry<String, Series[]> entry : seriesMap.entrySet()) {
result.add(entry.getKey());
}
return result;
}
public Map<String, PolarSheetsData> getPolarSheetsDataMap() {
return polarSheetsDataMap;
}
}
@@ -1,54 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.RootLayoutPanel;
import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.gwt.resources.Highcharts;
public class PolarSheetsEntryPoint extends AbstractSailingEntryPoint implements RegattaRefresher {
private Set<RegattasDisplayer> regattaDisplayers;
@Override
protected void doOnModuleLoad() {
Highcharts.ensureInjectedWithMore();
super.doOnModuleLoad();
regattaDisplayers = new HashSet<RegattasDisplayer>();
createUI();
fillRegattas();
}
private void createUI() {
RootLayoutPanel rootPanel = RootLayoutPanel.get();
PolarSheetsPanel polarSheetsPanel = new PolarSheetsPanel(sailingService, this, getStringMessages(), this);
polarSheetsPanel.addStyleName(PolarSheetsPanel.POLARSHEETS_STYLE);
regattaDisplayers.add(polarSheetsPanel);
rootPanel.add(polarSheetsPanel);
}
@Override
public void fillRegattas() {
sailingService.getRegattas(new AsyncCallback<List<RegattaDTO>>() {
@Override
public void onSuccess(List<RegattaDTO> result) {
for (RegattasDisplayer regattaDisplayer : regattaDisplayers) {
regattaDisplayer.fillRegattas(result);
}
}
@Override
public void onFailure(Throwable caught) {
reportError("Remote Procedure Call getRegattas() - Failure");
}
});
}
}
@@ -1,96 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.RadioButton;
/**
* Shows radio buttons allowing the user to arrange the data displayed in the histogram chart of the polar sheets panel
*
* @author d054528 Frederik Petersen
*
*/
public class PolarSheetsHistogramDataArrangeButtonBar extends HorizontalPanel{
private final RadioButton arrangeByNothingButton;
public PolarSheetsHistogramDataArrangeButtonBar(final PolarSheetsHistogramPanel parentPanel) {
this.setHeight("100%");
this.add(new Label("Arrange by..."));
arrangeByNothingButton = createArrangeByNothingButton(parentPanel);
this.add(arrangeByNothingButton);
RadioButton arrangeByGaugesIdsButton = createArrangeByGaugesIdButton(parentPanel);
this.add(arrangeByGaugesIdsButton);
RadioButton arrangeByDayButton = createArrangeByDayButton(parentPanel);
this.add(arrangeByDayButton);
RadioButton arrangeByDayAndWindGaugeButton = createArrangeByDayAndWindGaugeButton(parentPanel);
this.add(arrangeByDayAndWindGaugeButton);
}
private RadioButton createArrangeByNothingButton(final PolarSheetsHistogramPanel parentPanel) {
RadioButton arrangeByNothingButton = new RadioButton("Arrange","... Nothing");
arrangeByNothingButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (parentPanel.hasData()) {
parentPanel.arrangeByNothing();
}
}
});
arrangeByNothingButton.setValue(true, false);
return arrangeByNothingButton;
}
private RadioButton createArrangeByGaugesIdButton(final PolarSheetsHistogramPanel parentPanel) {
RadioButton arrangeByGaugesIdsButton = new RadioButton("Arrange","... Wind Gauge Ids");
arrangeByGaugesIdsButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (parentPanel.hasData()) {
parentPanel.arrangeByWindGaugeIds();
}
}
});
return arrangeByGaugesIdsButton;
}
private RadioButton createArrangeByDayButton(final PolarSheetsHistogramPanel parentPanel) {
RadioButton arrangeByDayButton = new RadioButton("Arrange","... Day");
arrangeByDayButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (parentPanel.hasData()) {
parentPanel.arrangeByDay();
}
}
});
return arrangeByDayButton;
}
private RadioButton createArrangeByDayAndWindGaugeButton(final PolarSheetsHistogramPanel parentPanel) {
RadioButton arrangeByDayAndWindGaugeButton = new RadioButton("Arrange","... Day And Gauge Ids");
arrangeByDayAndWindGaugeButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (parentPanel.hasData()) {
parentPanel.arrangeByDayAndGaugeIds();
}
}
});
return arrangeByDayAndWindGaugeButton;
}
/**
* Resets the histogram chart to the default (no filter)
*/
public void reset() {
arrangeByNothingButton.setValue(true);
}
}
@@ -1,151 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.Map;
import java.util.Map.Entry;
import org.moxieapps.gwt.highcharts.client.AxisTitle;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.ChartSubtitle;
import org.moxieapps.gwt.highcharts.client.ChartTitle;
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 PolarSheetsHistogramPanel extends DockLayoutPanel {
private final Chart chart;
private final StringMessages stringMessages;
private PolarSheetsHistogramData currentData;
private final PolarSheetsHistogramDataArrangeButtonBar dataArrangeBar;
public PolarSheetsHistogramPanel(StringMessages stringMessages) {
super(Unit.PX);
this.stringMessages = stringMessages;
setSize("100%", "100%");
dataArrangeBar = createDataArrangeButtons();
addSouth(dataArrangeBar, 20);
chart = createHistogramChart();
chart.getElement().setAttribute("align", "top");
add(chart);
}
private PolarSheetsHistogramDataArrangeButtonBar createDataArrangeButtons() {
return new PolarSheetsHistogramDataArrangeButtonBar(this);
}
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.speedInKnots()));
histogramChart.setAreaPlotOptions(new AreaPlotOptions().setStacking(Stacking.NORMAL).setLineColor("#666666")
.setLineWidth(1).setMarker(new Marker().setLineWidth(1).setLineColor("#666666")));
return histogramChart;
}
public void setData(PolarSheetsHistogramData data) {
dataArrangeBar.reset();
currentData = data;
chart.setTitle(new ChartTitle().setText(stringMessages.histogram()), new ChartSubtitle().setText(stringMessages
.angleAndTotalNumberOfDataPointsAndCovAndCm(data.getAngle(), data.getDataCount(),
data.getCoefficiantOfVariation(), data.getConfidenceMeasure())));
arrangeByNothing();
}
private Point[] toPoints(PolarSheetsHistogramData data) {
Number[] xValues = data.getxValues();
Number[] yValues = data.getyValues();
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();
}
public void arrangeByNothing() {
chart.removeAllSeries();
Point[] points = toPoints(currentData);
Series series = chart.createSeries();
series.setPoints(points);
chart.addSeries(series);
}
public void arrangeByWindGaugeIds() {
Map<String, Integer[]> yValueMap = currentData.getYValuesByGaugeIds();
arrangeByYValueMap(yValueMap);
}
public void arrangeByDay() {
Map<String, Integer[]> yValueMap = currentData.getYValuesByDay();
arrangeByYValueMap(yValueMap);
}
public void arrangeByDayAndGaugeIds() {
Map<String, Integer[]> yValueMap = currentData.getYValuesByDayAndGaugeId();
arrangeByYValueMap(yValueMap);
}
private void arrangeByYValueMap(Map<String, Integer[]> yValueMap) {
chart.removeAllSeries();
for (Entry<String, Integer[]> entry : yValueMap.entrySet()) {
Series series = chart.createSeries();
series.setName(entry.getKey());
series.setPoints(toPoints(currentData.getxValues(), entry.getValue()));
chart.addSeries(series);
}
}
public boolean hasData() {
boolean hasData = true;
if (currentData == null) {
hasData = false;
}
return hasData;
}
}
@@ -1,372 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.ArrayList;
import java.util.List;
import org.moxieapps.gwt.highcharts.client.events.PointSelectEvent;
import org.moxieapps.gwt.highcharts.client.events.PointSelectEventHandler;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.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;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
import com.sap.sailing.domain.common.PolarSheetsXYDiagramData;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.WindSpeedStepping;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener;
import com.sap.sailing.gwt.ui.client.RaceSelectionModel;
import com.sap.sailing.gwt.ui.client.RegattasDisplayer;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.shared.components.Component;
import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent;
/**
* A panel containing a list of tracked races (not the ones without gps or wind data), a polar chart and a histogram chart.
* Plus some buttons and settings to allow the user generate polar diagrams for the selected races and view
* histograms about the underlying data distribution of the points in the polar diagrams.
*
* @author d054528 Frederik Petersen
*
*/
public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionChangeListener, RegattasDisplayer, Component<PolarSheetGenerationSettings> {
public static final String POLARSHEETS_STYLE = "polarSheets";
private SailingServiceAsync sailingService;
private PolarSheetsTrackedRacesList polarSheetsTrackedRacesList;
private ErrorReporter errorReporter;
private StringMessages stringMessages;
private RaceSelectionModel raceSelectionProvider;
private PolarSheetsEntryPoint polarSheetsEntryPoint;
private List<RegattaAndRaceIdentifier> selectedRaces;
private PolarSheetsChartPanel chartPanel;
private PolarSheetsHistogramPanel histogramPanel;
private AngleOverDataSizeHistogramPanel angleOverDataSizeHistogramPanel;
private Label polarSheetsGenerationLabel;
private Label dataCountLabel;
private final ScrollPanel leftScrollPanel;
private PolarSheetGenerationSettings settings;
private final List<PolarSheetListChangeListener> polarSheetListChangeListeners = new ArrayList<PolarSheetListChangeListener>();
private final TextBox namingBox;
public PolarSheetsPanel(SailingServiceAsync sailingService, ErrorReporter errorReporter,
final StringMessages stringMessages, PolarSheetsEntryPoint polarSheetsEntryPoint) {
this.polarSheetsEntryPoint = polarSheetsEntryPoint;
this.sailingService = sailingService;
this.errorReporter = errorReporter;
this.stringMessages = stringMessages;
setSize("100%", "100%");
VerticalPanel leftPanel = addFilteredTrackedRacesList();
leftScrollPanel = new ScrollPanel(leftPanel);
addWest(leftScrollPanel, Window.getClientWidth() * 0.3);
polarSheetsGenerationLabel = createPolarSheetGenerationStatusLabel();
leftPanel.add(polarSheetsGenerationLabel);
dataCountLabel = new Label();
leftPanel.add(dataCountLabel);
HorizontalPanel namingContainer = new HorizontalPanel();
Label namingLabel = new Label(stringMessages.sheetName() + ": ");
namingLabel.setTitle(stringMessages.sheetNameTooltip());
namingContainer.add(namingLabel);
namingBox = new TextBox();
namingContainer.add(namingBox);
leftPanel.add(namingContainer);
addBoatClassDiagramArea(leftPanel);
DockLayoutPanel rightPanel = new DockLayoutPanel(Unit.PCT);
angleOverDataSizeHistogramPanel = new AngleOverDataSizeHistogramPanel(stringMessages);
PolarSheetsChartPanel polarSheetsChartPanel = createPolarSheetsChartPanel(angleOverDataSizeHistogramPanel);
DockLayoutPanel polarChartAndControlPanel = new DockLayoutPanel(Unit.PCT);
PolarChartControlPanel polarChartControlPanel = new PolarChartControlPanel(stringMessages,
polarSheetsChartPanel);
polarSheetListChangeListeners.add(polarChartControlPanel);
polarChartAndControlPanel.setWidth("100%");
polarChartAndControlPanel.setHeight("100%");
polarChartAndControlPanel.addWest(polarSheetsChartPanel, 80);
polarChartAndControlPanel.addEast(polarChartControlPanel, 20);
rightPanel.addNorth(polarChartAndControlPanel, 70);
histogramPanel = new PolarSheetsHistogramPanel(stringMessages);
histogramPanel.getElement().setAttribute("align", "top");
//rightPanel.addSouth(histogramPanel, 30);
angleOverDataSizeHistogramPanel.getElement().setAttribute("align", "top");
rightPanel.addSouth(angleOverDataSizeHistogramPanel, 30);
add(rightPanel);
setEventListenersForPolarSheetChart();
PolarSheetGenerationSettings initialSettings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings();
settings = initialSettings;
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);
Button showXYDiagramsButton = new Button(stringMessages.showXYDiagram());
showXYDiagramsButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
sailingService.createXYDiagramForBoatClass(
boatClassListBox.getItemText(boatClassListBox.getSelectedIndex()), new AsyncCallback<PolarSheetsXYDiagramData>() {
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(caught.getLocalizedMessage());
}
@Override
public void onSuccess(PolarSheetsXYDiagramData result) {
PolarSheetsXYDiagramPopupPanel popupPanel = new PolarSheetsXYDiagramPopupPanel(stringMessages, result);
popupPanel.show();
popupPanel.center();
}
});
}
});
leftPanel.add(showXYDiagramsButton);
}
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
public boolean onSelect(PointSelectEvent pointSelectEvent) {
int angle = (int) pointSelectEvent.getXAsLong();
String polarSheetNameWithWind = pointSelectEvent.getSeriesName();
String[] split = polarSheetNameWithWind.split("-");
String polarSheetName = split[0];
int windSpeed = Integer.parseInt(split[1]);
PolarSheetsData currentPolarSheetsData = chartPanel.getPolarSheetsDataMap().get(polarSheetName);
WindSpeedStepping stepping = currentPolarSheetsData.getStepping();
PolarSheetsHistogramData histogramData = currentPolarSheetsData.getHistogramDataMap()
.get(stepping.getLevelIndexForValue(windSpeed)).get(angle);
histogramPanel.setData(histogramData);
return true;
}
};
chartPanel.setPointSelectHandler(pointSelectEventHandler);
}
private Label createPolarSheetGenerationStatusLabel() {
Label polarSheetsGenerationStatusLabel = new Label();
return polarSheetsGenerationStatusLabel;
}
private PolarSheetsChartPanel createPolarSheetsChartPanel(AngleOverDataSizeHistogramPanel angleOverDataSizeHistogramPanel) {
chartPanel = new PolarSheetsChartPanel(stringMessages, angleOverDataSizeHistogramPanel);
return chartPanel;
}
private VerticalPanel addFilteredTrackedRacesList() {
VerticalPanel trackedRacesPanel = new VerticalPanel();
trackedRacesPanel.setWidth("100%");
createPolarSheetsTrackedRacesList();
trackedRacesPanel.add(polarSheetsTrackedRacesList);
return trackedRacesPanel;
}
private void createPolarSheetsTrackedRacesList() {
raceSelectionProvider = new RaceSelectionModel();
ClickHandler polarSheetsGenerationButtonClickHandler = new ClickHandler() {
@Override
public void onClick(ClickEvent arg0) {
startPolarSheetGeneration();
}
};
polarSheetsTrackedRacesList = new PolarSheetsTrackedRacesList(sailingService, errorReporter,
polarSheetsEntryPoint, raceSelectionProvider, stringMessages, true, new RaceFilter(true, true),
polarSheetsGenerationButtonClickHandler);
raceSelectionProvider.addRaceSelectionChangeListener(this);
polarSheetsTrackedRacesList.setSettingsHandler(this);
}
protected void startPolarSheetGeneration() {
// List conversion, to make List serializable
final List<RegattaAndRaceIdentifier> selectedRacesInArrayList = new ArrayList<RegattaAndRaceIdentifier>();
selectedRacesInArrayList.addAll(selectedRaces);
polarSheetsTrackedRacesList.changeGenerationButtonState(false);
setCompletionLabel(stringMessages.generating() + "...");
chartPanel.showLoadingInfo();
sailingService.generatePolarSheetForRaces(selectedRacesInArrayList, settings, namingBox.getValue(),
new AsyncCallback<PolarSheetGenerationResponse>() {
@Override
public void onSuccess(PolarSheetGenerationResponse result) {
String name = createNameForPolarSheet(result);
dataCountLabel.setText(stringMessages.dataCount() + ": " + result.getData().getDataCount());
chartPanel.getPolarSheetsDataMap().put(result.getId(), result.getData());
chartPanel.setData(name, result.getData());
setCompletionLabel(stringMessages.generationFinished());
chartPanel.hideLoadingInfo();
polarSheetsTrackedRacesList.changeGenerationButtonState(true);
}
@Override
public void onFailure(Throwable caught) {
errorReporter.reportError(caught.getLocalizedMessage());
}
});
namingBox.setValue("");
}
private String createNameForPolarSheet(PolarSheetGenerationResponse result) {
String sheetName = result.getName();
int index = 0;
String name = sheetName;
while (chartPanel.getPolarSheetsDataMap().containsKey(name)) {
index++;
name = sheetName + "_" + index;
}
for (PolarSheetListChangeListener listener : polarSheetListChangeListeners) {
listener.polarSheetAdded(name);
}
return name;
}
protected void setCompletionLabel(String string) {
polarSheetsGenerationLabel.setText(string);
}
@Override
public void onRaceSelectionChange(List<RegattaAndRaceIdentifier> selectedRaces) {
this.selectedRaces = selectedRaces;
}
@Override
public void fillRegattas(Iterable<RegattaDTO> regattas) {
polarSheetsTrackedRacesList.fillRegattas(regattas);
}
@Override
protected void onLoad() {
Timer timer = new Timer() {
@Override
public void run() {
setWidgetSize(leftScrollPanel, Window.getClientWidth() * 0.3);
Timer timer = new Timer() {
@Override
public void run() {
chartPanel.onResize();
histogramPanel.onResize();
}
};
timer.schedule(200);
}
};
timer.schedule(600);
super.onLoad();
}
@Override
public boolean hasSettings() {
return true;
}
@Override
public SettingsDialogComponent<PolarSheetGenerationSettings> getSettingsDialogComponent() {
return new PolarSheetGenerationSettingsDialogComponent(settings);
}
@Override
public void updateSettings(PolarSheetGenerationSettings newSettings) {
settings = newSettings;
chartPanel.setSettings(newSettings);
}
@Override
public String getLocalizedShortName() {
return stringMessages.polars();
}
@Override
public Widget getEntryWidget() {
return this;
}
@Override
public String getDependentCssClassName() {
return "polarSheet";
}
}
@@ -1,98 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.List;
import com.google.gwt.core.client.GWT;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.resources.client.ImageResource;
import com.google.gwt.user.client.ui.AbstractImagePrototype;
import com.google.gwt.user.client.ui.Anchor;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.HorizontalPanel;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.gwt.ui.client.RaceSelectionProvider;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.RegattaDTO;
import com.sap.sse.common.Util;
import com.sap.sse.gwt.client.ErrorReporter;
import com.sap.sse.gwt.client.shared.components.ComponentResources;
import com.sap.sse.gwt.client.shared.components.SettingsDialog;
public class PolarSheetsTrackedRacesList extends AbstractFilteredTrackedRacesList {
private static final ComponentResources resources = GWT.create(ComponentResources.class);
private Button btnPolarSheetGeneration;
private Anchor settingsAnchor;
public PolarSheetsTrackedRacesList(SailingServiceAsync sailingService, ErrorReporter errorReporter,
RegattaRefresher regattaRefresher, RaceSelectionProvider raceSelectionProvider,
StringMessages stringMessages, boolean hasMultiSelection, RaceFilter filter,
ClickHandler polarSheetsGenerationButtonClickedHandler) {
super(sailingService, errorReporter, regattaRefresher, raceSelectionProvider, stringMessages,
hasMultiSelection, filter);
btnPolarSheetGeneration.addClickHandler(polarSheetsGenerationButtonClickedHandler);
}
@Override
protected void addControlButtons(HorizontalPanel trackedRacesButtonPanel) {
btnPolarSheetGeneration = new Button(stringMessages.generatePolarSheet());
btnPolarSheetGeneration.ensureDebugId("PolarSheetGeneration");
trackedRacesButtonPanel.add(btnPolarSheetGeneration);
trackedRacesButtonPanel.add(createSettingsLink(stringMessages));
}
public Anchor createSettingsLink(final StringMessages stringMessages) {
ImageResource leaderboardSettingsIcon = resources.darkSettingsIcon();
settingsAnchor = new Anchor(AbstractImagePrototype.create(leaderboardSettingsIcon).getSafeHtml());
settingsAnchor.setTitle(stringMessages.settings());
return settingsAnchor;
}
public void setSettingsHandler(final PolarSheetsPanel overallPanel) {
settingsAnchor.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
SettingsDialog<PolarSheetGenerationSettings> dialog = new SettingsDialog<PolarSheetGenerationSettings>(
overallPanel, stringMessages);
dialog.show();
}
});
}
@Override
protected void makeControlsReactToSelectionChange(List<RaceDTO> selectedRaces) {
if (selectedRaces.isEmpty()) {
btnPolarSheetGeneration.setEnabled(false);
} else {
btnPolarSheetGeneration.setEnabled(true);
}
}
@Override
protected void makeControlsReactToFillRegattas(Iterable<RegattaDTO> regattas) {
if (Util.isEmpty(regattas)) {
btnPolarSheetGeneration.setVisible(false);
} else {
btnPolarSheetGeneration.setVisible(true);
btnPolarSheetGeneration.setEnabled(false);
}
}
/**
* Changes the state of the generation-start button
*/
public void changeGenerationButtonState(boolean enable) {
btnPolarSheetGeneration.setEnabled(enable);
}
@Override
public String getDependentCssClassName() {
return "polarSheetsTrackedRacesList";
}
}
@@ -1,89 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.List;
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 com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.PolarSheetsXYDiagramData;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.common.Util.Pair;
public class PolarSheetsXYDiagramPopupPanel extends DialogBox {
private final Chart chart;
private final StringMessages stringMessages;
public PolarSheetsXYDiagramPopupPanel(StringMessages stringMessages, PolarSheetsXYDiagramData result) {
this.stringMessages = stringMessages;
chart = createChart();
VerticalPanel containerPanel = new VerticalPanel();
this.add(containerPanel);
containerPanel.add(chart);
Button closeButton = new Button(stringMessages.close());
closeButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
PolarSheetsXYDiagramPopupPanel.this.hide(false);
}
});
containerPanel.add(closeButton);
addSpeedAndConfidenceSeriesForTackAndLegType(result, Tack.STARBOARD, LegType.UPWIND, true);
addSpeedAndConfidenceSeriesForTackAndLegType(result, Tack.PORT, LegType.UPWIND, false);
addSpeedAndConfidenceSeriesForTackAndLegType(result, Tack.STARBOARD, LegType.DOWNWIND, false);
addSpeedAndConfidenceSeriesForTackAndLegType(result, Tack.PORT, LegType.DOWNWIND, false);
chart.redraw();
}
private void addSpeedAndConfidenceSeriesForTackAndLegType(PolarSheetsXYDiagramData result, Tack tack, LegType legType, boolean showByDefault) {
Point[] pointsForUpwindStarboardAverageSpeedRegression = toPointArray(result.getPointsForAverageSpeedRegression(tack, legType));
Series speedSeriesRegression = chart.createSeries();
speedSeriesRegression.setName(tack + " " + legType + " Speed - Regression");
speedSeriesRegression.setPoints(pointsForUpwindStarboardAverageSpeedRegression);
chart.addSeries(speedSeriesRegression, false, false);
if (!showByDefault) {
speedSeriesRegression.setVisible(false, false);
}
}
private Point[] toPointArray(List<Pair<Double, Double>> pointsForUpwindStarboardAverageAngle) {
Point[] points = new Point[pointsForUpwindStarboardAverageAngle.size()];
int i = 0;
for (Pair<Double, Double> point : pointsForUpwindStarboardAverageAngle) {
points[i] = new Point(point.getA(), point.getB());
i++;
}
return points;
}
private Chart createChart() {
Chart chart = new Chart().setType(Type.LINE);
chart.setWidth(1000);
chart.setTitle(stringMessages.xyDiagram());
chart.getXAxis().setAxisTitle(new AxisTitle().setText(stringMessages.windSpeed()));
chart.getYAxis(0).setAxisTitle(new AxisTitle().setText(stringMessages.boatSpeed()));
chart.getYAxis(1).setAxisTitle(new AxisTitle().setText(stringMessages.confidence()));
return chart;
}
}
@@ -1,47 +0,0 @@
package com.sap.sailing.gwt.ui.polarsheets;
import com.sap.sailing.domain.common.dto.RaceDTO;
public class RaceFilter {
private Boolean withGpsFixes;
private Boolean withWindData;
public RaceFilter(Boolean withGpsFixes, Boolean withWindData) {
this.withGpsFixes = withGpsFixes;
this.withWindData = withWindData;
}
public boolean compliesToFilter(RaceDTO race) {
if (!compliesToGpsFixFilter(race) || !compliesToWindDataFilter(race)) {
return false;
}
return true;
}
private boolean compliesToGpsFixFilter(RaceDTO race) {
if (withGpsFixes!=null) {
if (race.trackedRace == null || race.trackedRace.hasGPSData != withGpsFixes) {
return false;
}
}
return true;
}
private boolean compliesToWindDataFilter(RaceDTO race) {
if (withGpsFixes!=null) {
if (race.trackedRace == null || race.trackedRace.hasWindData != withWindData) {
return false;
}
}
return true;
}
}
@@ -152,9 +152,6 @@ import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.PolarSheetsXYDiagramData;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.RaceFetcher;
@@ -199,7 +196,6 @@ import com.sap.sailing.domain.common.impl.KilometersPerHourSpeedImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationResponseImpl;
import com.sap.sailing.domain.common.impl.PolarSheetsXYDiagramDataImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
@@ -3949,56 +3945,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
swissTimingJsonUrl));
}
@Override
public PolarSheetGenerationResponse generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces,
PolarSheetGenerationSettings settings, String name) throws Exception {
String id = UUID.randomUUID().toString();
RacingEventService service = getService();
Set<TrackedRace> trackedRaces = new HashSet<TrackedRace>();
for (RegattaAndRaceIdentifier race : selectedRaces) {
trackedRaces.add(service.getTrackedRace(race));
}
if (name == null || name.isEmpty()) {
name = getCommonBoatClass(trackedRaces);
}
PolarDataService polarDataService = service.getPolarDataService();
PolarSheetsData result = polarDataService.generatePolarSheet(trackedRaces, settings, executor);
return new PolarSheetGenerationResponseImpl(id, name, result);
}
@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) {
if (boatClass == null) {
boatClass = race.getRace().getBoatClass();
}
if (!boatClass.getName().toLowerCase().matches(race.getRace().getBoatClass().getName().toLowerCase())) {
return "Mixed";
}
}
return boatClass.getName();
}
protected com.sap.sailing.domain.base.DomainFactory getBaseDomainFactory() {
return baseDomainFactory;
}
@@ -35,7 +35,6 @@
<set-property name="gwt.logging.enabled" value="FALSE"/>
<source path="datamining" />
<source path="polarsheets" />
<source path="polarmining" />
<inherits name='com.sap.sailing.gwt.ui.SourceCommon'/>
@@ -1,42 +0,0 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE module PUBLIC "-//Google Inc.//DTD Google Web Toolkit 2.7.0//EN" "http://gwtproject.org/doctype/2.7.0/gwt-module.dtd">
<module>
<inherits name='com.sap.sailing.gwt.ui.CommonConfigs'/>
<!-- Specify the paths for translatable code -->
<extend-property name="locale" values="de"/>
<!-- Highcharts API -->
<inherits name="com.sap.sse.gwt.Highcharts" />
<!-- Google Maps API -->
<inherits name='com.google.gwt.ajaxloader.AjaxLoader' />
<inherits name='com.google.gwt.maps.Maps' />
<!-- Bootstrap API -->
<inherits name="com.github.gwtbootstrap.BootstrapNoResources" />
<inherits name='com.github.gwtbootstrap.datetimepicker.DatetimepickerNoResources' />
<!-- Other module inherits -->
<inherits name="com.sap.sse.gwt.SSESharedGWT" />
<inherits name="com.sap.sse.SSECommon" />
<inherits name="com.sap.sailing.domain.SailingDomain" />
<inherits name="com.sap.sse.gwt.AdminConsole" />
<inherits name="com.sap.sse.datamining.DataMiningDomain" />
<inherits name="com.sap.sse.security.ui.LoginPanel" />
<source path="polarsheets" />
<inherits name='com.sap.sailing.gwt.ui.SourceCommon' />
<inherits name='com.sap.sailing.gwt.ui.SourceClient' />
<inherits name='com.sap.sailing.gwt.ui.SourceShared' />
<inherits name="com.sap.sailing.gwt.ui.SourceRaceboard"/>
<inherits name="com.sap.sailing.gwt.ui.SourceLeaderboard" />
<inherits name="com.sap.sailing.gwt.ui.SourceAdminConsole" />
<inherits name='com.sap.sse.gwt.CommonControlCSS_Autoinject'/>
<entry-point class="com.sap.sailing.gwt.ui.polarsheets.PolarSheetsEntryPoint" />
</module>
@@ -1,315 +0,0 @@
package com.sap.sailing.polars.test;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.NavigableSet;
import java.util.Set;
import java.util.UUID;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.concurrent.LinkedBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import org.junit.Assert;
import org.junit.Test;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.impl.BoatClassImpl;
import com.sap.sailing.domain.base.impl.BoatImpl;
import com.sap.sailing.domain.base.impl.CompetitorImpl;
import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.TeamImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
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.Position;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
import com.sap.sailing.domain.common.impl.WindImpl;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.impl.WindSpeedSteppingWithMaxDistance;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.test.mock.MockedTrackedRace;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.domain.tracking.impl.MarkPassingByTimeComparator;
import com.sap.sailing.domain.tracking.impl.MarkPassingImpl;
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;
import com.sap.sse.common.Color;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class PolarSheetGenerationTest {
@Test
public void testPolarSheetRawDataGeneration() throws InterruptedException, ExecutionException {
Executor executor = new ThreadPoolExecutor(/* corePoolSize */Runtime.getRuntime().availableProcessors(),
/* maximumPoolSize */Runtime.getRuntime().availableProcessors(),
/* keepAliveTime */60, TimeUnit.SECONDS,
/* workQueue */new LinkedBlockingQueue<Runnable>());
MockTrackedRaceForPolarSheetGeneration race = new MockTrackedRaceForPolarSheetGeneration();
double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. };
WindSpeedSteppingWithMaxDistance windStepping = new WindSpeedSteppingWithMaxDistance(levels, 2.0);
PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 20, 0, false, true, 5,
0.05, false, windStepping, false, 0);
TimePoint startTime = new MillisecondsTimePoint(9);
TimePoint endTime = new MillisecondsTimePoint(80);
// Only used for storing and exporting results in this test case:
PolarFixAggregator resultContainer = new PolarFixAggregator(new SimplePolarFixRaceInterval(
new HashSet<TrackedRace>()),
settings, executor);
BoatClass forelle = new BoatClassImpl("Forelle", true);
Competitor competitor = new CompetitorImpl(UUID.randomUUID(), "Hans Frantz", Color.RED, null, null,
new TeamImpl("SAP", null, null), new BoatImpl("Schnelle Forelle", forelle, "GER000"), /* timeOnTimeFactor */ null, /* timeOnDistanceAllowancePerNauticalMile */ null);
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();
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]);
Assert.assertEquals(6.0, data.getAveragedPolarDataByWindSpeed()[0][35]);
}
@Test
public void testHistogramBuilder() {
double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. };
WindSpeedSteppingWithMaxDistance windStepping = new WindSpeedSteppingWithMaxDistance(levels, 2.0);
PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 10, 0, false, true, 5,
0.05, false, windStepping, false, 0);
PolarSheetHistogramBuilder builder = new PolarSheetHistogramBuilder(settings);
List<DataPointWithOriginInfo> rawData = new ArrayList<DataPointWithOriginInfo>();
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);
Assert.assertTrue(xValues[4].doubleValue() > 1.9 - 0.001 && xValues[4].doubleValue() < 1.9 + 0.001);
Number[] yValues = result.getyValues();
Assert.assertEquals(10, yValues.length);
Assert.assertEquals(3, yValues[0]);
Assert.assertEquals(0, yValues[1]);
Assert.assertEquals(2, yValues[2]);
Assert.assertEquals(2, yValues[9]);
}
@Test
public void testOutlierNeighborhoodAlgorithm() {
PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings();
List<DataPointWithOriginInfo> values = new ArrayList<DataPointWithOriginInfo>();
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 = PolarSheetGenerator.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 0,
new DataPointWithOriginInfoImpl(0.0, "", ""), settings);
Assert.assertTrue(pct < 0.05);
pct = PolarSheetGenerator.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 2,
new DataPointWithOriginInfoImpl(20.0, "", ""), settings);
Assert.assertTrue(pct > 0.05);
}
@SuppressWarnings("serial")
private class MockTrackedRaceForPolarSheetGeneration extends MockedTrackedRace {
@Override
public DynamicGPSFixTrack<Competitor, GPSFixMoving> getTrack(Competitor competitor) {
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = new MockDynamicGPSFixMovinTrackForPolarSheetGeneration<Competitor>(competitor, 0);
//Before start
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(0.009, 0.009), new MillisecondsTimePoint(3), new KnotSpeedWithBearingImpl(12.0, new DegreeBearingImpl(-45.0))));
//Race
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.0, 1.0), new MillisecondsTimePoint(10), new KnotSpeedWithBearingImpl(3.0, new DegreeBearingImpl(-45.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.001, 1.001), new MillisecondsTimePoint(11), new KnotSpeedWithBearingImpl(5.0, new DegreeBearingImpl(-45.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.002, 1.002), new MillisecondsTimePoint(12), new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(-55.0))));
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.003, 1.003), new MillisecondsTimePoint(13), new KnotSpeedWithBearingImpl(6.0, new DegreeBearingImpl(-35.0))));
//After Finish
track.addGPSFix(new MockGPSFixMovingForPolarSheetGeneration(new DegreePosition(1.01, 1.01), new MillisecondsTimePoint(75), new KnotSpeedWithBearingImpl(12.0, new DegreeBearingImpl(-45.0))));
return track;
}
@Override
public Wind getWind(Position p, TimePoint at) {
Wind wind = new WindImpl(p, at, new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(180.0)));
return wind;
}
@Override
public Wind getWind(Position p, TimePoint at, Set<WindSource> windSourcesToExclude) {
return getWind(p, at);
}
@Override
public WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at) {
return new WindWithConfidenceImpl<com.sap.sse.common.Util.Pair<Position, TimePoint>>(new WindImpl(p, at,
new KnotSpeedWithBearingImpl(2.0, new DegreeBearingImpl(180.0))), 0.9,
new com.sap.sse.common.Util.Pair<Position, TimePoint>(p, at), true);
}
@Override
public WindWithConfidence<com.sap.sse.common.Util.Pair<Position, TimePoint>> getWindWithConfidence(Position p, TimePoint at,
Set<WindSource> windSourcesToExclude) {
return getWindWithConfidence(p, at);
}
@Override
public RaceDefinition getRace() {
BoatClass forelle = new BoatClassImpl("Forelle", true);
ArrayList<Waypoint> waypoints = new ArrayList<Waypoint>();
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
waypoints.add(new WaypointImpl(null));
RaceDefinition race = new RaceDefinitionImpl("Forelle1", new CourseImpl("ForelleCourse", waypoints), forelle, new ArrayList<Competitor>());
return race;
}
@Override
public NavigableSet<MarkPassing> getMarkPassings(Competitor competitor) {
NavigableSet<MarkPassing> passings = new ConcurrentSkipListSet<MarkPassing>(
MarkPassingByTimeComparator.INSTANCE);
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(10), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(20), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(30), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(40), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(50), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(60), null, competitor));
passings.add(new MarkPassingImpl(new MillisecondsTimePoint(70), null, competitor));
return passings;
}
@Override
public Set<WindSource> getWindSources(WindSourceType type) {
Set<WindSource> sources = new HashSet<>();
sources.add(new WindSourceImpl(type));
return sources;
}
}
@SuppressWarnings("serial")
private class MockGPSFixMovingForPolarSheetGeneration extends GPSFixMovingImpl {
public MockGPSFixMovingForPolarSheetGeneration(Position position, TimePoint timePoint, SpeedWithBearing speed) {
super(position, timePoint, speed);
}
@Override
public boolean isValidCached() {
return true;
}
}
@SuppressWarnings("serial")
private class MockDynamicGPSFixMovinTrackForPolarSheetGeneration<ItemType> extends DynamicGPSFixMovingTrackImpl<ItemType> {
public MockDynamicGPSFixMovinTrackForPolarSheetGeneration(ItemType trackedItem,
long millisecondsOverWhichToAverage) {
super(trackedItem, millisecondsOverWhichToAverage);
}
@Override
protected boolean isValid(NavigableSet<GPSFixMoving> rawFixes, GPSFixMoving e) {
return true;
}
@Override
public SpeedWithBearing getEstimatedSpeed(TimePoint at) {
return this.getFirstFixAtOrAfter(at).getSpeed();
}
}
}
@@ -7,7 +7,6 @@ Bundle-Vendor: SAP
Bundle-RequiredExecutionEnvironment: JavaSE-1.8
Bundle-ClassPath: .
Export-Package:
com.sap.sailing.polars.data,
com.sap.sailing.polars.mining,
com.sap.sailing.polars.regression,
com.sap.sailing.polars.windestimation
@@ -1,252 +0,0 @@
package com.sap.sailing.polars.aggregation;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.Iterator;
import java.util.List;
import java.util.NavigableSet;
import java.util.Set;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.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.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.polars.data.PolarFix;
import com.sap.sailing.polars.data.impl.PolarFixImpl;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
/**
* 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 PolarFixAggregationWorker implements Runnable {
private final TrackedRace race;
private final PolarFixAggregator polarSheetGenerationWorker;
private TimePoint startTime;
private TimePoint endTime;
private final Competitor competitor;
private boolean noConfidence = false;
private boolean done = false;
private int finishedEarlyAtWaypoint = -1;
private 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;
this.startTime = startTime;
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() {
NavigableSet<MarkPassing> markPassings = race.getMarkPassings(competitor);
if (markPassings == null || markPassings.size() < 1) {
return;
}
if (markPassings.size() < race.getRace().getCourse().getLegs().size() + 1) {
finishedEarlyAtWaypoint = markPassings.size() - 1;
}
}
private void optimizeEndTime() {
NavigableSet<MarkPassing> markPassings = race.getMarkPassings(competitor);
if (markPassings == null || markPassings.size() < 1) {
return;
}
MarkPassing passedFinish = markPassings.last();
TimePoint passedFinishTimePoint = passedFinish.getTimePoint();
if (passedFinishTimePoint.before(endTime)) {
endTime = passedFinishTimePoint;
}
}
private void optimizeStartTime() {
NavigableSet<MarkPassing> markPassings = race.getMarkPassings(competitor);
if (markPassings == null || markPassings.size() < 1) {
noConfidence = true;
return;
}
MarkPassing passedStart = markPassings.first();
TimePoint passedStartTimePoint = passedStart.getTimePoint();
if (passedStartTimePoint.after(startTime)) {
startTime = passedStartTimePoint;
}
}
@Override
public void run() {
if (noConfidence || (finishedEarlyAtWaypoint != -1 && finishedEarlyAtWaypoint < 3)) {
done = true;
} else {
GPSFixTrack<Competitor, GPSFixMoving> track = race.getTrack(competitor);
track.lockForRead();
try {
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);
MarkPassing lastConsideredPassing = markPassings.last();
lastConsideredPassing = markPassings.lower(lastConsideredPassing);
lastConsideredTimePoint = lastConsideredPassing.getTimePoint();
}
boolean reachedEnd = false;
while (fixesIterator.hasNext() && reachedEnd == false) {
GPSFixMoving fix = fixesIterator.next();
if (fix.getTimePoint().after(endTime)
|| (lastConsideredTimePoint != null && fix.getTimePoint().after(lastConsideredTimePoint))) {
reachedEnd = true;
} else {
addFixIfValid(track, fix);
}
}
} finally {
track.unlockAfterRead();
done = true;
}
}
}
private void addFixIfValid(GPSFixTrack<Competitor, GPSFixMoving> track, GPSFixMoving fix) {
if (!track.hasDirectionChange(fix.getTimePoint(), race.getRace().getBoatClass()
.getManeuverDegreeAngleThreshold())) {
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 Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>(
createWindGaugesString(race), race.getWindWithConfidence(fix.getPosition(),
fix.getTimePoint(), collectWindSourcesToIgnoreForSpeed())));
}
} else {
windWithConfidenceList.add(new Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>("Combined",
race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint())));
}
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 PolarFixImpl(fix, race, track, windWithConfidence.getObject(), settings,
windWithSourceIdStringPair.getA());
polarSheetGenerationWorker.addPolarFix(race.getRaceIdentifier(), polarFix);
}
}
}
}
private Collection<? extends Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>> addAllWindsOfWindGaugesSplitOneByOne(
GPSFixMoving fix) {
Iterable<WindSource> windGaugeSources = race.getWindSources(WindSourceType.EXPEDITION);
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>();
for (WindSource windSource : allSources) {
if (windSource != windGaugeSource) {
allSourcesButTheSingleWindGaugeSource.add(windSource);
}
}
windWithConfidenceList.add(new Pair<String, WindWithConfidence<Pair<Position, TimePoint>>>(windGaugeSource
.getId().toString(), race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(),
allSourcesButTheSingleWindGaugeSource)));
}
return windWithConfidenceList;
}
private String createWindGaugesString(TrackedRace race) {
Iterable<WindSource> gaugeWindSources = race.getWindSources(WindSourceType.EXPEDITION);
String gaugeIdString = "";
for (WindSource source : gaugeWindSources) {
if (gaugeIdString.isEmpty()) {
gaugeIdString = "" + source.getId();
} else {
gaugeIdString = gaugeIdString + "+" + source.getId();
}
}
return gaugeIdString;
}
public boolean isDone() {
return done;
}
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;
}
}
@@ -1,168 +0,0 @@
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.tracking.TrackedRace;
import com.sap.sailing.polars.data.PolarFix;
import com.sap.sse.common.TimePoint;
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;
}
}
@@ -1,14 +0,0 @@
package com.sap.sailing.polars.aggregation;
import java.util.Map;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
public interface PolarFixRaceInterval {
Map<TrackedRace, Map<Competitor, Pair<TimePoint, TimePoint>>> getCompetitorAndTimepointsForRace();
}
@@ -1,28 +0,0 @@
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.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
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;
}
}
@@ -1,15 +0,0 @@
package com.sap.sailing.polars.data;
import com.sap.sailing.domain.common.Speed;
public interface BoatAndWindSpeedWithOriginInfo {
public Speed getBoatSpeed();
public Speed getWindSpeed();
public String getWindGaugesIdString();
public String getDayString();
}
@@ -1,11 +0,0 @@
package com.sap.sailing.polars.data;
public interface DataPointWithOriginInfo extends Comparable<DataPointWithOriginInfo> {
public abstract Double getRawData();
public abstract String getWindGaugeIdString();
public abstract String getDayString();
}
@@ -1,18 +0,0 @@
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 getTrueWindAngleDeg();
public abstract String getGaugeIdString();
public abstract String getDayString();
}
@@ -1,41 +0,0 @@
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 {
private Speed boatSpeed;
private Speed windSpeed;
private String windGaugesIdString;
private String dayString;
public BoatAndWindSpeedWithOriginInfoImpl(Speed boatSpeed, Speed windSpeed, String windGaugesIdString,
String dayString) {
this.boatSpeed = boatSpeed;
this.windSpeed = windSpeed;
this.windGaugesIdString = windGaugesIdString;
this.dayString = dayString;
}
@Override
public Speed getBoatSpeed() {
return boatSpeed;
}
@Override
public Speed getWindSpeed() {
return windSpeed;
}
@Override
public String getWindGaugesIdString() {
return windGaugesIdString;
}
@Override
public String getDayString() {
return dayString;
}
}
@@ -1,39 +0,0 @@
package com.sap.sailing.polars.data.impl;
import com.sap.sailing.polars.data.DataPointWithOriginInfo;
public class DataPointWithOriginInfoImpl implements DataPointWithOriginInfo {
private Double rawData;
private String windGaugeIdString;
private 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;
}
@Override
public int compareTo(DataPointWithOriginInfo o) {
return getRawData().compareTo(o.getRawData());
}
}
@@ -1,122 +0,0 @@
package com.sap.sailing.polars.data.impl;
import java.text.SimpleDateFormat;
import java.util.HashSet;
import java.util.Set;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.data.PolarFix;
import com.sap.sse.common.TimePoint;
public class PolarFixImpl implements PolarFix {
private SpeedWithBearing boatSpeed;
private Speed windSpeed;
private double trueWindAngleDeg;
private String gaugeIdString;
private String dayString;
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();
Position position = fix.getPosition();
this.gaugeIdString = gaugeIdString;
dayString = createDayString(race);
this.windSpeed = windSpeed;
Set<WindSource> windSourcesToExclude;
if (settings.useOnlyEstimatedForWindDirection()) {
windSourcesToExclude = collectWindSourcesToIgnoreForBearing(race, /* exclude course based */true);
} else {
windSourcesToExclude = new HashSet<WindSource>();
}
Wind windEstimated = race.getWind(position, fix.getTimePoint(), windSourcesToExclude);
if (windEstimated == null) {
// no estimated wind; try to include course layout
windSourcesToExclude = collectWindSourcesToIgnoreForBearing(race, /* exclude course based */false);
windEstimated = race.getWind(position, fix.getTimePoint(), windSourcesToExclude);
}
if (windEstimated == null) {
windEstimated = windSpeed; // maybe no upwind start; need to default to measured wind speed/direction
}
Bearing windBearing = windEstimated.getFrom();
trueWindAngleDeg = bearing.getDifferenceTo(windBearing).getDegrees();
}
private String createDayString(TrackedRace race) {
TimePoint startTimePoint = race.getStartOfRace();
String resultDateString = "NoDate";
if (startTimePoint != null) {
SimpleDateFormat fmt = new SimpleDateFormat("yyyyMMdd");
resultDateString = fmt.format(startTimePoint.asDate());
}
return resultDateString;
}
public Set<WindSource> collectWindSourcesToIgnoreForBearing(TrackedRace race, boolean excludeCourseBased) {
Set<WindSource> windSourcesToExclude = new HashSet<WindSource>();
Iterable<WindSource> combinedSources = race.getWindSources(WindSourceType.COMBINED);
for (WindSource combinedSource : combinedSources) {
windSourcesToExclude.add(combinedSource);
}
if (excludeCourseBased) {
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;
}
@Override
public SpeedWithBearing getBoatSpeed() {
return boatSpeed;
}
@Override
public Speed getWindSpeed() {
return windSpeed;
}
@Override
public double getTrueWindAngleDeg() {
return trueWindAngleDeg;
}
@Override
public String getGaugeIdString() {
return gaugeIdString;
}
@Override
public String getDayString() {
return dayString;
}
}
@@ -1,273 +0,0 @@
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.WindSpeedSteppingWithMaxDistance;
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 WindSpeedSteppingWithMaxDistance stepping;
private PolarSheetGenerationSettings settings;
private final Set<PolarFix> fixes;
public PolarSheetGenerator(Set<PolarFix> fixes, PolarSheetGenerationSettings settings) {
this.settings = settings;
this.fixes = fixes;
stepping = settings.getWindSpeedStepping();
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 roundedAngleDeg = Math.round(polarFix.getTrueWindAngleDeg());
Speed boatSpeed = polarFix.getBoatSpeed();
Speed windSpeed = polarFix.getWindSpeed();
int angleDeg = (int) roundedAngleDeg;
if (angleDeg < 0) {
angleDeg = (360 + angleDeg);
}
BoatAndWindSpeedWithOriginInfo speeds = new BoatAndWindSpeedWithOriginInfoImpl(boatSpeed, windSpeed,
polarFix.getGaugeIdString(), polarFix.getDayString());
polarData.get(angleDeg).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 WindSpeedSteppingWithMaxDistance getStepping() {
return stepping;
}
}
@@ -1,81 +0,0 @@
package com.sap.sailing.polars.generation;
import java.util.Collections;
import java.util.HashMap;
import java.util.List;
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 {
private PolarSheetGenerationSettings settings;
public PolarSheetHistogramBuilder(PolarSheetGenerationSettings settings) {
this.settings = settings;
}
public PolarSheetsHistogramData build(List<DataPointWithOriginInfo> rawData, int angleIndex,
double coefficiantOfVariation) {
DataPointWithOriginInfo minPoint = Collections.min(rawData);
DataPointWithOriginInfo maxPoint = Collections.max(rawData);
Double min = minPoint.getRawData();
Double max = maxPoint.getRawData();
int numberOfColumns = settings.getNumberOfHistogramColumns();
double range = (max - min) / numberOfColumns;
Double[] xValues = new Double[numberOfColumns];
for (int u = 0; u < numberOfColumns; u++) {
xValues[u] = min + u * range + (0.5 * range);
}
Map<String, Integer[]> yValuesByGaugeIds = new HashMap<String, Integer[]>();
Map<String, Integer[]> yValuesByDay = new HashMap<String, Integer[]>();
Map<String, Integer[]> yValuesByDayAndGaugeId = new HashMap<String, Integer[]>();
Integer[] yValues = new Integer[numberOfColumns];
for (int i = 0; i < yValues.length; i++) {
yValues[i] = 0;
}
for (DataPointWithOriginInfo dataPoint : rawData) {
double notRoundedYet = (dataPoint.getRawData() - min) / range;
int u = (int) notRoundedYet;
if (u == numberOfColumns) {
// For max value
u = numberOfColumns - 1;
}
yValues[u]++;
String gaugeIdsString = dataPoint.getWindGaugeIdString();
createHistogramDataForArrangeOption(numberOfColumns, yValuesByGaugeIds, u, gaugeIdsString);
String dayString = dataPoint.getDayString();
createHistogramDataForArrangeOption(numberOfColumns, yValuesByDay, u, dayString);
String dayAndGaugeIdString = dayString + "-" + gaugeIdsString;
createHistogramDataForArrangeOption(numberOfColumns, yValuesByDayAndGaugeId, u, dayAndGaugeIdString);
}
PolarSheetsHistogramData histogramData = new PolarSheetsHistogramDataImpl(angleIndex, xValues, yValues,
yValuesByGaugeIds, yValuesByDay, yValuesByDayAndGaugeId, rawData.size(), coefficiantOfVariation);
return histogramData;
}
private void createHistogramDataForArrangeOption(int numberOfColumns, Map<String, Integer[]> arrangerMap, int u,
String arrangerString) {
if (!arrangerMap.containsKey(arrangerString)) {
Integer[] yValuesForArrangerString = new Integer[numberOfColumns];
for (int i = 0; i < yValuesForArrangerString.length; i++) {
yValuesForArrangerString[i] = 0;
}
arrangerMap.put(arrangerString, yValuesForArrangerString);
}
Integer[] yValuesForOneSpecificArrangerString = arrangerMap.get(arrangerString);
yValuesForOneSpecificArrangerString[u]++;
}
}
@@ -9,8 +9,6 @@ import java.net.MalformedURLException;
import java.util.HashSet;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ExecutionException;
import java.util.concurrent.Executor;
import java.util.logging.Logger;
import org.apache.commons.math.analysis.polynomials.PolynomialFunction;
@@ -25,7 +23,6 @@ import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.ManeuverType;
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.common.confidence.BearingWithConfidence;
@@ -39,10 +36,6 @@ import com.sap.sailing.domain.polars.PolarDataService;
import com.sap.sailing.domain.polars.PolarsChangedListener;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.polars.PolarDataOperation;
import com.sap.sailing.polars.aggregation.PolarFixAggregator;
import com.sap.sailing.polars.aggregation.SimplePolarFixRaceInterval;
import com.sap.sailing.polars.data.PolarFix;
import com.sap.sailing.polars.generation.PolarSheetGenerator;
import com.sap.sailing.polars.mining.BearingClusterGroup;
import com.sap.sailing.polars.mining.CubicRegressionPerCourseProcessor;
import com.sap.sailing.polars.mining.PolarDataMiner;
@@ -139,23 +132,6 @@ public class PolarDataServiceImpl implements PolarDataService,
return averageSpeedAndCourseOverGround;
}
@Override
public PolarSheetsData generatePolarSheet(Set<TrackedRace> trackedRaces, PolarSheetGenerationSettings settings,
Executor executor) throws InterruptedException, ExecutionException {
Set<PolarFix> fixes;
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 PolarSheetsData getPolarSheetForBoatClass(BoatClass boatClass) {
return polarDataMiner.createFullSheetForBoatClass(boatClass);
}
@Override
public Set<BoatClass> getAllBoatClassesWithPolarSheetsAvailable() {
return polarDataMiner.getAvailableBoatClasses();
@@ -1,16 +0,0 @@
package com.sap.sailing.polars.mining;
import com.sap.sailing.domain.common.Speed;
import com.sap.sse.datamining.annotations.Dimension;
import com.sap.sse.datamining.data.Cluster;
public interface MovingAveragePolarClusterKey extends LegTypePolarClusterKey {
/**
*
* @return wind speed cluster
*/
@Dimension(messageKey = "windSpeed")
Cluster<Speed> getWindSpeedCluster();
}
@@ -1,20 +0,0 @@
package com.sap.sailing.polars.mining;
import java.io.Serializable;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.data.ClusterGroup;
import com.sap.sse.datamining.impl.components.GroupedDataEntry;
public interface MovingAverageProcessor extends Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>, Void>,
Serializable {
SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass, Speed windSpeed,
LegType legType) throws NotEnoughDataHasBeenAddedException;
ClusterGroup<Speed> getSpeedCluster();
}
@@ -1,223 +0,0 @@
package com.sap.sailing.polars.mining;
import java.util.HashMap;
import java.util.Map;
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.SpeedWithBearingWithConfidenceImpl;
import com.sap.sailing.domain.base.impl.SpeedWithConfidenceImpl;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.LegType;
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.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sailing.polars.regression.MovingAverageBoatSpeedEstimator;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util.Pair;
import com.sap.sse.datamining.components.AdditionalResultDataBuilder;
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 MovingAverageProcessorImpl implements MovingAverageProcessor {
private static final long serialVersionUID = 9016357546663534562L;
private static final Logger logger = Logger.getLogger(MovingAverageProcessorImpl.class.getName());
private final Map<GroupKey, MovingAverageBoatSpeedEstimator> boatSpeedEstimators = new HashMap<>();
private final Map<GroupKey, AverageAngleContainer> averageAngleContainers = new HashMap<>();
private final ClusterGroup<Speed> speedClusterGroup;
public MovingAverageProcessorImpl(ClusterGroup<Speed> speedClusterGroup) {
this.speedClusterGroup = speedClusterGroup;
}
@Override
public boolean canProcessElements() {
// TODO Auto-generated method stub
return true;
}
@Override
public void processElement(GroupedDataEntry<GPSFixMovingWithPolarContext> element) {
GPSFixMovingWithPolarContext fix = element.getDataEntry();
if (fix.getLegType() == LegType.UPWIND || fix.getLegType() == LegType.DOWNWIND) {
GroupKey key = element.getKey();
MovingAverageBoatSpeedEstimator boatSpeedEstimator;
synchronized (boatSpeedEstimators) {
boatSpeedEstimator = boatSpeedEstimators.get(key);
if (boatSpeedEstimator == null) {
boatSpeedEstimator = new MovingAverageBoatSpeedEstimator();
boatSpeedEstimators.put(key, boatSpeedEstimator);
}
}
AverageAngleContainer averageAngleContainer;
synchronized (averageAngleContainers) {
averageAngleContainer = averageAngleContainers.get(key);
if (averageAngleContainer == null) {
averageAngleContainer = new AverageAngleContainer();
averageAngleContainers.put(key, averageAngleContainer);
}
}
BearingWithConfidence<Void> angleToTheWind = fix.getAbsoluteAngleToTheWind();
WindWithConfidence<Pair<Position, TimePoint>> windSpeed = fix.getWind();
SpeedWithBearingWithConfidence<TimePoint> boatSpeedWithConfidence = fix.getBoatSpeed();
WindWithConfidence<Pair<Position, TimePoint>> windWithConfidenceForSpeed = windSpeed;
double confidenceForWindSpeed = windWithConfidenceForSpeed.getConfidence();
double confidenceForWindBearing = angleToTheWind.getConfidence();
double confidenceForBoatSpeed = boatSpeedWithConfidence.getConfidence();
double averagedConfidence = (confidenceForBoatSpeed + confidenceForWindBearing + confidenceForWindSpeed) / 3;
averageAngleContainer.addFix(angleToTheWind.getObject().getDegrees());
boatSpeedEstimator.addData(boatSpeedWithConfidence.getObject(), averagedConfidence);
}
}
@Override
public void finish() throws InterruptedException {
// Nothing to do here
}
@Override
public boolean isFinished() {
return false;
}
@Override
public void abort() {
// TODO Auto-generated method stub
}
@Override
public boolean isAborted() {
// TODO Auto-generated method stub
return false;
}
@Override
public AdditionalResultDataBuilder getAdditionalResultData(AdditionalResultDataBuilder additionalDataBuilder) {
// TODO Auto-generated method stub
return null;
}
public static class SpeedWithConfidenceAndDataCount {
private final SpeedWithConfidence<Void> speedWithConfidence;
private final int dataCount;
protected SpeedWithConfidenceAndDataCount(SpeedWithConfidence<Void> speedWithConfidence, int dataCount) {
super();
this.speedWithConfidence = speedWithConfidence;
this.dataCount = dataCount;
}
public SpeedWithConfidence<Void> getSpeedWithConfidence() {
return speedWithConfidence;
}
public int getDataCount() {
return dataCount;
}
}
private SpeedWithConfidenceAndDataCount estimateBoatSpeed(GroupKey key) throws NotEnoughDataHasBeenAddedException {
MovingAverageBoatSpeedEstimator boatSpeedEstimator = boatSpeedEstimators.get(key);
if (boatSpeedEstimator == null) {
throw new NotEnoughDataHasBeenAddedException();
}
Speed speedWithoutConfidence = boatSpeedEstimator.estimateSpeed();
final int dataCount = boatSpeedEstimator.getDataCount();
double confidence = boatSpeedEstimator.getConfidence();
return new SpeedWithConfidenceAndDataCount(new SpeedWithConfidenceImpl<Void>(speedWithoutConfidence,
confidence, null), dataCount);
}
private Bearing estimateAngle(GroupKey key) throws NotEnoughDataHasBeenAddedException {
AverageAngleContainer averageAngleContainer = averageAngleContainers.get(key);
if (averageAngleContainer == null) {
throw new NotEnoughDataHasBeenAddedException();
}
return new DegreeBearingImpl(averageAngleContainer.getAverageAngleDeg());
}
@Override
public void onFailure(Throwable failure) {
logger.severe("Polar Data Mining Pipe failed.");
throw new RuntimeException("Polar Data Miner failed.", failure);
}
@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;
}
@Override
public SpeedWithBearingWithConfidence<Void> getAverageSpeedAndCourseOverGround(BoatClass boatClass,
Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException {
SpeedWithBearingWithConfidence<Void> result = estimateSpeedAndAngle(boatClass, windSpeed, legType);
return result;
}
private SpeedWithBearingWithConfidence<Void> estimateSpeedAndAngle(BoatClass boatClass, Speed windSpeed,
LegType legType) throws NotEnoughDataHasBeenAddedException {
GroupKey key = createGroupKey(boatClass, windSpeed, legType);
Bearing angleDiffTrueWindToBoat = estimateAngle(key);
SpeedWithConfidence<Void> estimatedSpeed = estimateBoatSpeed(key).getSpeedWithConfidence();
SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(estimatedSpeed.getObject().getKnots(),
angleDiffTrueWindToBoat);
return new SpeedWithBearingWithConfidenceImpl<Void>(speedWithBearing, estimatedSpeed.getConfidence(), null);
}
private GroupKey createGroupKey(final BoatClass boatClass, final Speed windSpeed, final LegType legType) {
MovingAveragePolarClusterKey key = new MovingAveragePolarClusterKey() {
@Override
public BoatClass getBoatClass() {
return boatClass;
}
@Override
public Cluster<Speed> getWindSpeedCluster() {
return speedClusterGroup.getClusterFor(windSpeed);
}
@Override
public LegType getLegType() {
return legType;
}
};
GroupKey compoundKey;
try {
compoundKey = GroupKeyFactory.createNestingCompoundKeyFor(key, PolarDataDimensionCollectionFactory
.getMovingAverageClusterKeyDimensions().iterator());
} catch (NoSuchMethodException e) {
e.printStackTrace();
throw new RuntimeException(e);
}
return compoundKey;
}
@Override
public ClusterGroup<Speed> getSpeedCluster() {
return speedClusterGroup;
}
}
@@ -12,24 +12,10 @@ import com.sap.sse.datamining.functions.Function;
public class PolarDataDimensionCollectionFactory {
public static Collection<Function<?>> getMovingAverageClusterKeyDimensions() throws NoSuchMethodException {
Collection<Function<?>> dimensions = new ArrayList<>();
FunctionFactory functionFactory = new FunctionFactory();
addTackAndLegTypeDimensions(dimensions, functionFactory);
addPolarBaseDimension(dimensions, functionFactory);
Function<WindSpeedLevel> windSpeedFunction = functionFactory
.createMethodWrappingFunction(MovingAveragePolarClusterKey.class.getMethod("getWindSpeedCluster",
new Class<?>[0]));
dimensions.add(windSpeedFunction);
return dimensions;
}
private static void addTackAndLegTypeDimensions(Collection<Function<?>> dimensions, FunctionFactory functionFactory)
throws NoSuchMethodException {
Function<LegType> legTypeFunction = functionFactory
.createMethodWrappingFunction(MovingAveragePolarClusterKey.class.getMethod("getLegType",
.createMethodWrappingFunction(LegTypePolarClusterKey.class.getMethod("getLegType",
new Class<?>[0]));
dimensions.add(legTypeFunction);
}
@@ -37,7 +23,7 @@ public class PolarDataDimensionCollectionFactory {
private static void addPolarBaseDimension(Collection<Function<?>> dimensions, FunctionFactory functionFactory)
throws NoSuchMethodException {
Function<BoatClass> boatClassFunction = functionFactory
.createMethodWrappingFunction(MovingAveragePolarClusterKey.class.getMethod("getBoatClass",
.createMethodWrappingFunction(BasePolarClusterKey.class.getMethod("getBoatClass",
new Class<?>[0]));
dimensions.add(boatClassFunction);
}
@@ -1,88 +0,0 @@
package com.sap.sailing.polars.regression;
import java.io.ObjectInputStream;
import java.io.Serializable;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.polars.mining.MovingAverageProcessorImpl;
import com.sap.sse.concurrent.LockUtil;
import com.sap.sse.concurrent.NamedReentrantReadWriteLock;
/**
* Supplies incremental arithmetic mean for confidence and speed.
* <p>
*
* {@link #estimateSpeed(double, double)} returns the average of the speed.
* <p>
*
* Note that this class should only be used for a small wind interval on a polar sheet. The
* {@link MovingAverageProcessorImpl} is one example for that. It has one instance of the
* {@link MovingAverageBoatSpeedEstimator} for each wind boatclass, speed level and rounded angle combination.
*
* @author Frederik Petersen (D054528)
*
*/
public class MovingAverageBoatSpeedEstimator implements Serializable {
private static final long serialVersionUID = -254184914347332658L;
private double speedSumInKnots = 0;
private transient NamedReentrantReadWriteLock lock = createLock();
private double confidenceSum = 0;
private int dataCount = 0;
public Speed estimateSpeed() throws NotEnoughDataHasBeenAddedException {
LockUtil.lockForRead(lock);
if (dataCount < 1) {
throw new NotEnoughDataHasBeenAddedException();
}
try {
return new KnotSpeedImpl(speedSumInKnots / dataCount);
} finally {
LockUtil.unlockAfterRead(lock);
}
}
public void addData(Speed boatSpeed, double confidence) {
LockUtil.lockForWrite(lock);
try {
speedSumInKnots = speedSumInKnots + boatSpeed.getKnots();
dataCount++;
confidenceSum = confidenceSum + confidence;
} finally {
LockUtil.unlockAfterWrite(lock);
}
}
public int getDataCount() {
LockUtil.lockForRead(lock);
try {
return dataCount;
} finally {
LockUtil.unlockAfterRead(lock);
}
}
public double getConfidence() {
LockUtil.lockForRead(lock);
try {
return confidenceSum / dataCount;
} finally {
LockUtil.unlockAfterRead(lock);
}
}
private void readObject(ObjectInputStream ois) {
lock = createLock();
}
private NamedReentrantReadWriteLock createLock() {
return new NamedReentrantReadWriteLock(getClass().getName(), true);
}
}