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
+422 -98

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 getSteppedValueForValue(double speed);
public abstract Double getSteppedValueForValue(double speed);
Integer[] getRawStepping();
Double[] getRawStepping();
public abstract int getLevelIndexFloorForValue(double speed);
@@ -1,15 +1,30 @@
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;
@@ -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() {};
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];
}
@@ -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;
@@ -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);
}
}
@@ -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",
@@ -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);
}
@@ -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);
}
@@ -1113,4 +1113,5 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages {
String serverReplies();
String errorCommunicatingWithServer();
String userManagement();
String showXYDiagram();
}
@@ -1095,3 +1095,4 @@ remoteProcedureCall=Remote Procedure Call
serverReplies=Server replies
errorCommunicatingWithServer=Error communicating with server
userManagement=User Management
showXYDiagram=Show x-y-diagram
@@ -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;
@@ -29,9 +32,13 @@ 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) {
@@ -97,6 +111,18 @@ public class AngleOverDataSizeHistogramPanel extends DockLayoutPanel {
chart.setSizeToMatchContainer();
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 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();
}
/**
@@ -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() {
@@ -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) {
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);
}
@@ -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;
}
}
@@ -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)));
}
@@ -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;
@@ -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;
/**
@@ -125,6 +126,9 @@ public interface PolarDataService {
* @return array with datacount for all angles in the given area, else null
*/
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 {
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;
}
@@ -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,8 +83,9 @@ 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();
double overallConfidence = (originConfidence + calcDataCountConfidence(dataCountPerAngle, estimatedPeak)) / 2;
@@ -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;
}
@@ -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;
@@ -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;
}
@@ -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];
@@ -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 {
double angleEstimated = angleRegression.getEstimatedY(angleToTheWind);
double windSpeedEstimated = windSpeedRegression.getEstimatedY(windSpeed);
return (angleEstimated + windSpeedEstimated) / 2.0;
if (useLinearRegression) {
double angleEstimated = angleRegression.getEstimatedY(angleToTheWind);
double windSpeedEstimated = windSpeedRegression.getEstimatedY(windSpeed);
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) {
@@ -11,5 +11,7 @@ public interface IncrementalLinearRegressionProcessor {
double getIntercept();
int getDataCount();
double getMeanOfY() throws NotEnoughDataHasBeenAddedException;
}
@@ -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;
}
}
@@ -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);