mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-25 13:33:59 +00:00
Removed old PolarSheets.html and its backend functionality
replaced by datamining ui approach to polar generation. see wiki soon...
This commit is contained in:
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);
|
||||
|
||||
/**
|
||||
*
|
||||
|
||||
-239
@@ -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();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
}
|
||||
@@ -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 "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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 "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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 "${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work" -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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 "${project_loc:com.sap.sailing.gwt.ui}/.tmp/gwt-work" -war "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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 "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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 "${project_loc:com.sap.sailing.gwt.ui}" -noserver -remoteUI "${gwt_remote_ui_server_port}:${unique_id}" -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,\
|
||||
|
||||
@@ -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>
|
||||
|
||||
-11
@@ -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 */
|
||||
|
||||
-11
@@ -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,
|
||||
|
||||
-1
@@ -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
-1
@@ -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;
|
||||
-32
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
}
|
||||
-160
@@ -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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
-117
@@ -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);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
-178
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-106
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-7
@@ -1,7 +0,0 @@
|
||||
package com.sap.sailing.gwt.ui.polarsheets;
|
||||
|
||||
public interface PolarSheetListChangeListener {
|
||||
|
||||
void polarSheetAdded(String name);
|
||||
|
||||
}
|
||||
-333
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-54
@@ -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");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
-96
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
-151
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
}
|
||||
-372
@@ -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";
|
||||
}
|
||||
}
|
||||
-98
@@ -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";
|
||||
}
|
||||
|
||||
}
|
||||
-89
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-47
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-54
@@ -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;
|
||||
}
|
||||
|
||||
-1
@@ -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'/>
|
||||
|
||||
-42
@@ -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>
|
||||
-315
@@ -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
|
||||
|
||||
-252
@@ -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;
|
||||
}
|
||||
}
|
||||
-168
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-14
@@ -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();
|
||||
|
||||
}
|
||||
-28
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-15
@@ -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();
|
||||
|
||||
}
|
||||
-11
@@ -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();
|
||||
|
||||
}
|
||||
-41
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-39
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-273
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
-81
@@ -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();
|
||||
|
||||
-16
@@ -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();
|
||||
|
||||
}
|
||||
-20
@@ -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();
|
||||
}
|
||||
-223
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+2
-16
@@ -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);
|
||||
}
|
||||
|
||||
-88
@@ -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);
|
||||
}
|
||||
|
||||
}
|
||||
Reference in new issue
Block a user