Merge remote-tracking branch 'server/polar-api' into polar-api

This commit is contained in:
Axel Uhl committed 2014-12-03 13:42:37 +01:00
commit 928cf87ea2
33 files changed
+419 -95

No files matched your search

@@ -0,0 +1,18 @@
package com.sap.sailing.domain.common;
import java.io.Serializable;
import java.util.List;
import com.sap.sse.common.Util.Pair;
public interface PolarSheetsXYDiagramData extends Serializable {
List<Pair<Double, Double>> getPointsForUpwindStarboardAverageSpeed();
List<Pair<Double, Double>> getPointsForUpwindStarboardAverageAngle();
List<Pair<Double, Double>> getPointsForUpwindStarboardAverageAngleMovingAverage();
List<Pair<Double, Double>> getPointsForUpwindStarboardAverageSpeedMovingAverage();
}
@@ -6,9 +6,9 @@ public interface WindStepping extends Serializable{
public abstract int getLevelIndexForValue(double speed); public abstract int getLevelIndexForValue(double speed);
public abstract int getSteppedValueForValue(double speed); public abstract Double getSteppedValueForValue(double speed);
Integer[] getRawStepping(); Double[] getRawStepping();
public abstract int getLevelIndexFloorForValue(double speed); public abstract int getLevelIndexFloorForValue(double speed);
@@ -1,16 +1,31 @@
package com.sap.sailing.domain.common.impl; package com.sap.sailing.domain.common.impl;
import java.util.ArrayList;
import java.util.List;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
public class PolarSheetGenerationSettingsImpl implements PolarSheetGenerationSettings { public class PolarSheetGenerationSettingsImpl implements PolarSheetGenerationSettings {
public static PolarSheetGenerationSettings createStandardPolarSettings() { public static PolarSheetGenerationSettings createStandardPolarSettings() {
Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 }; Double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. };
WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.5); WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.5);
return new PolarSheetGenerationSettingsImpl(50, 0.1, 20, 20, 0.1, true, true, 2, 0.05, true, windStepping, return new PolarSheetGenerationSettingsImpl(50, 0.1, 20, 20, 0.1, true, true, 2, 0.05, true, windStepping,
false); false);
} }
public static PolarSheetGenerationSettings createBackendPolarSettings() {
List<Double> levelList = new ArrayList<Double>();
for (double levelValue = 0.5; levelValue < 35; levelValue = levelValue + 0.5) {
levelList.add(levelValue);
}
Double[] levels = levelList.toArray(new Double[levelList.size()]);
WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 0.5);
return new PolarSheetGenerationSettingsImpl(50, 0.1, 20, 20, 0.1, true, true, 2, 0.05, true, windStepping,
false);
}
private static final long serialVersionUID = 2731616509404813790L; private static final long serialVersionUID = 2731616509404813790L;
private Integer minimumDataCountPerGraph; private Integer minimumDataCountPerGraph;
private double minimumWindConfidence; private double minimumWindConfidence;
@@ -0,0 +1,49 @@
package com.sap.sailing.domain.common.impl;
import java.util.List;
import com.sap.sailing.domain.common.PolarSheetsXYDiagramData;
import com.sap.sse.common.Util.Pair;
public class PolarSheetsXYDiagramDataImpl implements PolarSheetsXYDiagramData {
private static final long serialVersionUID = 778667444303004468L;
PolarSheetsXYDiagramDataImpl() {}
private List<Pair<Double, Double>> pointsForUpwindStarboardAverageAngle;
private List<Pair<Double, Double>> pointsForUpwindStarboardAverageSpeed;
private List<Pair<Double, Double>> pointsForUpwindStarboardAverageAngleMovingAverage;
private List<Pair<Double, Double>> pointsForUpwindStarboardAverageSpeedMovingAverage;
public PolarSheetsXYDiagramDataImpl(List<Pair<Double, Double>> pointsForUpwindStarboardAverageAngle,
List<Pair<Double, Double>> pointsForUpwindStarboardAverageSpeed,
List<Pair<Double, Double>> pointsForUpwindStarboardAverageAngleMovingAverage,
List<Pair<Double, Double>> pointsForUpwindStarboardAverageSpeedMovingAverage) {
this.pointsForUpwindStarboardAverageAngle = pointsForUpwindStarboardAverageAngle;
this.pointsForUpwindStarboardAverageSpeed = pointsForUpwindStarboardAverageSpeed;
this.pointsForUpwindStarboardAverageAngleMovingAverage = pointsForUpwindStarboardAverageAngleMovingAverage;
this.pointsForUpwindStarboardAverageSpeedMovingAverage = pointsForUpwindStarboardAverageSpeedMovingAverage;
}
@Override
public List<Pair<Double, Double>> getPointsForUpwindStarboardAverageAngle() {
return pointsForUpwindStarboardAverageAngle;
}
@Override
public List<Pair<Double, Double>> getPointsForUpwindStarboardAverageSpeed() {
return pointsForUpwindStarboardAverageSpeed;
}
@Override
public List<Pair<Double, Double>> getPointsForUpwindStarboardAverageAngleMovingAverage() {
return pointsForUpwindStarboardAverageAngleMovingAverage;
}
@Override
public List<Pair<Double, Double>> getPointsForUpwindStarboardAverageSpeedMovingAverage() {
return pointsForUpwindStarboardAverageSpeedMovingAverage;
}
}
@@ -11,9 +11,9 @@ public class WindSteppingImpl implements WindStepping {
protected WindSteppingImpl() {}; protected WindSteppingImpl() {};
private static final long serialVersionUID = 2215693490331489508L; private static final long serialVersionUID = 2215693490331489508L;
protected Integer[] levels; protected Double[] levels;
public WindSteppingImpl(Integer[] levels) { public WindSteppingImpl(Double[] levels) {
this.levels = levels; this.levels = levels;
} }
@@ -24,7 +24,7 @@ public class WindSteppingImpl implements WindStepping {
@Override @Override
public int getLevelIndexForValue(double speed) { public int getLevelIndexForValue(double speed) {
for (int i = 0; i < levels.length - 1; i++) { for (int i = 0; i < levels.length - 1; i++) {
if (speed < levels[i] + ((levels[i+1] - levels[i]) / 2)) { if (speed < levels[i] + ((levels[i+1] - levels[i]) / 2.)) {
return i; return i;
} }
} }
@@ -32,14 +32,14 @@ public class WindSteppingImpl implements WindStepping {
} }
@Override @Override
public Integer[] getRawStepping() { public Double[] getRawStepping() {
return levels; return levels;
} }
@Override @Override
public int getSteppedValueForValue(double speed) { public Double getSteppedValueForValue(double speed) {
int levelIndex = getLevelIndexForValue(speed); int levelIndex = getLevelIndexForValue(speed);
int result = - 1; Double result = -1.0;
if (levelIndex >= 0) { if (levelIndex >= 0) {
result = levels[levelIndex]; result = levels[levelIndex];
} }
@@ -11,7 +11,7 @@ public class WindSteppingWithMaxDistance extends WindSteppingImpl {
super(); super();
}; };
public WindSteppingWithMaxDistance(Integer[] levels, double maxDistance) { public WindSteppingWithMaxDistance(Double[] levels, double maxDistance) {
super(levels); super(levels);
this.maxDistance = maxDistance; this.maxDistance = maxDistance;
} }
@@ -20,7 +20,7 @@ public class WindSteppingWithMaxDistance extends WindSteppingImpl {
public int getLevelIndexForValue(double speed) { public int getLevelIndexForValue(double speed) {
int result = -1; int result = -1;
for (int i = 0; i < levels.length - 1; i++) { for (int i = 0; i < levels.length - 1; i++) {
double threshold = levels[i] + ((levels[i+1] - levels[i]) / 2); double threshold = levels[i] + ((levels[i+1] - levels[i]) / 2.);
if (speed < threshold) { if (speed < threshold) {
if (threshold - speed <= maxDistance * 2) { if (threshold - speed <= maxDistance * 2) {
result = i; result = i;
@@ -12,26 +12,26 @@ public class WindSteppingTest {
@Test @Test
public void testStepping() { public void testStepping() {
Integer[] levels = {2,4}; Double[] levels = {2.,4.};
WindStepping stepping = new WindSteppingImpl(levels); WindStepping stepping = new WindSteppingImpl(levels);
Assert.assertEquals(0, stepping.getLevelIndexForValue(1)); Assert.assertEquals(0, stepping.getLevelIndexForValue(1));
Assert.assertEquals(1, stepping.getLevelIndexForValue(5)); Assert.assertEquals(1, stepping.getLevelIndexForValue(5));
Assert.assertEquals(1, stepping.getLevelIndexForValue(3)); Assert.assertEquals(1, stepping.getLevelIndexForValue(3));
Assert.assertEquals(2, stepping.getSteppedValueForValue(1.064)); Assert.assertEquals(2., stepping.getSteppedValueForValue(1.064), 0.005);
Assert.assertEquals(4, stepping.getSteppedValueForValue(7.8365)); Assert.assertEquals(4., stepping.getSteppedValueForValue(7.8365), 0.005);
} }
@Test @Test
public void testSteppingWithMaxDistance() { public void testSteppingWithMaxDistance() {
Integer[] levels = {2,4}; Double[] levels = {2.,4.};
WindStepping stepping = new WindSteppingWithMaxDistance(levels, 1.0); WindStepping stepping = new WindSteppingWithMaxDistance(levels, 1.0);
Assert.assertEquals(0, stepping.getLevelIndexForValue(1)); Assert.assertEquals(0, stepping.getLevelIndexForValue(1));
Assert.assertEquals(1, stepping.getLevelIndexForValue(5)); Assert.assertEquals(1, stepping.getLevelIndexForValue(5));
Assert.assertEquals(-1, stepping.getLevelIndexForValue(5.01)); Assert.assertEquals(-1, stepping.getLevelIndexForValue(5.01));
Assert.assertEquals(-1, stepping.getLevelIndexForValue(0.5)); Assert.assertEquals(-1, stepping.getLevelIndexForValue(0.5));
Assert.assertEquals(-1, stepping.getLevelIndexForValue(8)); Assert.assertEquals(-1, stepping.getLevelIndexForValue(8));
Assert.assertEquals(2, stepping.getSteppedValueForValue(1.064)); Assert.assertEquals(2.0, stepping.getSteppedValueForValue(1.064), 0.005);
Assert.assertEquals(-1, stepping.getSteppedValueForValue(7.8365)); Assert.assertEquals(-1.0, stepping.getSteppedValueForValue(7.8365), 0.005);
} }
} }
@@ -66,7 +66,7 @@ public class PolarSheetGenerationServiceTest {
SailingService service = new MockSailingServiceForPolarSheetGeneration(); SailingService service = new MockSailingServiceForPolarSheetGeneration();
Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 }; Double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. };
WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0); WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0);
PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 20, 0, false, true, 5, PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 20, 0, false, true, 5,
0.05, false, windStepping, false); 0.05, false, windStepping, false);
@@ -47,6 +47,7 @@ Bundle-Activator: com.sap.sailing.gwt.ui.server.Activator
Bundle-ActivationPolicy: lazy Bundle-ActivationPolicy: lazy
Import-Package: com.sap.sailing.polars, Import-Package: com.sap.sailing.polars,
com.sap.sailing.polars.factory, com.sap.sailing.polars.factory,
com.sap.sailing.polars.regression,
javax.jms;version="1.1.0", javax.jms;version="1.1.0",
javax.servlet;version="3.1.0", javax.servlet;version="3.1.0",
javax.servlet.descriptor;version="3.1.0", javax.servlet.descriptor;version="3.1.0",
@@ -21,6 +21,7 @@ import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse; import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsXYDiagramData;
import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier;
@@ -534,4 +535,6 @@ public interface SailingService extends RemoteService {
* @return The RaceDTO of the modified race or <code>null</code>, if the given newStartTimeReceived was null. * @return The RaceDTO of the modified race or <code>null</code>, if the given newStartTimeReceived was null.
*/ */
RaceDTO setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived); RaceDTO setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived);
PolarSheetsXYDiagramData createXYDiagramForBoatClass(String itemText);
} }
@@ -16,6 +16,7 @@ import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.PassingInstruction; import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse; import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsXYDiagramData;
import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier;
@@ -646,4 +647,6 @@ public interface SailingServiceAsync extends BuildVersionRetriever {
AsyncCallback<Iterable<LeaderboardSearchResultDTO>> callback); AsyncCallback<Iterable<LeaderboardSearchResultDTO>> callback);
void setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived, AsyncCallback<RaceDTO> callback); void setStartTimeReceivedForRace(RaceIdentifier raceIdentifier, Date newStartTimeReceived, AsyncCallback<RaceDTO> callback);
void createXYDiagramForBoatClass(String itemText, AsyncCallback<PolarSheetsXYDiagramData> asyncCallback);
} }
@@ -1113,4 +1113,5 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages {
String serverReplies(); String serverReplies();
String errorCommunicatingWithServer(); String errorCommunicatingWithServer();
String userManagement(); String userManagement();
String showXYDiagram();
} }
@@ -1095,3 +1095,4 @@ remoteProcedureCall=Remote Procedure Call
serverReplies=Server replies serverReplies=Server replies
errorCommunicatingWithServer=Error communicating with server errorCommunicatingWithServer=Error communicating with server
userManagement=User Management userManagement=User Management
showXYDiagram=Show x-y-diagram
@@ -1,6 +1,9 @@
package com.sap.sailing.gwt.ui.polarsheets; package com.sap.sailing.gwt.ui.polarsheets;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map; import java.util.Map;
import java.util.Set;
import org.moxieapps.gwt.highcharts.client.AxisTitle; import org.moxieapps.gwt.highcharts.client.AxisTitle;
import org.moxieapps.gwt.highcharts.client.Chart; import org.moxieapps.gwt.highcharts.client.Chart;
@@ -30,8 +33,12 @@ public class AngleOverDataSizeHistogramPanel extends DockLayoutPanel {
private final Chart chart; private final Chart chart;
private final StringMessages stringMessages; private final StringMessages stringMessages;
private final Map<String, Set<Series>> seriesForId;
public AngleOverDataSizeHistogramPanel(StringMessages stringMessages) { public AngleOverDataSizeHistogramPanel(StringMessages stringMessages) {
super(Unit.PX); super(Unit.PX);
this.seriesForId = new HashMap<>();
this.stringMessages = stringMessages; this.stringMessages = stringMessages;
setSize("100%", "100%"); setSize("100%", "100%");
@@ -51,13 +58,20 @@ public class AngleOverDataSizeHistogramPanel extends DockLayoutPanel {
return histogramChart; return histogramChart;
} }
public void addData(Map<Integer, PolarSheetsHistogramData> histogramDataPerAngle) { public void addData(Map<Integer, PolarSheetsHistogramData> histogramDataPerAngle, String seriesId, String actualSeriesName) {
chart.setTitle(""); chart.setTitle("");
Series series = chart.createSeries(); Series series = chart.createSeries();
Point[] points = toPoints(histogramDataPerAngle); Point[] points = toPoints(histogramDataPerAngle);
series.setPoints(points); series.setPoints(points);
series.setName(actualSeriesName);
chart.addSeries(series); chart.addSeries(series);
if (!seriesForId.containsKey(seriesId)) {
seriesForId.put(seriesId, new HashSet<Series>());
}
Set<Series> seriesSet = seriesForId.get(seriesId);
seriesSet.add(series);
} }
private Point[] toPoints(Map<Integer, PolarSheetsHistogramData> histogramDataPerAngle) { private Point[] toPoints(Map<Integer, PolarSheetsHistogramData> histogramDataPerAngle) {
@@ -98,6 +112,18 @@ public class AngleOverDataSizeHistogramPanel extends DockLayoutPanel {
super.onResize(); super.onResize();
} }
public void removeSeries(String seriesId) {
for (Series series : seriesForId.get(seriesId)) {
chart.removeSeries(series);
}
seriesForId.remove(seriesId);
}
public void removeAllSeries() {
chart.removeAllSeries();
seriesForId.clear();
}
@@ -93,13 +93,12 @@ public class PolarSheetsChartPanel extends DockLayoutPanel {
* @param windSpeedLevel The id of the windspeed level in the windspeed steppings * @param windSpeedLevel The id of the windspeed level in the windspeed steppings
* @param windSpeed The actual windspeed * @param windSpeed The actual windspeed
*/ */
private void createSeriesForWindspeed(String name, int windSpeedLevel, int windSpeed) { private void createSeriesForWindspeed(String name, int windSpeedLevel, String seriesName) {
Series[] seriesPerWindSpeed = seriesMap.get(name); Series[] seriesPerWindSpeed = seriesMap.get(name);
Number[] forEachDeg = initializeDataForNewSeries(); Number[] forEachDeg = initializeDataForNewSeries();
seriesPerWindSpeed[windSpeedLevel] = chart.createSeries().setPoints(forEachDeg); seriesPerWindSpeed[windSpeedLevel] = chart.createSeries().setPoints(forEachDeg);
String actualSeriesName = name + "-" + windSpeed; seriesPerWindSpeed[windSpeedLevel].setName(seriesName);
seriesPerWindSpeed[windSpeedLevel].setName(actualSeriesName); nameForSeries.put(seriesPerWindSpeed[windSpeedLevel],seriesName);
nameForSeries.put(seriesPerWindSpeed[windSpeedLevel],actualSeriesName);
chart.addSeries(seriesPerWindSpeed[windSpeedLevel]); chart.addSeries(seriesPerWindSpeed[windSpeedLevel]);
} }
@@ -120,15 +119,16 @@ public class PolarSheetsChartPanel extends DockLayoutPanel {
if (seriesMap.containsKey(seriesId)) { if (seriesMap.containsKey(seriesId)) {
for (int i = 0; i < stepCount; i++) { for (int i = 0; i < stepCount; i++) {
if (hasSufficientDataForWindspeed(result.getDataCountPerAngleForWindspeed(i))) { if (hasSufficientDataForWindspeed(result.getDataCountPerAngleForWindspeed(i))) {
String actualSeriesName = seriesId + "-" + result.getStepping().getRawStepping()[i];
if (seriesMap.get(seriesId)[i] == null) { if (seriesMap.get(seriesId)[i] == null) {
createSeriesForWindspeed(seriesId, i, result.getStepping().getRawStepping()[i]); createSeriesForWindspeed(seriesId, i, actualSeriesName);
} }
Series series = seriesMap.get(seriesId)[i]; Series series = seriesMap.get(seriesId)[i];
//series.setPoints(result.getAveragedPolarDataByWindSpeed()[i], false); //series.setPoints(result.getAveragedPolarDataByWindSpeed()[i], false);
Point[] points = createPointsWithMarkerAlphaAccordingToDataCount(result, i); Point[] points = createPointsWithMarkerAlphaAccordingToDataCount(result, i);
if (points != null) { if (points != null) {
series.setPoints(points); series.setPoints(points);
angleOverDataSizeHistogramPanel.addData(result.getHistogramDataMap().get(i)); angleOverDataSizeHistogramPanel.addData(result.getHistogramDataMap().get(i), seriesId, actualSeriesName);
} }
} }
} }
@@ -209,6 +209,7 @@ public class PolarSheetsChartPanel extends DockLayoutPanel {
} }
seriesMap.remove(seriesId); seriesMap.remove(seriesId);
polarSheetsDataMap.remove(seriesId); polarSheetsDataMap.remove(seriesId);
angleOverDataSizeHistogramPanel.removeSeries(seriesId);
} }
} }
@@ -219,6 +220,7 @@ public class PolarSheetsChartPanel extends DockLayoutPanel {
chart.removeAllSeries(); chart.removeAllSeries();
seriesMap.clear(); seriesMap.clear();
polarSheetsDataMap.clear(); polarSheetsDataMap.clear();
angleOverDataSizeHistogramPanel.clear();
} }
/** /**
@@ -26,6 +26,7 @@ import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsData; import com.sap.sailing.domain.common.PolarSheetsData;
import com.sap.sailing.domain.common.PolarSheetsHistogramData; 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.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.WindStepping; import com.sap.sailing.domain.common.WindStepping;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
@@ -158,6 +159,29 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC
} }
}); });
leftPanel.add(showBoatClassDiagramButton); 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();
}
});
}
});
leftPanel.add(showXYDiagramsButton);
} }
private ListBox createBoatClassListBox() { private ListBox createBoatClassListBox() {
@@ -0,0 +1,94 @@
package com.sap.sailing.gwt.ui.polarsheets;
import java.util.List;
import org.moxieapps.gwt.highcharts.client.Chart;
import org.moxieapps.gwt.highcharts.client.Point;
import org.moxieapps.gwt.highcharts.client.Series;
import org.moxieapps.gwt.highcharts.client.Series.Type;
import com.google.gwt.event.dom.client.ClickEvent;
import com.google.gwt.event.dom.client.ClickHandler;
import com.google.gwt.user.client.Timer;
import com.google.gwt.user.client.ui.Button;
import com.google.gwt.user.client.ui.DialogBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.common.PolarSheetsXYDiagramData;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.common.Util.Pair;
public class PolarSheetsXYDiagramPopupPanel extends DialogBox {
private final Chart chart;
public PolarSheetsXYDiagramPopupPanel(StringMessages stringMessages, PolarSheetsXYDiagramData result) {
chart = createChart();
VerticalPanel containerPanel = new VerticalPanel();
containerPanel.setWidth("100%");
containerPanel.setHeight("100%");
this.add(containerPanel);
containerPanel.add(chart);
Button closeButton = new Button(stringMessages.close());
closeButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
PolarSheetsXYDiagramPopupPanel.this.hide(false);
}
});
containerPanel.add(closeButton);
Point[] pointsForUpwindStarboardAverageAngle = toPointArray(result.getPointsForUpwindStarboardAverageAngle());
Series angleSeries = chart.createSeries();
angleSeries.setPoints(pointsForUpwindStarboardAverageAngle);
//chart.addSeries(angleSeries);
Point[] pointsForUpwindStarboardAverageSpeed = toPointArray(result.getPointsForUpwindStarboardAverageSpeed());
Series speedSeries = chart.createSeries();
speedSeries.setName("Upwind Starboard Speed - LinearRegressions");
speedSeries.setPoints(pointsForUpwindStarboardAverageSpeed);
chart.addSeries(speedSeries);
Point[] pointsForUpwindStarboardAverageSpeedMovingAverage = toPointArray(result.getPointsForUpwindStarboardAverageSpeedMovingAverage());
Series speedSeriesMovingAverage = chart.createSeries();
speedSeriesMovingAverage.setName("Upwind Starboard Speed - MovingAverage");
speedSeriesMovingAverage.setPoints(pointsForUpwindStarboardAverageSpeedMovingAverage);
chart.addSeries(speedSeriesMovingAverage);
this.setWidth("1200px");
this.setHeight("800px");
}
private Point[] toPointArray(List<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.setHeight("80%");
return chart;
}
@Override
protected void onLoad() {
Timer timer = new Timer() {
@Override
public void run() {
chart.setSizeToMatchContainer();
}
};
timer.schedule(200);
super.onLoad();
}
}
@@ -27,9 +27,9 @@ public class WindSteppingConfiguratorPanel extends HorizontalPanel {
public WindSteppingConfiguratorPanel(WindSteppingWithMaxDistance windStepping) { public WindSteppingConfiguratorPanel(WindSteppingWithMaxDistance windStepping) {
setupPlusAndMinusButtons(); setupPlusAndMinusButtons();
Integer[] levels = windStepping.getRawStepping(); Double[] levels = windStepping.getRawStepping();
for (int i = 0; i < levels.length; i++) { for (int i = 0; i < levels.length; i++) {
TextBox textBox = createSingleBox(levels[i]); TextBox textBox = createSingleBox((int) Math.round(levels[i]));
textBoxes.add(textBox); textBoxes.add(textBox);
} }
updateTextBoxes(); updateTextBoxes();
@@ -69,12 +69,12 @@ public class WindSteppingConfiguratorPanel extends HorizontalPanel {
} }
public WindSteppingWithMaxDistance getStepping(double maxDistance) { public WindSteppingWithMaxDistance getStepping(double maxDistance) {
List<Integer> levelList = new ArrayList<Integer>(); List<Double> levelList = new ArrayList<Double>();
for (TextBox box : textBoxes) { for (TextBox box : textBoxes) {
levelList.add(Integer.parseInt(box.getValue())); levelList.add(new Double(Integer.parseInt(box.getValue())));
} }
Collections.sort(levelList); Collections.sort(levelList);
Integer[] levels = levelList.toArray(new Integer[levelList.size()]); Double[] levels = levelList.toArray(new Double[levelList.size()]);
return new WindSteppingWithMaxDistance(levels, maxDistance); return new WindSteppingWithMaxDistance(levels, maxDistance);
} }
@@ -102,6 +102,7 @@ import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.RemoteSailingServerReference; import com.sap.sailing.domain.base.RemoteSailingServerReference;
import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.base.Sideline; import com.sap.sailing.domain.base.Sideline;
import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence;
import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.base.configuration.DeviceConfiguration; import com.sap.sailing.domain.base.configuration.DeviceConfiguration;
import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher; import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher;
@@ -141,6 +142,7 @@ import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.PolarSheetGenerationResponse; import com.sap.sailing.domain.common.PolarSheetGenerationResponse;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings; import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.PolarSheetsData; 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.Position;
import com.sap.sailing.domain.common.RaceFetcher; import com.sap.sailing.domain.common.RaceFetcher;
import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RaceIdentifier;
@@ -184,6 +186,7 @@ import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationResponseImpl; import com.sap.sailing.domain.common.impl.PolarSheetGenerationResponseImpl;
import com.sap.sailing.domain.common.impl.PolarSheetsXYDiagramDataImpl;
import com.sap.sailing.domain.common.impl.TimeRangeImpl; import com.sap.sailing.domain.common.impl.TimeRangeImpl;
import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.impl.WindSourceImpl;
import com.sap.sailing.domain.common.media.MediaTrack; import com.sap.sailing.domain.common.media.MediaTrack;
@@ -329,6 +332,7 @@ import com.sap.sailing.manage2sail.Manage2SailEventResultsParserImpl;
import com.sap.sailing.manage2sail.RaceResultDescriptor; import com.sap.sailing.manage2sail.RaceResultDescriptor;
import com.sap.sailing.manage2sail.RegattaResultDescriptor; import com.sap.sailing.manage2sail.RegattaResultDescriptor;
import com.sap.sailing.polars.PolarDataService; import com.sap.sailing.polars.PolarDataService;
import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
import com.sap.sailing.resultimport.ResultUrlProvider; import com.sap.sailing.resultimport.ResultUrlProvider;
import com.sap.sailing.resultimport.ResultUrlRegistry; import com.sap.sailing.resultimport.ResultUrlRegistry;
import com.sap.sailing.server.RacingEventService; import com.sap.sailing.server.RacingEventService;
@@ -5115,4 +5119,40 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
} }
return null; return null;
} }
@Override
public PolarSheetsXYDiagramData createXYDiagramForBoatClass(String boatClassName) {
BoatClass boatClass = getService().getBaseDomainFactory().getOrCreateBoatClass(boatClassName);
List<Pair<Double, Double>> pointsForUpwindStarboardAverageSpeed = new ArrayList<Pair<Double, Double>>();
List<Pair<Double, Double>> pointsForUpwindStarboardAverageAngle = new ArrayList<Pair<Double, Double>>();
List<Pair<Double, Double>> pointsForUpwindStarboardAverageSpeedMovingAverage = new ArrayList<Pair<Double, Double>>();
List<Pair<Double, Double>> pointsForUpwindStarboardAverageAngleMovingAverage = new ArrayList<Pair<Double, Double>>();
for (double windInKnots = 0.1; windInKnots < 30; windInKnots = windInKnots + 0.1) {
try {
SpeedWithBearingWithConfidence<Void> averageUpwindStarboard = getService().getPolarDataService()
.getAverageSpeedWithBearing(boatClass, new KnotSpeedImpl(windInKnots), LegType.UPWIND,
Tack.STARBOARD, true);
pointsForUpwindStarboardAverageSpeed.add(new Pair<Double, Double>(windInKnots, averageUpwindStarboard
.getObject().getKnots()));
pointsForUpwindStarboardAverageAngle.add(new Pair<Double, Double>(windInKnots, averageUpwindStarboard
.getObject().getBearing().getDegrees()));
SpeedWithBearingWithConfidence<Void> averageUpwindStarboardMovingAverage = getService().getPolarDataService()
.getAverageSpeedWithBearing(boatClass, new KnotSpeedImpl(windInKnots), LegType.UPWIND,
Tack.STARBOARD, false);
pointsForUpwindStarboardAverageSpeedMovingAverage.add(new Pair<Double, Double>(windInKnots,
averageUpwindStarboardMovingAverage.getObject().getKnots()));
pointsForUpwindStarboardAverageAngleMovingAverage.add(new Pair<Double, Double>(windInKnots,
averageUpwindStarboardMovingAverage.getObject().getBearing().getDegrees()));
} catch (NotEnoughDataHasBeenAddedException e) {
// Do not add a point to the result
}
}
PolarSheetsXYDiagramData data = new PolarSheetsXYDiagramDataImpl(pointsForUpwindStarboardAverageAngle,
pointsForUpwindStarboardAverageSpeed, pointsForUpwindStarboardAverageAngleMovingAverage,
pointsForUpwindStarboardAverageSpeedMovingAverage);
return data;
}
} }
@@ -107,17 +107,17 @@ public class PolarDataMinerTest {
} }
SpeedWithConfidence<Void> estimatedSpeed1 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), SpeedWithConfidence<Void> estimatedSpeed1 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15),
new DegreeBearingImpl(44.9)); new DegreeBearingImpl(44.9), true);
assertThat(estimatedSpeed1, is(notNullValue())); assertThat(estimatedSpeed1, is(notNullValue()));
assertThat(estimatedSpeed1.getObject().getKnots(), is(closeTo(10.55, EPSILON))); assertThat(estimatedSpeed1.getObject().getKnots(), is(closeTo(10.55, EPSILON)));
SpeedWithConfidence<Void> estimatedSpeed2 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), SpeedWithConfidence<Void> estimatedSpeed2 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15),
new DegreeBearingImpl(42)); new DegreeBearingImpl(42), true);
assertThat(estimatedSpeed2, is(notNullValue())); assertThat(estimatedSpeed2, is(notNullValue()));
assertThat(estimatedSpeed2.getObject().getKnots(), is(closeTo(10.05, EPSILON))); assertThat(estimatedSpeed2.getObject().getKnots(), is(closeTo(10.05, EPSILON)));
SpeedWithConfidence<Void> estimatedSpeed3 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15), SpeedWithConfidence<Void> estimatedSpeed3 = miner.estimateBoatSpeed(mockedBoatClass, new KnotSpeedImpl(15),
new DegreeBearingImpl(42.8)); new DegreeBearingImpl(42.8), true);
assertThat(estimatedSpeed3, is(notNullValue())); assertThat(estimatedSpeed3, is(notNullValue()));
assertThat(estimatedSpeed3.getObject().getKnots(), is(closeTo(10, EPSILON))); assertThat(estimatedSpeed3.getObject().getKnots(), is(closeTo(10, EPSILON)));
} }
@@ -30,25 +30,25 @@ public class PolarSheetAnalyzerTest {
BoatClass boatClass = mock(BoatClass.class); BoatClass boatClass = mock(BoatClass.class);
SpeedWithBearingWithConfidence<Void> result = analyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, SpeedWithBearingWithConfidence<Void> result = analyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass,
new KnotSpeedImpl(14)); new KnotSpeedImpl(14), true);
assertThat(result.getObject().getKnots(), closeTo(8.5, 0.1)); assertThat(result.getObject().getKnots(), closeTo(8.5, 0.1));
assertThat(result.getObject().getBearing().getDegrees(), closeTo(50, 0.1)); assertThat(result.getObject().getBearing().getDegrees(), closeTo(50, 0.1));
assertThat(result.getConfidence(), closeTo(0.7, 0.1)); assertThat(result.getConfidence(), closeTo(0.7, 0.1));
SpeedWithBearingWithConfidence<Void> result2 = analyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, SpeedWithBearingWithConfidence<Void> result2 = analyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass,
new KnotSpeedImpl(14)); new KnotSpeedImpl(14), true);
assertThat(result2.getObject().getKnots(), closeTo(14.2, 0.1)); assertThat(result2.getObject().getKnots(), closeTo(14.2, 0.1));
assertThat(result2.getObject().getBearing().getDegrees(), closeTo(142.8, 0.1)); assertThat(result2.getObject().getBearing().getDegrees(), closeTo(142.8, 0.1));
assertThat(result2.getConfidence(), closeTo(0.6, 0.1)); assertThat(result2.getConfidence(), closeTo(0.6, 0.1));
SpeedWithBearingWithConfidence<Void> result3 = analyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, SpeedWithBearingWithConfidence<Void> result3 = analyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass,
new KnotSpeedImpl(14)); new KnotSpeedImpl(14), true);
assertThat(result3.getObject().getKnots(), closeTo(8.6, 0.1)); assertThat(result3.getObject().getKnots(), closeTo(8.6, 0.1));
assertThat(result3.getObject().getBearing().getDegrees(), closeTo(-49.5, 0.1)); assertThat(result3.getObject().getBearing().getDegrees(), closeTo(-49.5, 0.1));
assertThat(result3.getConfidence(), closeTo(0.7, 0.1)); assertThat(result3.getConfidence(), closeTo(0.7, 0.1));
SpeedWithBearingWithConfidence<Void> result4 = analyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, SpeedWithBearingWithConfidence<Void> result4 = analyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass,
new KnotSpeedImpl(14)); new KnotSpeedImpl(14), true);
assertThat(result4.getObject().getKnots(), closeTo(13.5, 0.1)); assertThat(result4.getObject().getKnots(), closeTo(13.5, 0.1));
assertThat(result4.getObject().getBearing().getDegrees(), closeTo(-143.6, 0.1)); assertThat(result4.getObject().getBearing().getDegrees(), closeTo(-143.6, 0.1));
assertThat(result4.getConfidence(), closeTo(0.25, 0.1)); assertThat(result4.getConfidence(), closeTo(0.25, 0.1));
@@ -63,46 +63,46 @@ public class PolarSheetAnalyzerTest {
//Starboard Upwind //Starboard Upwind
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(49)))).thenReturn( argThat(new BearingMatcher(49)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.478048671702888), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.478048671702888), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(50)))).thenReturn( argThat(new BearingMatcher(50)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.466303997538812), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.466303997538812), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(51)))).thenReturn( argThat(new BearingMatcher(51)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.583383077026435), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.583383077026435), 0.5, null));
//Starboard Downwind //Starboard Downwind
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(141)))).thenReturn( argThat(new BearingMatcher(141)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.794354053931528), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.794354053931528), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(142)))).thenReturn( argThat(new BearingMatcher(142)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.665782579628802), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.665782579628802), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(143)))).thenReturn( argThat(new BearingMatcher(143)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(14.169301888320263), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(14.169301888320263), 0.5, null));
//Port Upwind //Port Upwind
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-48)))).thenReturn( argThat(new BearingMatcher(-48)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.445599410456111), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.445599410456111), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-50)))).thenReturn( argThat(new BearingMatcher(-50)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.553274292235153), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.553274292235153), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-49)))).thenReturn( argThat(new BearingMatcher(-49)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.614582090896583), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(8.614582090896583), 0.5, null));
//Port Downwind //Port Downwind
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-145)))).thenReturn( argThat(new BearingMatcher(-145)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.78894715705271), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.78894715705271), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-144)))).thenReturn( argThat(new BearingMatcher(-144)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.420656294986587), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.420656294986587), 0.5, null));
when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)), when(mockedPolarDataService.getSpeed(any(BoatClass.class), argThat(new SpeedMatcher(14)),
argThat(new BearingMatcher(-143)))).thenReturn( argThat(new BearingMatcher(-143)), true)).thenReturn(
new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.268607457651942), 0.5, null)); new SpeedWithConfidenceImpl<Void>(new KnotSpeedImpl(13.268607457651942), 0.5, null));
return mockedPolarDataService; return mockedPolarDataService;
@@ -58,14 +58,14 @@ import com.sap.sailing.domain.tracking.impl.WindImpl;
import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl; import com.sap.sailing.domain.tracking.impl.WindWithConfidenceImpl;
import com.sap.sailing.polars.aggregation.PolarFixAggregationWorker; import com.sap.sailing.polars.aggregation.PolarFixAggregationWorker;
import com.sap.sailing.polars.aggregation.PolarFixAggregator; import com.sap.sailing.polars.aggregation.PolarFixAggregator;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
import com.sap.sailing.polars.aggregation.SimplePolarFixRaceInterval; import com.sap.sailing.polars.aggregation.SimplePolarFixRaceInterval;
import com.sap.sailing.polars.data.DataPointWithOriginInfo; import com.sap.sailing.polars.data.DataPointWithOriginInfo;
import com.sap.sailing.polars.data.PolarFix; import com.sap.sailing.polars.data.PolarFix;
import com.sap.sailing.polars.data.impl.DataPointWithOriginInfoImpl; import com.sap.sailing.polars.data.impl.DataPointWithOriginInfoImpl;
import com.sap.sailing.polars.generation.PolarSheetGenerator; import com.sap.sailing.polars.generation.PolarSheetGenerator;
import com.sap.sailing.polars.generation.PolarSheetHistogramBuilder; import com.sap.sailing.polars.generation.PolarSheetHistogramBuilder;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.impl.MillisecondsTimePoint;
public class PolarSheetGenerationTest { public class PolarSheetGenerationTest {
@@ -78,7 +78,7 @@ public class PolarSheetGenerationTest {
MockTrackedRaceForPolarSheetGeneration race = new MockTrackedRaceForPolarSheetGeneration(); MockTrackedRaceForPolarSheetGeneration race = new MockTrackedRaceForPolarSheetGeneration();
Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 }; Double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. };
WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0); WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0);
PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 20, 0, false, true, 5, PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 20, 0, false, true, 5,
0.05, false, windStepping, false); 0.05, false, windStepping, false);
@@ -118,7 +118,7 @@ public class PolarSheetGenerationTest {
@Test @Test
public void testHistogramBuilder() { public void testHistogramBuilder() {
Integer[] levels = { 4, 6, 8, 10, 12, 14, 16, 20, 25, 30 }; Double[] levels = { 4., 6., 8., 10., 12., 14., 16., 20., 25., 30. };
WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0); WindSteppingWithMaxDistance windStepping = new WindSteppingWithMaxDistance(levels, 2.0);
PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 10, 0, false, true, 5, PolarSheetGenerationSettings settings = new PolarSheetGenerationSettingsImpl(1, 0, 1, 10, 0, false, true, 5,
0.05, false, windStepping, false); 0.05, false, windStepping, false);
@@ -26,7 +26,7 @@ import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
* It uses a {@link PolarSheetAnalyzer} for more advanced analysis. It's methods are facaded in this interface for * It uses a {@link PolarSheetAnalyzer} for more advanced analysis. It's methods are facaded in this interface for
* central access. * central access.
* *
* The interesting methods for a user are {@link #getSpeed(BoatClass, Speed, Bearing)} if data for a specific angle is * The interesting methods for a user are {@link #getSpeed(BoatClass, Speed, Bearing, boolean)} if data for a specific angle is
* needed and {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)} * needed and {@link #getAverageSpeedWithBearing(BoatClass, Speed, LegType, Tack)}
* which also returns the average angle for the provided parameters. * which also returns the average angle for the provided parameters.
* *
@@ -41,10 +41,11 @@ public interface PolarDataService {
* @param windSpeed * @param windSpeed
* @param bearingToTheWind * @param bearingToTheWind
* Boat's direction relative to the wind. either in -180 -> +180 or 0 -> 359 degrees * Boat's direction relative to the wind. either in -180 -> +180 or 0 -> 359 degrees
* @param useLinearRegression if true uses lin. Regression for estimation in the wind interval, if false simple arithm. mean in interval
* @return The speed the boat is moving at for the specified wind and bearing according to the polar diagram. * @return The speed the boat is moving at for the specified wind and bearing according to the polar diagram.
* @throws NotEnoughDataHasBeenAddedException * @throws NotEnoughDataHasBeenAddedException
*/ */
SpeedWithConfidence<Void> getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) SpeedWithConfidence<Void> getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind, boolean useLinearRegression)
throws NotEnoughDataHasBeenAddedException; throws NotEnoughDataHasBeenAddedException;
/** /**
@@ -126,5 +127,8 @@ public interface PolarDataService {
*/ */
Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive); Integer[] getDataCountsForWindSpeed(BoatClass boatClass, Speed windSpeed, int startAngleInclusive, int endAngleExclusive);
SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed,
LegType legType, Tack tack, boolean useLinReg) throws NotEnoughDataHasBeenAddedException;
} }
@@ -8,15 +8,15 @@ import com.sap.sailing.polars.regression.NotEnoughDataHasBeenAddedException;
public interface PolarSheetAnalyzer { public interface PolarSheetAnalyzer {
SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass,
Speed windSpeed) throws NotEnoughDataHasBeenAddedException; Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException;
SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass,
Speed windSpeed) throws NotEnoughDataHasBeenAddedException; Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException;
SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass,
Speed windSpeed) throws NotEnoughDataHasBeenAddedException; Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException;
SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass,
Speed windSpeed) throws NotEnoughDataHasBeenAddedException; Speed windSpeed, boolean useLinReg) throws NotEnoughDataHasBeenAddedException;
} }
@@ -33,48 +33,48 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer {
} }
@Override @Override
public SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) public SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed, boolean useLinReg)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
int startAngleInclusive = 1; int startAngleInclusive = 1;
int endAngleExclusive = 90; int endAngleExclusive = 90;
SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive,
endAngleExclusive); endAngleExclusive, useLinReg);
return speedWithBearing; return speedWithBearing;
} }
@Override @Override
public SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed) public SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnStarboardTackFor(BoatClass boatClass, Speed windSpeed, boolean useLinReg)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
int startAngleInclusive = 91; int startAngleInclusive = 91;
int endAngleExclusive = 180; int endAngleExclusive = 180;
SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive,
endAngleExclusive); endAngleExclusive, useLinReg);
return speedWithBearing; return speedWithBearing;
} }
@Override @Override
public SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) public SpeedWithBearingWithConfidence<Void> getAverageDownwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed, boolean useLinReg)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
int startAngleInclusive = 181; int startAngleInclusive = 181;
int endAngleExclusive = 270; int endAngleExclusive = 270;
SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive,
endAngleExclusive); endAngleExclusive, useLinReg);
return speedWithBearing; return speedWithBearing;
} }
@Override @Override
public SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed) public SpeedWithBearingWithConfidence<Void> getAverageUpwindSpeedWithBearingOnPortTackFor(BoatClass boatClass, Speed windSpeed, boolean useLinReg)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
int startAngleInclusive = 271; int startAngleInclusive = 271;
int endAngleExclusive = 360; int endAngleExclusive = 360;
SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive, SpeedWithBearingWithConfidence<Void> speedWithBearing = estimateAnglePeakAndAverageSpeed(boatClass, windSpeed, startAngleInclusive,
endAngleExclusive); endAngleExclusive, useLinReg);
return speedWithBearing; return speedWithBearing;
} }
private SpeedWithBearingWithConfidence<Void> estimateAnglePeakAndAverageSpeed(BoatClass boatClass, Speed windSpeed, private SpeedWithBearingWithConfidence<Void> estimateAnglePeakAndAverageSpeed(BoatClass boatClass, Speed windSpeed,
int startAngleInclusive, int endAngleExclusive) throws NotEnoughDataHasBeenAddedException { int startAngleInclusive, int endAngleExclusive, boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException {
Integer[] dataCountPerAngle = getDataCountArray(boatClass, windSpeed, startAngleInclusive, endAngleExclusive); Integer[] dataCountPerAngle = getDataCountArray(boatClass, windSpeed, startAngleInclusive, endAngleExclusive);
@@ -83,7 +83,8 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer {
double convertedAngleIfOver180 = convertAngleIfNecessary(estimatedPeak); double convertedAngleIfOver180 = convertAngleIfNecessary(estimatedPeak);
SpeedWithConfidence<Void> averagedSpeedWithConfidence = estimatedSpeed(boatClass, windSpeed, convertedAngleIfOver180); SpeedWithConfidence<Void> averagedSpeedWithConfidence = estimateSpeed(boatClass, windSpeed,
convertedAngleIfOver180, useLinearRegression);
double originConfidence = averagedSpeedWithConfidence.getConfidence(); double originConfidence = averagedSpeedWithConfidence.getConfidence();
@@ -122,10 +123,10 @@ public class PolarSheetAnalyzerImpl implements PolarSheetAnalyzer {
return convertedAngleIfOver180; return convertedAngleIfOver180;
} }
private SpeedWithConfidence<Void> estimatedSpeed(BoatClass boatClass, Speed windSpeed, double estimatedPeak) private SpeedWithConfidence<Void> estimateSpeed(BoatClass boatClass, Speed windSpeed, double estimatedPeak,
throws NotEnoughDataHasBeenAddedException { boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException {
SpeedWithConfidence<Void> speed = polarDataService.getSpeed(boatClass, windSpeed, new DegreeBearingImpl( SpeedWithConfidence<Void> speed = polarDataService.getSpeed(boatClass, windSpeed, new DegreeBearingImpl(
estimatedPeak)); estimatedPeak), useLinearRegression);
return speed; return speed;
} }
@@ -59,26 +59,32 @@ public class PolarDataServiceImpl implements PolarDataService {
} }
@Override @Override
public SpeedWithConfidence<Void> getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind) public SpeedWithConfidence<Void> getSpeed(BoatClass boatClass, Speed windSpeed, Bearing bearingToTheWind,
throws NotEnoughDataHasBeenAddedException { boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException {
return polarDataMiner.estimateBoatSpeed(boatClass, windSpeed, bearingToTheWind); return polarDataMiner.estimateBoatSpeed(boatClass, windSpeed, bearingToTheWind, useLinearRegression);
} }
@Override @Override
public SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed, public SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed,
LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException {
return getAverageSpeedWithBearing(boatClass, windSpeed, legType, tack, true);
}
@Override
public SpeedWithBearingWithConfidence<Void> getAverageSpeedWithBearing(BoatClass boatClass,
Speed windSpeed, LegType legType, Tack tack, boolean useLinReg) throws NotEnoughDataHasBeenAddedException {
SpeedWithBearingWithConfidence<Void> speedWithBearing = null; SpeedWithBearingWithConfidence<Void> speedWithBearing = null;
if (tack.equals(Tack.STARBOARD)) { if (tack.equals(Tack.STARBOARD)) {
if (legType.equals(LegType.UPWIND)) { if (legType.equals(LegType.UPWIND)) {
speedWithBearing = polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); speedWithBearing = polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed, useLinReg);
} else if (legType.equals(LegType.DOWNWIND)) { } else if (legType.equals(LegType.DOWNWIND)) {
speedWithBearing = polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed); speedWithBearing = polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnStarboardTackFor(boatClass, windSpeed, useLinReg);
} }
} else if (tack.equals(Tack.PORT)) { } else if (tack.equals(Tack.PORT)) {
if (legType.equals(LegType.UPWIND)) { if (legType.equals(LegType.UPWIND)) {
return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); return polarSheetAnalyzer.getAverageUpwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed, useLinReg);
} else if (legType.equals(LegType.DOWNWIND)) { } else if (legType.equals(LegType.DOWNWIND)) {
return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed); return polarSheetAnalyzer.getAverageDownwindSpeedWithBearingOnPortTackFor(boatClass, windSpeed, useLinReg);
} }
} }
return speedWithBearing; return speedWithBearing;
@@ -94,7 +94,7 @@ public class IncrementalRegressionProcessor implements Processor<GroupedDataEntr
} }
public Pair<SpeedWithConfidence<Void>, Integer> estimateBoatSpeed(final BoatClass boatClass, final Speed windSpeed, public Pair<SpeedWithConfidence<Void>, Integer> estimateBoatSpeed(final BoatClass boatClass, final Speed windSpeed,
final Bearing angleToTheWind) final Bearing angleToTheWind, boolean useLinearRegression)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
int dataCount = 0; int dataCount = 0;
PolarClusterKey key = new PolarClusterKey() { PolarClusterKey key = new PolarClusterKey() {
@@ -126,7 +126,7 @@ public class IncrementalRegressionProcessor implements Processor<GroupedDataEntr
throw new NotEnoughDataHasBeenAddedException(); throw new NotEnoughDataHasBeenAddedException();
} }
KnotSpeedImpl speedWithoutConfidence = new KnotSpeedImpl(boatSpeedEstimator.estimateSpeed(windSpeed.getKnots(), KnotSpeedImpl speedWithoutConfidence = new KnotSpeedImpl(boatSpeedEstimator.estimateSpeed(windSpeed.getKnots(),
angleToTheWind.getDegrees())); angleToTheWind.getDegrees(), useLinearRegression));
dataCount = boatSpeedEstimator.getDataCount(); dataCount = boatSpeedEstimator.getDataCount();
double confidence = boatSpeedEstimator.getConfidence(); double confidence = boatSpeedEstimator.getConfidence();
return new Pair<SpeedWithConfidence<Void>, Integer>(new SpeedWithConfidenceImpl<Void>( return new Pair<SpeedWithConfidence<Void>, Integer>(new SpeedWithConfidenceImpl<Void>(
@@ -40,7 +40,7 @@ public class PolarDataMiner {
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3); private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
private static final ThreadPoolExecutor executor = createExecutor(); private static final ThreadPoolExecutor executor = createExecutor();
private final PolarSheetGenerationSettings defaultPolarSheetGenerationSettings; private final PolarSheetGenerationSettings backendPolarSheetGenerationSettings;
private static ThreadPoolExecutor createExecutor() { private static ThreadPoolExecutor createExecutor() {
return new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS, return new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS,
@@ -52,7 +52,7 @@ public class PolarDataMiner {
private IncrementalRegressionProcessor incrementalRegressionProcessor; private IncrementalRegressionProcessor incrementalRegressionProcessor;
public PolarDataMiner() { public PolarDataMiner() {
defaultPolarSheetGenerationSettings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings(); backendPolarSheetGenerationSettings = PolarSheetGenerationSettingsImpl.createBackendPolarSettings();
try { try {
setUpWorkflow(); setUpWorkflow();
} catch (ClassCastException | NoSuchMethodException | SecurityException e) { } catch (ClassCastException | NoSuchMethodException | SecurityException e) {
@@ -65,7 +65,7 @@ public class PolarDataMiner {
private void setUpWorkflow() throws ClassCastException, NoSuchMethodException, private void setUpWorkflow() throws ClassCastException, NoSuchMethodException,
SecurityException { SecurityException {
WindSteppingWithMaxDistance stepping = defaultPolarSheetGenerationSettings.getWindStepping(); WindSteppingWithMaxDistance stepping = backendPolarSheetGenerationSettings.getWindStepping();
final ClusterGroup<Speed> speedClusterGroup = SpeedClusterGroupFromWindSteppingCreator final ClusterGroup<Speed> speedClusterGroup = SpeedClusterGroupFromWindSteppingCreator
.createSpeedClusterGroupFrom(stepping); .createSpeedClusterGroupFrom(stepping);
incrementalRegressionProcessor = new IncrementalRegressionProcessor(speedClusterGroup); incrementalRegressionProcessor = new IncrementalRegressionProcessor(speedClusterGroup);
@@ -108,15 +108,25 @@ public class PolarDataMiner {
return isActive || hasQueue; return isActive || hasQueue;
} }
public SpeedWithConfidence<Void> estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind) /**
*
* @param boatClass
* @param windSpeed
* @param angleToTheWind
* @param useLinearRegression if true uses lin. regression in the wind interval, otherwise arithm. mean
* @return
* @throws NotEnoughDataHasBeenAddedException
*/
public SpeedWithConfidence<Void> estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing angleToTheWind,
boolean useLinearRegression)
throws NotEnoughDataHasBeenAddedException { throws NotEnoughDataHasBeenAddedException {
return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind).getA(); return incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, angleToTheWind, useLinearRegression).getA();
} }
public PolarSheetsData createFullSheetForBoatClass(BoatClass boatClass) { public PolarSheetsData createFullSheetForBoatClass(BoatClass boatClass) {
Integer[] defaultWindSpeeds = defaultPolarSheetGenerationSettings.getWindStepping().getRawStepping(); Double[] defaultWindSpeeds = backendPolarSheetGenerationSettings.getWindStepping().getRawStepping();
Number[][] averagedPolarDataByWindSpeed = new Number[defaultWindSpeeds.length][360]; Number[][] averagedPolarDataByWindSpeed = new Number[defaultWindSpeeds.length][360];
Map<Integer, Integer[]> dataCountPerAngleForWindspeed = new HashMap<>(); Map<Integer, Integer[]> dataCountPerAngleForWindspeed = new HashMap<>();
@@ -125,7 +135,7 @@ public class PolarDataMiner {
int totalDataCount = 0; int totalDataCount = 0;
for (int windIndex = 0; windIndex < defaultWindSpeeds.length; windIndex++) { for (int windIndex = 0; windIndex < defaultWindSpeeds.length; windIndex++) {
Integer windSpeed = defaultWindSpeeds[windIndex]; Double windSpeed = defaultWindSpeeds[windIndex];
Integer[] perAngle = new Integer[360]; Integer[] perAngle = new Integer[360];
Map<Integer, PolarSheetsHistogramData> perWindSpeed = new HashMap<>(); Map<Integer, PolarSheetsHistogramData> perWindSpeed = new HashMap<>();
@@ -134,7 +144,7 @@ public class PolarDataMiner {
try { try {
int convertedAngle = convertAngleIfNecessary(angle); int convertedAngle = convertAngleIfNecessary(angle);
speedWithConfidenceAndDataCount = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, speedWithConfidenceAndDataCount = incrementalRegressionProcessor.estimateBoatSpeed(boatClass,
new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle), true);
} catch (NotEnoughDataHasBeenAddedException e) { } catch (NotEnoughDataHasBeenAddedException e) {
// No data so put in a 0 speed with 0 confidence // No data so put in a 0 speed with 0 confidence
speedWithConfidenceAndDataCount = new Pair<SpeedWithConfidence<Void>, Integer>( speedWithConfidenceAndDataCount = new Pair<SpeedWithConfidence<Void>, Integer>(
@@ -159,7 +169,7 @@ public class PolarDataMiner {
PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, totalDataCount, PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, totalDataCount,
dataCountPerAngleForWindspeed, defaultPolarSheetGenerationSettings.getWindStepping(), histogramDataMap); dataCountPerAngleForWindspeed, backendPolarSheetGenerationSettings.getWindStepping(), histogramDataMap);
return data; return data;
} }
@@ -206,7 +216,7 @@ public class PolarDataMiner {
if (angle >= startAngleInclusive && angle < endAngleExclusive) { if (angle >= startAngleInclusive && angle < endAngleExclusive) {
try { try {
dataCounts[angle] = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed, dataCounts[angle] = incrementalRegressionProcessor.estimateBoatSpeed(boatClass, windSpeed,
new DegreeBearingImpl(convertAngleIfNecessary(angle))).getB(); new DegreeBearingImpl(convertAngleIfNecessary(angle)), true).getB();
} catch (NotEnoughDataHasBeenAddedException e) { } catch (NotEnoughDataHasBeenAddedException e) {
dataCounts[angle] = 0; dataCounts[angle] = 0;
} }
@@ -9,7 +9,7 @@ public class SpeedClusterGroupFromWindSteppingCreator {
public static ClusterGroup<Speed> createSpeedClusterGroupFrom(WindSteppingWithMaxDistance windStepping) { public static ClusterGroup<Speed> createSpeedClusterGroupFrom(WindSteppingWithMaxDistance windStepping) {
double maxDistance = windStepping.getMaxDistance(); double maxDistance = windStepping.getMaxDistance();
Integer[] rawIntegerStepping = windStepping.getRawStepping(); Double[] rawIntegerStepping = windStepping.getRawStepping();
double[] rawDoubleLevelMids = new double[rawIntegerStepping.length]; double[] rawDoubleLevelMids = new double[rawIntegerStepping.length];
for (int i = 0; i < rawIntegerStepping.length; i++) { for (int i = 0; i < rawIntegerStepping.length; i++) {
rawDoubleLevelMids[i] = rawIntegerStepping[i]; rawDoubleLevelMids[i] = rawIntegerStepping[i];
@@ -30,15 +30,31 @@ public class BoatSpeedEstimator {
private int dataCount = 0; private int dataCount = 0;
public double estimateSpeed(double windSpeed, double angleToTheWind) throws NotEnoughDataHasBeenAddedException { /**
*
* @param windSpeed
* @param angleToTheWind
* @param useLinearRegression if false mean of wind interval is used, otherwise lin. regression
* @return
* @throws NotEnoughDataHasBeenAddedException
*/
public double estimateSpeed(double windSpeed, double angleToTheWind, boolean useLinearRegression) throws NotEnoughDataHasBeenAddedException {
lock.readLock().lock(); lock.readLock().lock();
double result;
try { try {
if (useLinearRegression) {
double angleEstimated = angleRegression.getEstimatedY(angleToTheWind); double angleEstimated = angleRegression.getEstimatedY(angleToTheWind);
double windSpeedEstimated = windSpeedRegression.getEstimatedY(windSpeed); double windSpeedEstimated = windSpeedRegression.getEstimatedY(windSpeed);
return (angleEstimated + windSpeedEstimated) / 2.0;
result = (angleEstimated + windSpeedEstimated) / 2.0;
} else {
//Return average for that wind interval
result = windSpeedRegression.getMeanOfY();
}
} finally { } finally {
lock.readLock().unlock(); lock.readLock().unlock();
} }
return result;
} }
public void addData(double windSpeed, double angleToTheWind, double boatSpeed, double confidence) { public void addData(double windSpeed, double angleToTheWind, double boatSpeed, double confidence) {
@@ -12,4 +12,6 @@ public interface IncrementalLinearRegressionProcessor {
int getDataCount(); int getDataCount();
double getMeanOfY() throws NotEnoughDataHasBeenAddedException;
} }
@@ -119,4 +119,12 @@ public class IncrementalLeastSquaresProcessor implements IncrementalLinearRegres
return numberOfPointsAdded; return numberOfPointsAdded;
} }
@Override
public double getMeanOfY() throws NotEnoughDataHasBeenAddedException {
if (numberOfPointsAdded < 1) {
throw new NotEnoughDataHasBeenAddedException();
}
return meanOfY;
}
} }
@@ -42,7 +42,7 @@ public class PolarResource extends AbstractSailingServerResource {
ResponseBuilder responseBuilder; ResponseBuilder responseBuilder;
try { try {
speedWithConfidence = getService().getPolarDataService().getSpeed(boatClass, new KnotSpeedImpl(windSpeed), speedWithConfidence = getService().getPolarDataService().getSpeed(boatClass, new KnotSpeedImpl(windSpeed),
new DegreeBearingImpl(angle)); new DegreeBearingImpl(angle), true);
String resultString = "Speed: " + speedWithConfidence.getObject().getKnots() + "kn; Confidence: " String resultString = "Speed: " + speedWithConfidence.getObject().getKnots() + "kn; Confidence: "
+ speedWithConfidence.getConfidence(); + speedWithConfidence.getConfidence();
responseBuilder = Response.ok(resultString, MediaType.TEXT_PLAIN); responseBuilder = Response.ok(resultString, MediaType.TEXT_PLAIN);