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