diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java index 49843a04b40..bc6bae99dfb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorEditDialog.java @@ -1,5 +1,9 @@ package com.sap.sailing.gwt.ui.adminconsole; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.safehtml.shared.SafeHtmlUtils; +import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Label; @@ -39,6 +43,12 @@ public abstract class CompetitorEditDialog private final URLFieldWithFileUpload flagImageURL; private final URLFieldWithFileUpload imageUrlAndUploadComposite; + private final Label yardstickLabel; + private final FlowPanel yardstickPanel; + private final DoubleBox yardstickNumber; + private double yardstickScale = 100; + private boolean enableYardstickScaleDetection = true; + private boolean yardstickLastModified = true; private final DoubleBox timeOnTimeFactor; private final DoubleBox timeOnDistanceAllowanceInSecondsPerNauticalMile; @@ -106,7 +116,35 @@ public abstract class CompetitorEditDialog this.flagImageURL.setURL(competitorToEdit.getFlagImageURL()); this.imageUrlAndUploadComposite = new URLFieldWithFileUpload(stringMessages); this.imageUrlAndUploadComposite.setURL(competitorToEdit.getImageURL()); + this.yardstickLabel = new Label(stringMessages.yardstickNumber(yardstickScale)); + this.yardstickNumber = createDoubleBox(competitorToEdit.getTimeOnTimeFactor() == null ? null + : convertYardstickTimeOnTime(competitorToEdit.getTimeOnTimeFactor(), yardstickScale), 10); + Button yardstickScaleButton = new Button(SafeHtmlUtils.fromSafeConstant(stringMessages.changeScale())); + yardstickScaleButton.getElement().getStyle().setMarginLeft(3, Unit.PX); + yardstickScaleButton.addClickHandler(event -> { + enableYardstickScaleDetection = false; + setYardstickScale(yardstickScale == 1000 ? 100 : 1000, /* convertYardstickNumber */ !yardstickLastModified); + }); + this.yardstickPanel = new FlowPanel(); + this.yardstickPanel.add(yardstickNumber); + this.yardstickPanel.add(yardstickScaleButton); this.timeOnTimeFactor = createDoubleBox(competitorToEdit.getTimeOnTimeFactor(), 10); + this.yardstickNumber.addChangeHandler(event -> { + if (yardstickNumber.getValue() == null) { + enableYardstickScaleDetection = true; + } + detectAndSetYardstickScale(); + if (yardstickNumber.getValue() != null) { + timeOnTimeFactor.setValue(convertYardstickTimeOnTime(yardstickNumber.getValue(), yardstickScale), /* fireEvents */ false); + } + yardstickLastModified = true; + }); + this.timeOnTimeFactor.addChangeHandler(event -> { + if (timeOnTimeFactor.getValue() != null) { + yardstickNumber.setValue(convertYardstickTimeOnTime(timeOnTimeFactor.getValue(), yardstickScale), /* fireEvents */ false); + } + yardstickLastModified = false; + }); this.timeOnDistanceAllowanceInSecondsPerNauticalMile = createDoubleBox( competitorToEdit.getTimeOnDistanceAllowancePerNauticalMile() == null ? null : competitorToEdit .getTimeOnDistanceAllowancePerNauticalMile().asSeconds(), 10); @@ -157,7 +195,7 @@ public abstract class CompetitorEditDialog */ protected abstract CompetitorType getResult(); - protected CompetitorDTO getBaseResult() { + protected CompetitorDTO getBaseResult() { Color color; if (displayColorTextBox.getText() == null || displayColorTextBox.getText().isEmpty()) { color = null; @@ -181,9 +219,47 @@ public abstract class CompetitorEditDialog return result; } + /** + * Sets the yardstick scale to the given value and updates {@link #yardstickLabel}. + * @param scale {@code double} value to set scale to. + * @param convertYardstickNumber if {@code true} the current yardstick number will be converted to the new scale. + */ + protected void setYardstickScale(double scale, boolean convertYardstickNumber) { + if (convertYardstickNumber && yardstickNumber.getValue() != null) { + final double norm = yardstickNumber.getValue() / yardstickScale; + yardstickNumber.setValue(norm * scale, /* fireEvents */ false); + } + yardstickScale = scale; + yardstickLabel.setText(stringMessages.yardstickNumber(yardstickScale)); + } + + /** + * If {@link #enableYardstickScaleDetection} is set {@link #yardstickScale} will be set to {@code 100} or + * {@code 1000} depending on which is closer to the current {@link #yardstickNumber}. + */ + private void detectAndSetYardstickScale() { + if (enableYardstickScaleDetection) { + Double number = yardstickNumber.getValue(); + if (number != null) { + if (number < 550) { + setYardstickScale(100, false); + } else { + setYardstickScale(1000, false); + } + } + } + } + + /** + * Converts between yardstick number and time-on-time factor and vice-versa. + */ + private static double convertYardstickTimeOnTime(double yardstickOrTimeOnTime, double scale) { + return scale / yardstickOrTimeOnTime; + } + @Override protected Widget getAdditionalWidget() { - Grid result = new Grid(10, 2); + Grid result = new Grid(11, 2); result.setWidget(0, 0, new Label(stringMessages.name())); result.setWidget(0, 1, name); result.setWidget(1, 0, new Label(stringMessages.shortName())); @@ -200,10 +276,12 @@ public abstract class CompetitorEditDialog result.setWidget(6, 1, flagImageURL); result.setWidget(7, 0, new Label(stringMessages.imageURL())); result.setWidget(7, 1, imageUrlAndUploadComposite); - result.setWidget(8, 0, new Label(stringMessages.timeOnTimeFactor())); - result.setWidget(8, 1, timeOnTimeFactor); - result.setWidget(9, 0, new Label(stringMessages.timeOnDistanceAllowanceInSecondsPerNauticalMile())); - result.setWidget(9, 1, timeOnDistanceAllowanceInSecondsPerNauticalMile); + result.setWidget(8, 0, yardstickLabel); + result.setWidget(8, 1, yardstickPanel); + result.setWidget(9, 0, new Label(stringMessages.timeOnTimeFactor())); + result.setWidget(9, 1, timeOnTimeFactor); + result.setWidget(10, 0, new Label(stringMessages.timeOnDistanceAllowanceInSecondsPerNauticalMile())); + result.setWidget(10, 1, timeOnDistanceAllowanceInSecondsPerNauticalMile); return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 1f6c5484b22..9db73cf9b49 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -1428,6 +1428,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String orcPerformanceCurveByImpliedWindRankingMetricDescription(); String orcPerformanceCurveLeaderForBaselineRankingMetricDescription(); String timeOnTimeAndDistanceRankingMetricDescription(); + String yardstickNumber(double scale); + String changeScale(); String timeOnTimeFactor(); String timeOnDistanceAllowanceInSecondsPerNauticalMile(); String calculatedTimeTraveled(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 522ceb16254..2cc5631d323 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -1420,6 +1420,8 @@ orcPerformanceCurveRankingMetricDescription=ORC Performance Curve: Sophisticated orcPerformanceCurveByImpliedWindRankingMetricDescription=ORC Performance Curve: Sophisticated handicapping scheme that awards time allowances based on a VPP/polar assuming pre-defined legs'' angles to the wind;\nbased on these allowances, an "implied wind" is calculated which is the basis for ranking.\nImplied wind can be calculated at any time in the race and is therefore the basis for in-race ranking. orcPerformanceCurveLeaderForBaselineRankingMetricDescription=ORC Performance Curve: Sophisticated handicapping scheme that awards time allowances based on a VPP/polar assuming pre-defined legs'' angles to the wind;\nthe greatest implied wind value is determined; all boats are ranked by mapping this greatest implied wind to their performance curves, determining their allowance for that implied wind\nand then computing the delta to their elapsed time. The baseline for corrected times is the boat with the least relative corrected time. timeOnTimeAndDistanceRankingMetricDescription=Time-on-Time / Time-on-Distance: Final ranking by calculated time;\ntime is calculated by optionally applying a factor to the actual time (defaulting to 1.0)\nand subtracting an allowance in seconds per nautical mile,\napplied to the course length.\nIn-race ranks based on expected arrival at fastest boat''s current position\nassuming current leg''s average VMG/VMC. +yardstickNumber=Yardstick Number (Scale = {0}) +changeScale=Change Scale timeOnTimeFactor=ToT Factor timeOnDistanceAllowanceInSecondsPerNauticalMile=ToD Allowance timeOnTimeFactorTooltip=Time-on-Time factor; actual time is multiplied with this factor in calculated time formula diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties index 1caef570fe3..a5a07511fdb 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -1400,6 +1400,8 @@ orcPerformanceCurveRankingMetricDescription=ORC Performance Curve (seit 2015): A orcPerformanceCurveByImpliedWindRankingMetricDescription=ORC Performance Curve nach impliziertem Wind (vor 2015): Aufwendiges Handicap-System, das jedem Teilnehmer erwartete Geschwindigkeiten für bestimmte Winkel eines Bahnschenkels zum Wind zuteilt.\nAuf dieser Grundlage wird der sogenannte "implizierte Wind" bestimmt,\nalso der Wind, den der Teilnehmer gehabt haben müßte, um die Strecke in der vorliegenden Zeit zu segeln.\nDieser "implizierte Wind" ist die Grundlage für die Plazierungen\nund kann zu jeder Zeit während Wettfahrt bestimmt werden. orcPerformanceCurveLeaderForBaselineRankingMetricDescription=ORC Performance Curve (seit 2015): Aufwendiges Handicap-System, das jedem Teilnehmer erwartete Geschwindigkeiten für bestimmte Winkel eines Bahnschenkels zum Wind zuteilt.\nAuf dieser Grundlage wird der sogenannte "implizierte Wind" bestimmt,\nalso der Wind, den der Teilnehmer gehabt haben müßte, um die Strecke in der vorliegenden Zeit zu segeln.\nDer höchste implizierte Windwert wird ermittelt; für alle Teilnehmer wir dieser Wert auf ihre Performance Curve übertragen, ergibt eine erlaubte Zeit, die dann mit der tatsächlich gesegelten Zeit verglichen wird. Die Differenz wird zur gesegelten Zeit des führenden Bootes addiert und ergibt die korrigierte Zeit. timeOnTimeAndDistanceRankingMetricDescription=Time-on-Time / Time-on-Distance: Abschließende Rangreihenfolge nach korrigierter Zeit;\nDie Zeit wird korrigiert, indem ein optionaler Faktor an die tatsächlich gesegelte Zeit angebracht wird;\nZusätzlich kann eine optionale erwartete Zeit pro Seemeile, skaliert auf die Renndistanz, subtrahiert werden, angewandt auf die Kurslänge\nWährend der Wettfahrt wird die Rangreihenfolge anhand der korrigierten erwarteten Zeit bei Ankunft an der Position\ndes jetzt absolut in Führung liegenden Teilnehmers bestimmt,\nbasierend auf dem durchschnittlichen VMG/VMC des momentanen Bahnschenkels +yardstickNumber=Yardstickzahl (Maßstab = {0}) +changeScale=Maßstab ändern timeOnTimeFactor=ToT Faktor timeOnDistanceAllowanceInSecondsPerNauticalMile=ToD Zuteilung timeOnTimeFactorTooltip=Zeit-auf-Zeit Faktor; tatsächlich gesegelte Zeit wird für die berechnete Zeit mit diesem Faktor multipliziert