Added option in UI to display cached boat class polars

also added optimal downwind speed calculation to polar api
This commit is contained in:
Frederik Petersen committed 2014-01-08 17:24:55 +01:00
1 parent fb9339b8d7
commit dcdd746844
10 files changed
+143 -3

No files matched your search

@@ -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);
@@ -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);
}
@@ -910,4 +910,8 @@ public interface StringMessages extends Messages {
String distanceToLine();
String beatAngleTooltip();
String beatAngle();
String showBoatClassChartsLabel();
String showDiagram();
}
@@ -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
@@ -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
@@ -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
@@ -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;
}
}
@@ -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;
@@ -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;
}
}
@@ -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