mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-16 17:21:00 +00:00
Added option in UI to display cached boat class polars
also added optimal downwind speed calculation to polar api
This commit is contained in:
1 parent
fb9339b8d7
commit
dcdd746844
10 files changed
+143
-3
No files matched your search
+4
@@ -308,6 +308,10 @@ public interface SailingService extends RemoteService {
|
||||
PolarSheetGenerationResponse generatePolarSheetForRaces(List<RegattaAndRaceIdentifier> selectedRaces,
|
||||
PolarSheetGenerationSettings settings, String name) throws Exception;
|
||||
|
||||
List<String> getBoatClassNamesWithPolarSheetsAvailable();
|
||||
|
||||
PolarSheetGenerationResponse showCachedPolarSheetForBoatClass(String boatClassName);
|
||||
|
||||
CourseAreaDTO createCourseArea(String eventIdAsString, String courseAreaName);
|
||||
|
||||
EventDTO getEventByIdAsString(String eventIdAsString);
|
||||
|
||||
+5
@@ -485,5 +485,10 @@ public interface SailingServiceAsync {
|
||||
void removeIgtimiAccount(String eMailOfAccountToRemove, AsyncCallback<Void> asyncCallback);
|
||||
|
||||
void importWindFromIgtimi(List<RaceDTO> selectedRaces, AsyncCallback<Map<RegattaAndRaceIdentifier, Integer>> asyncCallback);
|
||||
|
||||
void getBoatClassNamesWithPolarSheetsAvailable(AsyncCallback<List<String>> asyncCallback);
|
||||
|
||||
void showCachedPolarSheetForBoatClass(String boatClassName,
|
||||
AsyncCallback<PolarSheetGenerationResponse> asyncCallback);
|
||||
}
|
||||
|
||||
+4
@@ -910,4 +910,8 @@ public interface StringMessages extends Messages {
|
||||
String distanceToLine();
|
||||
String beatAngleTooltip();
|
||||
String beatAngle();
|
||||
|
||||
String showBoatClassChartsLabel();
|
||||
|
||||
String showDiagram();
|
||||
}
|
||||
+2
@@ -898,3 +898,5 @@ countDownInMillis={0}s to start
|
||||
distanceToLine=Distance to start line
|
||||
beatAngleTooltip=The angle between the competitors direction and the wind
|
||||
beatAngle=Beat Angle
|
||||
showBoatClassChartsLabel=You can also view the overall diagram for the available boat classes.
|
||||
showDiagram=Show Diagram
|
||||
+2
@@ -894,3 +894,5 @@ countDownInMillis={0}s bis zum Start
|
||||
distanceToLine=Abstand zur Startlinie
|
||||
beatAngleTooltip=Der Winkel zwischen Windrichtung und Fahrtrichtung des Teilnehmers
|
||||
beatAngle=Winkel zum Wind
|
||||
showBoatClassChartsLabel=Im folgenden können außerdem auch die existierenden Diagramme für die vorhandenen Bootsklassen angezeigt werden.
|
||||
showDiagram=Diagramm anzeigen
|
||||
+54
@@ -12,9 +12,11 @@ import com.google.gwt.event.dom.client.ClickHandler;
|
||||
import com.google.gwt.user.client.Timer;
|
||||
import com.google.gwt.user.client.Window;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.Button;
|
||||
import com.google.gwt.user.client.ui.DockLayoutPanel;
|
||||
import com.google.gwt.user.client.ui.HorizontalPanel;
|
||||
import com.google.gwt.user.client.ui.Label;
|
||||
import com.google.gwt.user.client.ui.ListBox;
|
||||
import com.google.gwt.user.client.ui.ScrollPanel;
|
||||
import com.google.gwt.user.client.ui.SplitLayoutPanel;
|
||||
import com.google.gwt.user.client.ui.TextBox;
|
||||
@@ -93,6 +95,7 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC
|
||||
namingBox = new TextBox();
|
||||
namingContainer.add(namingBox);
|
||||
leftPanel.add(namingContainer);
|
||||
addBoatClassDiagramArea(leftPanel);
|
||||
DockLayoutPanel rightPanel = new DockLayoutPanel(Unit.PCT);
|
||||
PolarSheetsChartPanel polarSheetsChartPanel = createPolarSheetsChartPanel();
|
||||
DockLayoutPanel polarChartAndControlPanel = new DockLayoutPanel(Unit.PCT);
|
||||
@@ -119,6 +122,57 @@ public class PolarSheetsPanel extends SplitLayoutPanel implements RaceSelectionC
|
||||
chartPanel.setSettings(initialSettings);
|
||||
}
|
||||
|
||||
private void addBoatClassDiagramArea(VerticalPanel leftPanel) {
|
||||
leftPanel.add(new Label(stringMessages.showBoatClassChartsLabel()));
|
||||
final ListBox boatClassListBox = createBoatClassListBox();
|
||||
leftPanel.add(boatClassListBox);
|
||||
Button showBoatClassDiagramButton = new Button(stringMessages.showDiagram());
|
||||
showBoatClassDiagramButton.addClickHandler(new ClickHandler() {
|
||||
|
||||
@Override
|
||||
public void onClick(ClickEvent event) {
|
||||
chartPanel.showLoadingInfo();
|
||||
sailingService.showCachedPolarSheetForBoatClass(
|
||||
boatClassListBox.getItemText(boatClassListBox.getSelectedIndex()),
|
||||
new AsyncCallback<PolarSheetGenerationResponse>() {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getLocalizedMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(PolarSheetGenerationResponse result) {
|
||||
String name = createNameForPolarSheet(result);
|
||||
chartPanel.getPolarSheetsDataMap().put(result.getId(), result.getData());
|
||||
chartPanel.setData(name, result.getData());
|
||||
chartPanel.hideLoadingInfo();
|
||||
}
|
||||
});
|
||||
}
|
||||
});
|
||||
leftPanel.add(showBoatClassDiagramButton);
|
||||
}
|
||||
|
||||
private ListBox createBoatClassListBox() {
|
||||
final ListBox boatClassListBox = new ListBox();
|
||||
sailingService.getBoatClassNamesWithPolarSheetsAvailable(new AsyncCallback<List<String>>() {
|
||||
|
||||
@Override
|
||||
public void onFailure(Throwable caught) {
|
||||
errorReporter.reportError(caught.getLocalizedMessage());
|
||||
}
|
||||
|
||||
@Override
|
||||
public void onSuccess(List<String> result) {
|
||||
for (String item : result) {
|
||||
boatClassListBox.addItem(item);
|
||||
}
|
||||
}
|
||||
});
|
||||
return boatClassListBox;
|
||||
}
|
||||
|
||||
private void setEventListenersForPolarSheetChart() {
|
||||
PointSelectEventHandler pointSelectEventHandler = new PointSelectEventHandler() {
|
||||
@Override
|
||||
|
||||
+20
@@ -3138,6 +3138,25 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
return new PolarSheetGenerationResponseImpl(id, name, result);
|
||||
}
|
||||
|
||||
@Override
|
||||
public List<String> getBoatClassNamesWithPolarSheetsAvailable() {
|
||||
Set<BoatClass> boatClasses = getService().getPolarDataService().getAllBoatClassesWithPolarSheetsAvailable();
|
||||
List<String> names = new ArrayList<String>();
|
||||
for (BoatClass boatClass : boatClasses) {
|
||||
names.add(boatClass.getName());
|
||||
}
|
||||
return names;
|
||||
}
|
||||
|
||||
@Override
|
||||
public PolarSheetGenerationResponse showCachedPolarSheetForBoatClass(String boatClassName) {
|
||||
BoatClass boatClass = getService().getBaseDomainFactory().getOrCreateBoatClass(boatClassName);
|
||||
PolarSheetsData data = getService().getPolarDataService().getPolarSheetForBoatClass(boatClass);
|
||||
String name = boatClassName + "_OVERALL";
|
||||
String id = name;
|
||||
return new PolarSheetGenerationResponseImpl(id, name, data);
|
||||
}
|
||||
|
||||
private String getCommonBoatClass(Set<TrackedRace> trackedRaces) {
|
||||
BoatClass boatClass = null;
|
||||
for (TrackedRace race : trackedRaces) {
|
||||
@@ -3684,4 +3703,5 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
}
|
||||
+21
-1
@@ -25,7 +25,7 @@ public class PolarSheetAnalyzerTest {
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0.2, 0.3, 0.4, 0.5, 0.501, 0.502, 0.503, 0.504, 0.505, 0.506, 0.507,
|
||||
0.508, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, {} };
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 }, {} };
|
||||
PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null,
|
||||
settings.getWindStepping(), null);
|
||||
PolarSheetAnalyzer analyzer = new PolarSheetAnalyzer(new MockedPolarDataService(null, data));
|
||||
@@ -34,6 +34,26 @@ public class PolarSheetAnalyzerTest {
|
||||
Assert.assertEquals(45, result.getBearing().getDegrees(), 0.01);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testOptimalDownwindSpeedCalculation() {
|
||||
PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings();
|
||||
Number[][] averagedPolarDataByWindSpeed = {
|
||||
{},
|
||||
{ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 4, 4.5, 4, 3, 2, 1, 0, 0, 0,
|
||||
0, 0, 0, 0, 0, 0, 0 }, {} };
|
||||
PolarSheetsData data = new PolarSheetsDataImpl(averagedPolarDataByWindSpeed, 0, null,
|
||||
settings.getWindStepping(), null);
|
||||
PolarSheetAnalyzer analyzer = new PolarSheetAnalyzer(new MockedPolarDataService(null, data));
|
||||
SpeedWithBearing result = analyzer.getOptimalDownwindSpeedWithBearingFor(null, new KnotSpeedImpl(6));
|
||||
Assert.assertEquals(4.5 * Math.sin(Math.toRadians(85)), result.getKnots(), 0.01);
|
||||
Assert.assertEquals(175, result.getBearing().getDegrees(), 0.01);
|
||||
}
|
||||
|
||||
private class MockedPolarDataService extends PolarDataServiceImpl {
|
||||
|
||||
private PolarSheetsData mockedData;
|
||||
|
||||
+30
@@ -27,6 +27,9 @@ public class PolarSheetAnalyzer {
|
||||
Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed();
|
||||
int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots());
|
||||
Number[] dataForProvidedWindSpeed = dataPerWindSpeed[windSpeedIndex];
|
||||
if (dataForProvidedWindSpeed.length == 360) {
|
||||
dataForProvidedWindSpeed = convertToOneSidedPolar(dataForProvidedWindSpeed);
|
||||
}
|
||||
double optUpwindSpeed = 0;
|
||||
int optUpwindDeg = 0;
|
||||
for (int i = 0; i < 90; i++) {
|
||||
@@ -40,4 +43,31 @@ public class PolarSheetAnalyzer {
|
||||
return new KnotSpeedWithBearingImpl(optUpwindSpeed, new DegreeBearingImpl(optUpwindDeg));
|
||||
}
|
||||
|
||||
public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) {
|
||||
PolarSheetsData data = polarDataService.getPolarSheetForBoatClass(boatClass);
|
||||
Number[][] dataPerWindSpeed = data.getAveragedPolarDataByWindSpeed();
|
||||
int windSpeedIndex = data.getStepping().getLevelIndexForValue(windSpeed.getKnots());
|
||||
Number[] dataForProvidedWindSpeed = dataPerWindSpeed[windSpeedIndex];
|
||||
double optDownwindSpeed = 0;
|
||||
int optUpwindDeg = 0;
|
||||
for (int i = 90; i < 180; i++) {
|
||||
Number speed = dataForProvidedWindSpeed[i];
|
||||
double speedUpwind = speed.doubleValue() * Math.sin(Math.toRadians(i - 90));
|
||||
if (speedUpwind > optDownwindSpeed) {
|
||||
optUpwindDeg = i;
|
||||
optDownwindSpeed = speedUpwind;
|
||||
}
|
||||
}
|
||||
return new KnotSpeedWithBearingImpl(optDownwindSpeed, new DegreeBearingImpl(optUpwindDeg));
|
||||
}
|
||||
|
||||
private Number[] convertToOneSidedPolar(Number[] dataForProvidedWindSpeed) {
|
||||
Number[] oneSided = new Number[180];
|
||||
for (int i = 181; i < 360; i++) {
|
||||
oneSided[360 - i] = (dataForProvidedWindSpeed[360 - i].doubleValue() + dataForProvidedWindSpeed[i]
|
||||
.doubleValue()) / 2;
|
||||
}
|
||||
return oneSided;
|
||||
}
|
||||
|
||||
}
|
||||
+1
-2
@@ -54,8 +54,7 @@ public class PolarDataServiceImpl implements PolarDataService {
|
||||
|
||||
@Override
|
||||
public SpeedWithBearing getOptimalDownwindSpeedWithBearingFor(BoatClass boatClass, Speed windSpeed) {
|
||||
// TODO Auto-generated method stub
|
||||
return null;
|
||||
return polarSheetAnalyzer.getOptimalDownwindSpeedWithBearingFor(boatClass, windSpeed);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
Reference in new issue
Block a user