From 9b610c6c383b4de3b63585d324bd1e9270f9af3a Mon Sep 17 00:00:00 2001 From: fmittag Date: Mon, 9 Jan 2012 16:15:10 +0100 Subject: [PATCH 01/33] first try of scaling boat icons on the map --- .../sailing/gwt/ui/adminconsole/ImageRotator.java | 3 ++- .../sailing/gwt/ui/adminconsole/RaceMapPanel.java | 12 +++++++++++- 2 files changed, 13 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java index f8175b0c9a7..88e22340dec 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java @@ -61,7 +61,7 @@ public class ImageRotator { * If the platform supports the {@link Canvas} element, returns a view rotated by degrees degrees. Otherwise, * the {@link #getUnrotatedImageURL() unrotated image's URL} is returned. */ - public String getRotatedImageURL(double angleInDegrees) { + public String getRotatedImageURL(double angleInDegrees, double scaleFactor) { String result = getUnrotatedImageURL(); if (canvas != null) { if (imageElement != null) { @@ -70,6 +70,7 @@ public class ImageRotator { context.save(); context.translate(canvasRadius, canvasRadius); context.rotate(angleInRadians); + context.scale(scaleFactor, scaleFactor); context.drawImage(imageElement, -imageWidth/2, -imageHeight/2); result = canvas.toDataUrl("image/png"); context.restore(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java index b33e14ae1b4..38129369515 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java @@ -802,6 +802,7 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe if (newBounds != null) { map.setZoomLevel(map.getBoundsZoomLevel(newBounds)); map.setCenter(newBounds.getCenter()); + map.checkResizeAndCenter(); mapFirstZoomDone = true; /* * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that the map was just zoomed @@ -891,7 +892,16 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe } private String getBoatImageURL(ImageRotator boatImageRotator, GPSFixDAO boatFix) { - return boatImageRotator.getRotatedImageURL(boatFix.speedWithBearing.bearingInDegrees); + // the possible zoom level range is 0 to 21 (zoom level 0 would show the whole world) + int zoomLevel = map.getZoomLevel(); + double minScaleFactor = 0.1; + double maxScaleFactor = 1.0; + + double scaleDiffPerZoomLevel = (maxScaleFactor - minScaleFactor) / 21.0; + + double scaleFactor = minScaleFactor + scaleDiffPerZoomLevel * zoomLevel; + + return boatImageRotator.getRotatedImageURL(boatFix.speedWithBearing.bearingInDegrees, scaleFactor); } private Icon getBoatImageIcon(GPSFixDAO boatFix, boolean highlighted) { From 261e8aac16e5105bff1c9ffde2e02cfc79081ed0 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jan 2012 16:18:17 +0100 Subject: [PATCH 02/33] added getConfidence() with comment --- .../sailing/domain/base/HasConfidence.java | 25 ++++++++++++++++++- 1 file changed, 24 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/HasConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/HasConfidence.java index 60ac6a81771..aab19b49ff6 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/HasConfidence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/HasConfidence.java @@ -13,5 +13,28 @@ package com.sap.sailing.domain.base; * */ public interface HasConfidence { - + /** + * A confidence is a number between 0.0 and 1.0 (inclusive) where 0.0 means that the value is randomly guessed while + * 1.0 means the value is authoritatively known for a fact. It represents the weight with which a value is to be + * considered by averaging, interpolation and extrapolation algorithms. + *

+ * + * An averaging algorithm for a sequence of n tuples (v1, c1), ..., (vn, cn) of a value + * vi with a confidence ci each can for example look like this: + * a := (c1*v1 + ... + cn*vn) / (c1 + ... + cn). For a single value with a confidence this trivially + * results in c1*v1 / (c1) which is equivalent to v1. As another example, consider two + * values with equal confidence 0.8. Then, a := (0.8*v1 + 0.8*vn) / (0.8 + 0.8) which + * resolves to 0.5*v1 + 0.5*v2 which is obviously the arithmetic mean of the two values. If one value + * has confidence 0.8 and the other 0.4, then + * a := (0.8*v1 + 0.4*vn) / (0.8 + 0.4) which resolves to 2/3*v1 + 1/3*v2 which is a + * weighed average. + *

+ * + * Note, that this doesn't exactly take facts for facts. In other words, if one value is provided with a confidence + * of 1.0, the average may still be influenced by other values. However, this cleanly resolves otherwise + * mutually contradictory "facts" such a (v1, 1.0), (v2, 1.0) with v1 != v2. It is + * considered bad practice to claim a fact as soon as it results from any kind of measurement or estimation. All + * measurement devices produce some statistical errors, no matter how small (cf. Heisenberg ;-) ). + */ + double getConfidence(); } From 3e81f8211fe6cba8a43e48c472cbca0f953be7ae Mon Sep 17 00:00:00 2001 From: fmittag Date: Mon, 9 Jan 2012 16:19:28 +0100 Subject: [PATCH 03/33] removed maven repositories (profiles) and mirrors from settings.xml --- settings.xml | 40 ---------------------------------------- 1 file changed, 40 deletions(-) diff --git a/settings.xml b/settings.xml index 7a871fa4fde..0da14730939 100644 --- a/settings.xml +++ b/settings.xml @@ -15,46 +15,6 @@ localhost|*.sap.corp - - - - snapshot.build - - - build.snapshots - http://nexus.wdf.sap.corp:8081/nexus/content/groups/build.snapshots/ - - - - - central - http://repo1.maven.org/maven2 - - - p2 - eclipse - http://download.eclipse.org/releases/indigo/ - - - build.snapshots - http://nexus.wdf.sap.corp:8081/nexus/content/groups/build.snapshots/ - - - - - - - - - - leandi.nexus.mirror - http://nexus.wdf.sap.corp:8081/nexus/content/groups/build.snapshots/ - *,!build.snapshots,!build.milestones,!sonar - - - - - From 4c4cad95449f6acc80054a59cf0804a33e13a82a Mon Sep 17 00:00:00 2001 From: fmittag Date: Mon, 9 Jan 2012 17:02:54 +0100 Subject: [PATCH 04/33] adjust ADMIN doc to the new build process --- doc/ADMIN | 47 +++++++++++++++++++++++++++++++++++------------ doc/DEVELOPMENT | 10 ++++++++-- 2 files changed, 43 insertions(+), 14 deletions(-) diff --git a/doc/ADMIN b/doc/ADMIN index 2a022c71735..cd2664cc070 100755 --- a/doc/ADMIN +++ b/doc/ADMIN @@ -145,20 +145,18 @@ First, check out the branch you want to re-build: - ensure that there is an ssh tunnel to localhost that - - "mvn deploy" + - "mvn install" This will compile the sources, bundle them up in to OSGi bundle JAR files, run all tests which required online access to the TracTrac -servers, and, if successful, deploy the new JAR files to -maven-repositories/ where is one of dev, -test or prod. +servers, and, if successful, creates an eclipse product in the directory +/home/trac/git/java/com.sap.sailing.feature.p2build/bin/products/raceanalysis.product.id/linux/gtk/x86_64 +which contains all files required to install and run the build result. -Note that the running server(s) are not updated automatically by -deploying the new bundle versions to the maven-repositories -subdirectory. See next topic. +Note that the running server(s) are not updated automatically. See next topic. -Updating an OSGi Server Instance --------------------------------- +OSGi Server Instances +--------------------- We usually have four OSGi consoles running with a terminal window each, visible at the VNC server running on por 5902. The title bar @@ -170,11 +168,36 @@ At the "osgi>" prompt of the OSGi console you can type - "exit" -to terminate the particular Java server instance. To update the server -instance from its respective maven repository (located under -~/maven-repositories/...) you can issue +to terminate the particular Java server instance. +You can either install a complete new server instance or just update an existing one. +Install a new OSGi Server Instance +---------------------------------- + +To install a complete new server instance you can issue + + - "./install" + +This command will delete the old installation and copy all required files +from the build result to the right directories of the server instance. + +To start the server instance again, type + + - "./start" + +and the server should welcome you again with the "osgi>" prompt after +a successful start. + + +Update an existing OSGi Server Instance +----------------------------------------- + +To update the server you can issue + - "./update" + +This command will NOT delete the old installation but copy the updated plugins +from the build result to the right directories of the server instance. To start the server instance again, type diff --git a/doc/DEVELOPMENT b/doc/DEVELOPMENT index 8a06ce4c30b..aab1602948b 100644 --- a/doc/DEVELOPMENT +++ b/doc/DEVELOPMENT @@ -52,8 +52,8 @@ com.sap.sailing.feature -> contains the feature.xml for the race analysis projec com.sap.sailing.feature.runtime -> contains the feature.xml for additional runtime libraries which are not required for the source build (e.g. jetty.osgi bundle) com.sap.sailing.feature.p2build -> contains the product defintion (.product file) for the race analysis project (required for the product build) -Typical usecases for changing the target platform definition ------------------------------------------------------ +Typical usecases changing the target platform definition +-------------------------------------------------------- 1. Adding an external open source library to the target-platform @@ -70,4 +70,10 @@ g) Submit the changes to the git repository 3. Adding a p2 repository as new source of libraries +Typical usecases changing the features and product definition +------------------------------------------------------------- + +4. Adding a new bundle project + + From e5892695c6dda3f7d1ef3bca283efe048ff72d22 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jan 2012 17:46:41 +0100 Subject: [PATCH 05/33] added a first confidence calculation placeholder and test case --- .../sailing/domain/test/ConfidenceTest.java | 67 +++++++++++++++++++ .../META-INF/MANIFEST.MF | 2 + .../{base => confidence}/HasConfidence.java | 9 ++- .../domain/confidence/ScalableValue.java | 11 +++ .../impl/ConfidenceBasedAveragerImpl.java | 47 +++++++++++++ 5 files changed, 134 insertions(+), 2 deletions(-) create mode 100755 java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java rename java/com.sap.sailing.domain/src/com/sap/sailing/domain/{base => confidence}/HasConfidence.java (88%) create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ScalableValue.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java new file mode 100755 index 00000000000..3b76d8f6f4b --- /dev/null +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java @@ -0,0 +1,67 @@ +package com.sap.sailing.domain.test; + +import static org.junit.Assert.assertEquals; + +import org.junit.Test; + +import com.sap.sailing.domain.confidence.HasConfidence; +import com.sap.sailing.domain.confidence.ScalableValue; +import com.sap.sailing.domain.confidence.impl.ConfidenceBasedAveragerImpl; + +public class ConfidenceTest { + private static class ScalableDouble implements ScalableValue { + private final Double d; + + public ScalableDouble(Double d) { + this.d = d; + } + + @Override + public ScalableValue multiply(double factor) { + return new ScalableDouble(d * factor); + } + + @Override + public ScalableValue add(ScalableValue t) { + return new ScalableDouble(d+t.getValue()); + } + + @Override + public Double divide(double divisor) { + return d / divisor; + } + + @Override + public Double getValue() { + return d; + } + } + + private static class ScalableDoubleWithConfidence extends ScalableDouble implements HasConfidence { + private final double confidence; + + public ScalableDoubleWithConfidence(double d, double confidence) { + super(d); + this.confidence = confidence; + } + + @Override + public double getConfidence() { + return confidence; + } + + @Override + public ScalableValue getScalableValue() { + return this; + } + } + + @Test + public void testAveragingSimpleDoubles() { + ScalableDoubleWithConfidence d1 = new ScalableDoubleWithConfidence(1., 0.5); + ScalableDoubleWithConfidence d2 = new ScalableDoubleWithConfidence(2., 0.5); + @SuppressWarnings("unchecked") + Double average = new ConfidenceBasedAveragerImpl().getAverage(d1, d2); + assertEquals(1.5, average, 0.00000001); + } +} diff --git a/java/com.sap.sailing.domain/META-INF/MANIFEST.MF b/java/com.sap.sailing.domain/META-INF/MANIFEST.MF index a3cf2225006..f64f045c08b 100755 --- a/java/com.sap.sailing.domain/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.domain/META-INF/MANIFEST.MF @@ -8,6 +8,8 @@ Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ClassPath: . Export-Package: com.sap.sailing.domain.base, com.sap.sailing.domain.base.impl, + com.sap.sailing.domain.confidence;x-friends:="com.sap.sailing.domain.test", + com.sap.sailing.domain.confidence.impl;x-friends:="com.sap.sailing.domain.test", com.sap.sailing.domain.leaderboard, com.sap.sailing.domain.leaderboard.impl, com.sap.sailing.domain.tracking, diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/HasConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java similarity index 88% rename from java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/HasConfidence.java rename to java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java index aab19b49ff6..2e42a7cbb9a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/HasConfidence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java @@ -1,4 +1,4 @@ -package com.sap.sailing.domain.base; +package com.sap.sailing.domain.confidence; /** * Some values, particularly those obtained from real-world measurements, are not always accurate. Some @@ -10,9 +10,11 @@ package com.sap.sailing.domain.base; * averaging these values more properly than would be possible without a confidence value. * * @author Axel Uhl (d043530) + * + * @param the type of the scalable value. See particularly the {@link #scale()} method. * */ -public interface HasConfidence { +public interface HasConfidence { /** * A confidence is a number between 0.0 and 1.0 (inclusive) where 0.0 means that the value is randomly guessed while * 1.0 means the value is authoritatively known for a fact. It represents the weight with which a value is to be @@ -37,4 +39,7 @@ public interface HasConfidence { * measurement devices produce some statistical errors, no matter how small (cf. Heisenberg ;-) ). */ double getConfidence(); + + ScalableValue getScalableValue(); + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ScalableValue.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ScalableValue.java new file mode 100755 index 00000000000..a65e5696786 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ScalableValue.java @@ -0,0 +1,11 @@ +package com.sap.sailing.domain.confidence; + +public interface ScalableValue { + ScalableValue multiply(double factor); + + ScalableValue add(ScalableValue t); + + AveragesTo divide(double divisor); + + ValueType getValue(); +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java new file mode 100755 index 00000000000..ffbeeb41231 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java @@ -0,0 +1,47 @@ +package com.sap.sailing.domain.confidence.impl; + +import com.sap.sailing.domain.confidence.HasConfidence; +import com.sap.sailing.domain.confidence.ScalableValue; + + +/** + * A set of values that {@link HasConfidence have a confidence attached} can be averaged using the confidence value to + * weigh values with greater confidence more than those with lesser confidence. It implements the following algorithm + * for a sequence of n tuples (v1, c1), ..., (vn, cn) of a value vi with a + * confidence ci each:

+ * + * a := (c1*v1 + ... + cn*vn) / (c1 + ... + cn). For a single value with a confidence this trivially + * results in c1*v1 / (c1) which is equivalent to v1. As another example, consider two values + * with equal confidence 0.8. Then, a := (0.8*v1 + 0.8*vn) / (0.8 + 0.8) which resolves to + * 0.5*v1 + 0.5*v2 which is obviously the arithmetic mean of the two values. If one value has confidence + * 0.8 and the other 0.4, then a := (0.8*v1 + 0.4*vn) / (0.8 + 0.4) which + * resolves to 2/3*v1 + 1/3*v2 which is a weighed average. + *

+ * + * Note, that this doesn't exactly take facts for facts. In other words, if one value is provided with a confidence of + * 1.0, the average may still be influenced by other values. However, this cleanly resolves otherwise + * mutually contradictory "facts" such a (v1, 1.0), (v2, 1.0) with v1 != v2. It is considered + * bad practice to claim a fact as soon as it results from any kind of measurement or estimation. All measurement + * devices produce some statistical errors, no matter how small (cf. Heisenberg ;-) ). + * + * @author Axel Uhl (d043530) + * + * @param + * the type of value to be scaled / averaged using their confidence + */ +public class ConfidenceBasedAveragerImpl { + public AveragesTo getAverage(HasConfidence... values) { + if (values == null || values.length == 0) { + return null; + } else { + ScalableValue numerator = values[0].getScalableValue().multiply(values[0].getConfidence()); + double confidenceSum = values[0].getConfidence(); + for (int i=1; i Date: Mon, 9 Jan 2012 18:08:49 +0100 Subject: [PATCH 06/33] added example for bearings with confidence in a test case --- .../sailing/domain/test/ConfidenceTest.java | 105 +++++++++++++++++- 1 file changed, 104 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java index 3b76d8f6f4b..0b3af4fddf5 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java @@ -1,9 +1,14 @@ package com.sap.sailing.domain.test; import static org.junit.Assert.assertEquals; +import static org.junit.Assert.assertNull; import org.junit.Test; +import com.sap.sailing.domain.base.Bearing; +import com.sap.sailing.domain.base.impl.DegreeBearingImpl; +import com.sap.sailing.domain.base.impl.RadianBearingImpl; +import com.sap.sailing.domain.common.Util.Pair; import com.sap.sailing.domain.confidence.HasConfidence; import com.sap.sailing.domain.confidence.ScalableValue; import com.sap.sailing.domain.confidence.impl.ConfidenceBasedAveragerImpl; @@ -56,12 +61,110 @@ public class ConfidenceTest { } } + private static class ScalableBearing implements ScalableValue, Bearing> { + private final double sin; + private final double cos; + + public ScalableBearing(Bearing bearing) { + this.sin = Math.sin(bearing.getRadians()); + this.cos = Math.cos(bearing.getRadians()); + } + + private ScalableBearing(double sin, double cos) { + this.sin = sin; + this.cos = cos; + } + + @Override + public ScalableValue, Bearing> multiply(double factor) { + Pair pair = getValue(); + return new ScalableBearing(factor*pair.getA(), factor*pair.getB()); + } + + @Override + public ScalableValue, Bearing> add(ScalableValue, Bearing> t) { + Pair value = getValue(); + Pair tValue = t.getValue(); + return new ScalableBearing(value.getA()+tValue.getA(), value.getB()+tValue.getB()); + } + + @Override + public Bearing divide(double divisor) { + double angle; + if (cos == 0) { + angle = sin >= 0 ? Math.PI / 2 : -Math.PI / 2; + } else { + angle = Math.atan2(sin, cos); + } + Bearing result = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); + return result; + } + + @Override + public Pair getValue() { + return new Pair(sin, cos); + } + } + + private static class ScalableBearingWithConfidence extends ScalableBearing implements HasConfidence, Bearing> { + private final double confidence; + + public ScalableBearingWithConfidence(Bearing bearing, double confidence) { + super(bearing); + this.confidence = confidence; + } + + @Override + public double getConfidence() { + return confidence; + } + + @Override + public ScalableValue, Bearing> getScalableValue() { + return this; + } + + } + @Test - public void testAveragingSimpleDoubles() { + public void testAveragingWithEmptyListYieldsNull() { + @SuppressWarnings("unchecked") + Double average = new ConfidenceBasedAveragerImpl().getAverage(); + assertNull(average); + } + + @Test + public void testAveragingWithNullArrayYieldsNull() { + Double average = new ConfidenceBasedAveragerImpl().getAverage((HasConfidence[]) null); + assertNull(average); + } + + @Test + public void testAveragingWithTwoDoubles() { ScalableDoubleWithConfidence d1 = new ScalableDoubleWithConfidence(1., 0.5); ScalableDoubleWithConfidence d2 = new ScalableDoubleWithConfidence(2., 0.5); @SuppressWarnings("unchecked") Double average = new ConfidenceBasedAveragerImpl().getAverage(d1, d2); assertEquals(1.5, average, 0.00000001); } + + @Test + public void testAveragingWithThreeDoubles() { + ScalableDoubleWithConfidence d1 = new ScalableDoubleWithConfidence(1., 1.); + ScalableDoubleWithConfidence d2 = new ScalableDoubleWithConfidence(2., 1.); + ScalableDoubleWithConfidence d3 = new ScalableDoubleWithConfidence(3., 2.); + @SuppressWarnings("unchecked") + Double average = new ConfidenceBasedAveragerImpl().getAverage(d1, d2, d3); + assertEquals(2.25, average, 0.00000001); + } + + @Test + public void testAveragingWithTwoBearings() { + ScalableBearingWithConfidence d1 = new ScalableBearingWithConfidence(new DegreeBearingImpl(350.), 1.); + ScalableBearingWithConfidence d2 = new ScalableBearingWithConfidence(new DegreeBearingImpl(10.), 1.); + @SuppressWarnings("unchecked") + Bearing average = new ConfidenceBasedAveragerImpl, Bearing>().getAverage(d1, d2); + assertEquals(0, average.getDegrees(), 0.00000001); + } + } From f72e32e935e0d72001c904fece80cb2ff4064315 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jan 2012 18:22:06 +0100 Subject: [PATCH 07/33] added an averaging test for bearing with confidence with three bearings with different confidences --- .../com/sap/sailing/domain/test/ConfidenceTest.java | 10 ++++++++++ 1 file changed, 10 insertions(+) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java index 0b3af4fddf5..58c2757e9f5 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java @@ -167,4 +167,14 @@ public class ConfidenceTest { assertEquals(0, average.getDegrees(), 0.00000001); } + @Test + public void testAveragingWithThreeoBearings() { + ScalableBearingWithConfidence d1 = new ScalableBearingWithConfidence(new DegreeBearingImpl(350.), 1.); + ScalableBearingWithConfidence d2 = new ScalableBearingWithConfidence(new DegreeBearingImpl(10.), 1.); + ScalableBearingWithConfidence d3 = new ScalableBearingWithConfidence(new DegreeBearingImpl(20.), 2.); + @SuppressWarnings("unchecked") + Bearing average = new ConfidenceBasedAveragerImpl, Bearing>().getAverage(d1, d2, d3); + assertEquals(10, average.getDegrees(), 0.1); + } + } From 04784c9e18b28cd91b99d9b5de83e7ac7430607e Mon Sep 17 00:00:00 2001 From: fmittag Date: Tue, 10 Jan 2012 14:59:14 +0100 Subject: [PATCH 08/33] refactoring of RaceMapPanel into RaceMapPanel, RaceMap und RaceMapData --- .../gwt/ui/adminconsole/ImageRotator.java | 2 +- .../sailing/gwt/ui/adminconsole/RaceMap.java | 844 ++++++++++++++ .../gwt/ui/adminconsole/RaceMapData.java | 197 ++++ .../gwt/ui/adminconsole/RaceMapPanel.java | 1031 +---------------- 4 files changed, 1103 insertions(+), 971 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapData.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java index 88e22340dec..38316668348 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java @@ -71,7 +71,7 @@ public class ImageRotator { context.translate(canvasRadius, canvasRadius); context.rotate(angleInRadians); context.scale(scaleFactor, scaleFactor); - context.drawImage(imageElement, -imageWidth/2, -imageHeight/2); + context.drawImage(imageElement, (-imageWidth/2) * scaleFactor, (-imageHeight/2) * scaleFactor); result = canvas.toDataUrl("image/png"); context.restore(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java new file mode 100644 index 00000000000..e2920590d31 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java @@ -0,0 +1,844 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.ArrayList; +import java.util.Collection; +import java.util.Date; +import java.util.HashMap; +import java.util.HashSet; +import java.util.Iterator; +import java.util.List; +import java.util.Map; +import java.util.Set; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.maps.client.InfoWindowContent; +import com.google.gwt.maps.client.MapType; +import com.google.gwt.maps.client.MapWidget; +import com.google.gwt.maps.client.Maps; +import com.google.gwt.maps.client.control.LargeMapControl3D; +import com.google.gwt.maps.client.control.MenuMapTypeControl; +import com.google.gwt.maps.client.control.ScaleControl; +import com.google.gwt.maps.client.event.MapDragEndHandler; +import com.google.gwt.maps.client.event.MapMouseMoveHandler; +import com.google.gwt.maps.client.event.MapZoomEndHandler; +import com.google.gwt.maps.client.event.MarkerClickHandler; +import com.google.gwt.maps.client.event.MarkerMouseOutHandler; +import com.google.gwt.maps.client.event.MarkerMouseOverHandler; +import com.google.gwt.maps.client.event.PolylineClickHandler; +import com.google.gwt.maps.client.event.PolylineMouseOutHandler; +import com.google.gwt.maps.client.event.PolylineMouseOverHandler; +import com.google.gwt.maps.client.geom.LatLng; +import com.google.gwt.maps.client.geom.LatLngBounds; +import com.google.gwt.maps.client.geom.Point; +import com.google.gwt.maps.client.geom.Projection; +import com.google.gwt.maps.client.overlay.Icon; +import com.google.gwt.maps.client.overlay.Marker; +import com.google.gwt.maps.client.overlay.MarkerOptions; +import com.google.gwt.maps.client.overlay.Polyline; +import com.google.gwt.maps.client.overlay.PolylineOptions; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Grid; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.LegType; +import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.domain.common.Util.Triple; +import com.sap.sailing.gwt.ui.client.ErrorReporter; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.Timer; +import com.sap.sailing.gwt.ui.shared.CompetitorDAO; +import com.sap.sailing.gwt.ui.shared.EventDAO; +import com.sap.sailing.gwt.ui.shared.GPSFixDAO; +import com.sap.sailing.gwt.ui.shared.ManeuverDAO; +import com.sap.sailing.gwt.ui.shared.MarkDAO; +import com.sap.sailing.gwt.ui.shared.RaceDAO; +import com.sap.sailing.gwt.ui.shared.RegattaDAO; +import com.sap.sailing.server.api.EventNameAndRaceName; + +public class RaceMap { + protected MapWidget map; + + private final SailingServiceAsync sailingService; + private final ErrorReporter errorReporter; + + /** + * Two sails on downwind leg, wind from port (sails on starboard); no highlighting + */ + protected ImageRotator boatIconDownwindPortRotator; + + /** + * Two sails on downwind leg, wind from port (sails on starboard); with highlighting + */ + protected ImageRotator boatIconHighlightedDownwindPortRotator; + + /** + * Two sails on downwind leg, wind from starboard (sails on port); no highlighting + */ + protected ImageRotator boatIconDownwindStarboardRotator; + + /** + * Two sails on downwind leg, wind from starboard (sails on port); with highlighting + */ + protected ImageRotator boatIconHighlightedDownwindStarboardRotator; + + /** + * One sail, wind from port (sails on starboard); no highlighting + */ + protected ImageRotator boatIconPortRotator; + + /** + * One sail, wind from port (sails on starboard); with highlighting + */ + protected ImageRotator boatIconHighlightedPortRotator; + + /** + * One sail, wind from starboard (sails on port); no highlighting + */ + protected ImageRotator boatIconStarboardRotator; + + /** + * One sail, wind from starboard (sails on port); with highlighting + */ + protected ImageRotator boatIconHighlightedStarboardRotator; + + protected Icon buoyIcon; + protected Icon tackToStarboardIcon; + protected Icon tackToPortIcon; + protected Icon jibeToStarboardIcon; + protected Icon jibeToPortIcon; + protected Icon markPassingToStarboardIcon; + protected Icon markPassingToPortIcon; + protected Icon headUpOnStarboardIcon; + protected Icon headUpOnPortIcon; + protected Icon bearAwayOnStarboardIcon; + protected Icon bearAwayOnPortIcon; + protected Icon unknownManeuverIcon; + protected Icon penaltyCircleToStarboardIcon; + protected Icon penaltyCircleToPortIcon; + protected LatLng lastMousePosition; + + protected long tailLengthInMilliSeconds = 30000l; + + protected List> selectedEventAndRace; + + /** + * If the user explicitly zoomed or panned the map, don't adjust zoom/pan unless a new race is selected + */ + protected boolean mapZoomedOrPannedSinceLastRaceSelectionChange = false; + + /** + * Used to check if the first initial zoom to the buoy markers was already done. + */ + protected boolean mapFirstZoomDone = false; + + + // key for domain web4sap.com + protected final String mapsAPIKey = "ABQIAAAAmvjPh3ZpHbnwuX3a66lDqRRLCigyC_gRDASMpyomD2do5awpNhRCyD_q-27hwxKe_T6ivSZ_0NgbUg"; + + protected static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class); + + protected final RaceMapData data; + + private final Timer timer; + + protected boolean showDouglasPeuckerPoints = false; + + protected boolean showManeuverHeadUp = false; + + protected boolean showManeuverBearAway = false; + + protected boolean showManeuverTack = false; + + protected boolean showManeuverJibe = false; + + protected boolean showManeuverPenaltyCircle = false; + + protected boolean showManeuverMarkPassing = false; + + protected boolean showManeuverOther = false; + + public RaceMap(SailingServiceAsync sailingService, ErrorReporter errorReporter, Timer timer) { + this.sailingService = sailingService; + this.errorReporter = errorReporter; + this.timer = timer; + + data = new RaceMapData(); + } + + public void loadMapsAPI(final Grid grid, final int gridRow, final int gridColumn) { + Maps.loadMapsApi(mapsAPIKey, "2", false, new Runnable() { + public void run() { + map = new MapWidget(); + map.addControl(new LargeMapControl3D()); + map.addControl(new MenuMapTypeControl()); + map.addControl(new ScaleControl()); + // Add the map to the HTML host page + map.setSize("100%", "100%"); + map.setScrollWheelZoomEnabled(true); + map.setContinuousZoom(true); + + MapType currentMapType = map.getCurrentMapType(); + Projection projection = currentMapType.getProjection(); + + grid.setWidget(gridRow, gridColumn, map); + + map.addMapZoomEndHandler(new MapZoomEndHandler() { + @Override + public void onZoomEnd(MapZoomEndEvent event) { + mapZoomedOrPannedSinceLastRaceSelectionChange = true; + } + }); + map.addMapDragEndHandler(new MapDragEndHandler() { + @Override + public void onDragEnd(MapDragEndEvent event) { + mapZoomedOrPannedSinceLastRaceSelectionChange = true; + } + }); + map.addMapMouseMoveHandler(new MapMouseMoveHandler() { + @Override + public void onMouseMove(MapMouseMoveEvent event) { + lastMousePosition = event.getLatLng(); + } + }); + boatIconDownwindPortRotator = new ImageRotator(resources.lowlightedBoatIconDW_Port()); + boatIconHighlightedDownwindPortRotator = new ImageRotator(resources.highlightedBoatIconDW_Port()); + boatIconDownwindStarboardRotator = new ImageRotator(resources.lowlightedBoatIconDW_Starboard()); + boatIconHighlightedDownwindStarboardRotator = new ImageRotator(resources + .highlightedBoatIconDW_Starboard()); + boatIconPortRotator = new ImageRotator(resources.lowlightedBoatIcon_Port()); + boatIconHighlightedPortRotator = new ImageRotator(resources.highlightedBoatIcon_Port()); + boatIconStarboardRotator = new ImageRotator(resources.lowlightedBoatIcon_Starboard()); + boatIconHighlightedStarboardRotator = new ImageRotator(resources.highlightedBoatIcon_Starboard()); + buoyIcon = Icon.newInstance(resources.buoyIcon().getSafeUri().asString()); + buoyIcon.setIconAnchor(Point.newInstance(4, 4)); + tackToStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=T|00FF00|000000"); + tackToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + tackToPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=T|FF0000|000000"); + tackToPortIcon.setIconAnchor(Point.newInstance(10, 33)); + jibeToStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=J|00FF00|000000"); + jibeToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + jibeToPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=J|FF0000|000000"); + jibeToPortIcon.setIconAnchor(Point.newInstance(10, 33)); + headUpOnStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=H|00FF00|000000"); + headUpOnStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + headUpOnPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=H|FF0000|000000"); + headUpOnPortIcon.setIconAnchor(Point.newInstance(10, 33)); + bearAwayOnStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=B|00FF00|000000"); + bearAwayOnStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + bearAwayOnPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=B|FF0000|000000"); + bearAwayOnPortIcon.setIconAnchor(Point.newInstance(10, 33)); + markPassingToStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=M|00FF00|000000"); + markPassingToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + markPassingToPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=M|FF0000|000000"); + markPassingToPortIcon.setIconAnchor(Point.newInstance(10, 33)); + unknownManeuverIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=?|FFFFFF|000000"); + unknownManeuverIcon.setIconAnchor(Point.newInstance(10, 33)); + penaltyCircleToStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=P|00FF00|000000"); + penaltyCircleToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + penaltyCircleToPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=P|FF0000|000000"); + penaltyCircleToPortIcon.setIconAnchor(Point.newInstance(10, 33)); + } + }); + } + + /** + * From {@link #data.fixes} as well as the selection of {@link #getCompetitorsToShow competitors to show}, computes the + * from/to times for which to request GPS raceMapData.fixes from the server. No update is performed here to {@link #data.fixes}. The + * result guarantees that, when used in + * {@link SailingServiceAsync#getBoatPositions(String, String, Map, Map, boolean, AsyncCallback)}, for each + * competitor from {@link #competitorsToShow} there are all raceMapData.fixes known by the server for that competitor starting + * at upTo-{@link #tailLengthInMilliSeconds} and ending at upTo (exclusive). + * + * @return a triple whose {@link Triple#getA() first} component contains the "from", and whose {@link Triple#getB() + * second} component contains the "to" times for the competitors whose trails / positions to show; the + * {@link Triple#getC() third} component tells whether the existing raceMapData.fixes can remain and be augmented by + * those requested or need to be replaced + */ + protected Triple, Map, Map> computeFromAndTo( + Date upTo, Collection competitorsToShow) { + Date tailstart = new Date(upTo.getTime() - tailLengthInMilliSeconds); + Map from = new HashMap(); + Map to = new HashMap(); + Map overlapWithKnownFixes = new HashMap(); + + for (CompetitorDAO competitor : competitorsToShow) { + List fixesForCompetitor = data.fixes.get(competitor); + Date fromDate; + Date toDate; + Date timepointOfLastKnownFix = fixesForCompetitor == null ? null + : data.getTimepointOfLastNonExtrapolated(fixesForCompetitor); + Date timepointOfFirstKnownFix = fixesForCompetitor == null ? null + : data.getTimepointOfFirstNonExtrapolated(fixesForCompetitor); + boolean overlap = false; + if (fixesForCompetitor != null && timepointOfFirstKnownFix != null + && !tailstart.before(timepointOfFirstKnownFix) && timepointOfLastKnownFix != null + && !tailstart.after(timepointOfLastKnownFix)) { + // the beginning of what we need is contained in the interval we already have; skip what we already have + fromDate = timepointOfLastKnownFix; + overlap = true; + } else { + fromDate = tailstart; + } + if (fixesForCompetitor != null && timepointOfFirstKnownFix != null + && !upTo.before(timepointOfFirstKnownFix) && timepointOfLastKnownFix != null + && !upTo.after(timepointOfLastKnownFix)) { + // the end of what we need is contained in the interval we already have; skip what we already have + toDate = timepointOfFirstKnownFix; + overlap = true; + } else { + toDate = upTo; + } + // only request something for the competitor if we're missing information at all + if (fromDate.before(toDate) || fromDate.equals(toDate)) { + from.put(competitor, fromDate); + to.put(competitor, toDate); + overlapWithKnownFixes.put(competitor, overlap); + } + } + return new Triple, Map, Map>(from, to, + overlapWithKnownFixes); + } + + + /** + * Adds the raceMapData.fixes received in result to {@link #data.fixes} and ensures they are still contiguous for each + * competitor. If overlapsWithKnownraceMapData.fixes indicates that the raceMapData.fixes received in result + * overlap with those already known, the raceMapData.fixes are merged into the list of already known raceMapData.fixes for the competitor. + * Otherwise, the raceMapData.fixes received in result replace those known so far for the respective competitor. + */ + protected void updateFixes(Map> result, + Map overlapsWithKnownFixes) { + for (Map.Entry> e : result.entrySet()) { + if (e.getValue() != null && !e.getValue().isEmpty()) { + List fixesForCompetitor = data.fixes.get(e.getKey()); + if (fixesForCompetitor == null) { + fixesForCompetitor = new ArrayList(); + data.fixes.put(e.getKey(), fixesForCompetitor); + } + if (!overlapsWithKnownFixes.get(e.getKey())) { + fixesForCompetitor.clear(); + // to re-establish the invariants for raceMapData.tails, raceMapData.firstShownFix and raceMapData.lastShownFix, we now need to remove + // all + // points from the competitor's polyline and clear the entries in raceMapData.firstShownFix and raceMapData.lastShownFix + if (map != null && data.tails.containsKey(e.getKey())) { + map.removeOverlay(data.tails.remove(e.getKey())); + } + data.firstShownFix.remove(e.getKey()); + data.lastShownFix.remove(e.getKey()); + fixesForCompetitor.addAll(e.getValue()); + } else { + data.mergeFixes(e.getKey(), e.getValue()); + } + } + } + } + + protected void showMarksOnMap(List result) { + if (map != null) { + Set toRemove = new HashSet(data.buoyMarkers.keySet()); + for (MarkDAO markDAO : result) { + Marker buoyMarker = data.buoyMarkers.get(markDAO); + if (buoyMarker == null) { + buoyMarker = createBuoyMarker(markDAO); + data.buoyMarkers.put(markDAO, buoyMarker); + map.addOverlay(buoyMarker); + } else { + buoyMarker.setLatLng(LatLng.newInstance(markDAO.position.latDeg, markDAO.position.lngDeg)); + toRemove.remove(markDAO); + } + } + for (MarkDAO toRemoveMarkDAO : toRemove) { + Marker marker = data.buoyMarkers.remove(toRemoveMarkDAO); + map.removeOverlay(marker); + } + zoomMapFirstTimeToMarks(data.buoyMarkers.keySet()); + } + } + +/** + * Zooms the map to the given marks if the map was not zoomed yet. If the map zoom to the marks was successful one + * time, the initial zoom to these marks can not be done again except the race selection is changed via {@link RaceMapPanel#onRaceSelectionChange(List). + * + * @param marksToZoomAt + * the marks to zoom at + */ + protected void zoomMapFirstTimeToMarks(Set marksToZoomAt) { + if (!mapZoomedOrPannedSinceLastRaceSelectionChange && !mapFirstZoomDone) { + LatLngBounds newBounds = null; + if (marksToZoomAt != null && !marksToZoomAt.isEmpty()) { + for (MarkDAO markDAO : marksToZoomAt) { + LatLng latLngZoomFirstTime = LatLng.newInstance(markDAO.position.latDeg, markDAO.position.lngDeg); + LatLngBounds bounds = LatLngBounds.newInstance(latLngZoomFirstTime, latLngZoomFirstTime); + if (newBounds == null) { + newBounds = bounds; + } else { + newBounds.extend(bounds.getNorthEast()); + newBounds.extend(bounds.getSouthWest()); + } + } + + } + if (newBounds != null) { + map.setZoomLevel(map.getBoundsZoomLevel(newBounds)); + map.setCenter(newBounds.getCenter()); + map.checkResizeAndCenter(); + mapFirstZoomDone = true; + /* + * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that the map was just zoomed + * to the bounds of the buoys, it was not a zoom or pan triggered by the user. As a consequence the + * mapZoomedOrPannedSinceLastRaceSelection option has to reset again. + */ + mapZoomedOrPannedSinceLastRaceSelectionChange = false; + } + } + } + + /** + * @param from + * time point for first fix to show in raceMapData.tails + * @param to + * time point for last fix to show in raceMapData.tails + */ + protected void showBoatsOnMap(Date from, Date to, Collection competitorsToShow, + Set competitorsSelectedInMap) { + if (map != null) { + LatLngBounds newMapBounds = null; + Set competitorDAOsOfUnusedTails = new HashSet(data.tails.keySet()); + Set competitorDAOsOfUnusedMarkers = new HashSet(data.boatMarkers.keySet()); + + for (CompetitorDAO competitorDAO : competitorsToShow) { + if (data.fixes.containsKey(competitorDAO)) { + Polyline tail = data.tails.get(competitorDAO); + if (tail == null) { + tail = createTailAndUpdateIndices(competitorDAO, from, to); + map.addOverlay(tail); + } else { + data.updateTail(tail, competitorDAO, from, to); + competitorDAOsOfUnusedTails.remove(competitorDAO); + } + LatLngBounds bounds = tail.getBounds(); + if (newMapBounds == null) { + newMapBounds = bounds; + } else { + newMapBounds.extend(bounds.getNorthEast()); + newMapBounds.extend(bounds.getSouthWest()); + } + if (data.lastShownFix.containsKey(competitorDAO) && data.lastShownFix.get(competitorDAO) != -1) { + GPSFixDAO lastPos = data.getBoatFix(competitorDAO); + Marker boatMarker = data.boatMarkers.get(competitorDAO); + if (boatMarker == null) { + boatMarker = createBoatMarker(competitorDAO, false); + map.addOverlay(boatMarker); + data.boatMarkers.put(competitorDAO, boatMarker); + } else { + competitorDAOsOfUnusedMarkers.remove(competitorDAO); + // check if anchors match; re-use marker with setImage only if anchors match + Point newAnchor = getBoatImageRotator(lastPos, + competitorsSelectedInMap.contains(competitorDAO)).getAnchor(); + Point oldAnchor = boatMarker.getIcon().getIconAnchor(); + if (oldAnchor.getX() == newAnchor.getX() && oldAnchor.getY() == newAnchor.getY()) { + boatMarker.setLatLng(LatLng.newInstance(lastPos.position.latDeg, + lastPos.position.lngDeg)); + boatMarker.setImage(getBoatImageURL(lastPos, + competitorsSelectedInMap.contains(competitorDAO))); + } else { + // anchors don't match; replace marker + map.removeOverlay(boatMarker); + boatMarker = createBoatMarker(competitorDAO, + competitorsSelectedInMap.contains(competitorDAO)); + map.addOverlay(boatMarker); + data.boatMarkers.put(competitorDAO, boatMarker); + } + } + } + } + } + if (!mapZoomedOrPannedSinceLastRaceSelectionChange && newMapBounds != null) { + map.setZoomLevel(map.getBoundsZoomLevel(newMapBounds)); + map.setCenter(newMapBounds.getCenter()); + mapFirstZoomDone = true; + } + for (CompetitorDAO unusedMarkerCompetitorDAO : competitorDAOsOfUnusedMarkers) { + map.removeOverlay(data.boatMarkers.remove(unusedMarkerCompetitorDAO)); + } + for (CompetitorDAO unusedTailCompetitorDAO : competitorDAOsOfUnusedTails) { + map.removeOverlay(data.tails.remove(unusedTailCompetitorDAO)); + } + } + } + + + private String getBoatImageURL(GPSFixDAO boatFix, boolean highlighted) { + return getBoatImageURL(getBoatImageRotator(boatFix, highlighted), boatFix); + } + + private String getBoatImageURL(ImageRotator boatImageRotator, GPSFixDAO boatFix) { + // the possible zoom level range is 0 to 21 (zoom level 0 would show the whole world) + int zoomLevel = map.getZoomLevel(); + double minScaleFactor = 0.1; + double maxScaleFactor = 1.0; + + double scaleDiffPerZoomLevel = (maxScaleFactor - minScaleFactor) / 21.0; + + double scaleFactor = minScaleFactor + scaleDiffPerZoomLevel * zoomLevel; + + return boatImageRotator.getRotatedImageURL(boatFix.speedWithBearing.bearingInDegrees, scaleFactor); + } + + private Icon getBoatImageIcon(GPSFixDAO boatFix, boolean highlighted) { + ImageRotator boatImageRotator = getBoatImageRotator(boatFix, highlighted); + Icon icon = Icon.newInstance(getBoatImageURL(boatImageRotator, boatFix)); + icon.setIconAnchor(boatImageRotator.getAnchor()); + return icon; + } + + private ImageRotator getBoatImageRotator(GPSFixDAO boatFix, boolean highlighted) { + if (boatFix.tack == Tack.PORT) { + if (LegType.DOWNWIND == boatFix.legType) { + if (highlighted) { + return boatIconHighlightedDownwindStarboardRotator; + } else { + return boatIconDownwindStarboardRotator; + } + } else { + if (highlighted) { + return boatIconHighlightedStarboardRotator; + } else { + return boatIconStarboardRotator; + } + } + } else { + if (LegType.DOWNWIND == boatFix.legType) { + if (highlighted) { + return boatIconHighlightedDownwindPortRotator; + } else { + return boatIconDownwindPortRotator; + } + } else { + if (highlighted) { + return boatIconHighlightedPortRotator; + } else { + return boatIconPortRotator; + } + } + } + } + + protected Marker createBuoyMarker(final MarkDAO markDAO) { + MarkerOptions options = MarkerOptions.newInstance(); + if (buoyIcon != null) { + options.setIcon(buoyIcon); + } + options.setTitle(markDAO.name); + final Marker buoyMarker = new Marker(LatLng.newInstance(markDAO.position.latDeg, markDAO.position.lngDeg), + options); + buoyMarker.addMarkerClickHandler(new MarkerClickHandler() { + @Override + public void onClick(MarkerClickEvent event) { + LatLng latlng = buoyMarker.getLatLng(); + showMarkInfoWindow(markDAO, latlng); + } + }); + return buoyMarker; + } + + protected Marker createBoatMarker(final CompetitorDAO competitorDAO, boolean highlighted) { + GPSFixDAO boatFix = data.getBoatFix(competitorDAO); + double latDeg = boatFix.position.latDeg; + double lngDeg = boatFix.position.lngDeg; + MarkerOptions options = MarkerOptions.newInstance(); + Icon icon = getBoatImageIcon(boatFix, highlighted); + options.setIcon(icon); + options.setTitle(competitorDAO.name); + final Marker boatMarker = new Marker(LatLng.newInstance(latDeg, lngDeg), options); + boatMarker.addMarkerClickHandler(new MarkerClickHandler() { + @Override + public void onClick(MarkerClickEvent event) { + LatLng latlng = boatMarker.getLatLng(); + showCompetitorInfoWindow(competitorDAO, latlng); + } + }); + boatMarker.addMarkerMouseOverHandler(new MarkerMouseOverHandler() { + @Override + public void onMouseOver(MarkerMouseOverEvent event) { + map.setTitle(competitorDAO.name); + // setSelectedInMap(competitorDAO, true); + // quickRanksBox.setItemSelected(quickRanksList.indexOf(competitorDAO), true); + } + }); + boatMarker.addMarkerMouseOutHandler(new MarkerMouseOutHandler() { + @Override + public void onMouseOut(MarkerMouseOutEvent event) { + map.setTitle(""); + } + }); + return boatMarker; + } + + private void showMarkInfoWindow(MarkDAO markDAO, LatLng latlng) { + map.getInfoWindow().open(latlng, new InfoWindowContent(getInfoWindowContent(markDAO))); + } + + private void showCompetitorInfoWindow(final CompetitorDAO competitorDAO, LatLng where) { + GPSFixDAO latestFixForCompetitor = data.getBoatFix(competitorDAO); + map.getInfoWindow().open(where, + new InfoWindowContent(getInfoWindowContent(competitorDAO, latestFixForCompetitor))); + } + + private Widget getInfoWindowContent(MarkDAO markDAO) { + VerticalPanel result = new VerticalPanel(); + result.add(new Label("Mark " + markDAO.name)); + result.add(new Label("" + markDAO.position)); + return result; + } + + private Widget getInfoWindowContent(CompetitorDAO competitorDAO, GPSFixDAO lastFix) { + final VerticalPanel result = new VerticalPanel(); + result.add(new Label("Competitor " + competitorDAO.name)); + result.add(new Label("" + lastFix.position)); + result.add(new Label(lastFix.speedWithBearing.speedInKnots + "kts " + lastFix.speedWithBearing.bearingInDegrees + + "deg")); + result.add(new Label("Tack: " + lastFix.tack.name())); + if (!selectedEventAndRace.isEmpty()) { + EventDAO event = selectedEventAndRace.get(selectedEventAndRace.size() - 1).getA(); + RaceDAO race = selectedEventAndRace.get(selectedEventAndRace.size() - 1).getC(); + if (event != null && race != null) { + Map from = new HashMap(); + from.put(competitorDAO, data.fixes.get(competitorDAO).get(data.firstShownFix.get(competitorDAO)).timepoint); + Map to = new HashMap(); + to.put(competitorDAO, data.getBoatFix(competitorDAO).timepoint); + /* currently not showing Douglas-Peucker points; TODO use checkboxes to select what to show (Bug #6) */ + sailingService.getDouglasPoints(new EventNameAndRaceName(event.name, race.name), from, to, 3, + new AsyncCallback>>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage()); + } + + @Override + public void onSuccess(Map> result) { + data.lastDouglasPeuckerResult = result; + if (data.douglasMarkers != null) { + removeAllMarkDouglasPeuckerpoints(); + } + if (!timer.isPlaying()) { + if (showDouglasPeuckerPoints) { + showMarkDouglasPeuckerPoints(result); + } + } + } + }); + sailingService.getManeuvers(new EventNameAndRaceName(event.name, race.name), from, to, + new AsyncCallback>>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage()); + } + + @Override + public void onSuccess(Map> result) { + data.lastManeuverResult = result; + if (data.maneuverMarkers != null) { + removeAllManeuverMarkers(); + } + if (!timer.isPlaying()) { + showManeuvers(result); + } + } + }); + + } + } + return result; + } + + private String getColorString(CompetitorDAO competitorDAO) { + // TODO green no more than 70, red no less than 120 + return "#" + (Integer.toHexString(competitorDAO.hashCode()) + "000000").substring(0, 4).toUpperCase() + "00"; + } + + /** + * Creates a polyline for the competitor represented by competitorDAO, taking the raceMapData.fixes from + * {@link #data.fixes raceMapData.fixes.get(competitorDAO)} and using the raceMapData.fixes starting at time point from (inclusive) + * up to the last fix with time point before to. The polyline is returned. Updates are applied to + * {@link #data.lastShownFix}, {@link #data.firstShownFix} and {@link #data.tails}. + */ + protected Polyline createTailAndUpdateIndices(final CompetitorDAO competitorDAO, Date from, Date to) { + List points = new ArrayList(); + List fixesForCompetitor = data.fixes.get(competitorDAO); + int indexOfFirst = -1; + int indexOfLast = -1; + int i = 0; + for (Iterator fixIter = fixesForCompetitor.iterator(); fixIter.hasNext() && indexOfLast == -1;) { + GPSFixDAO fix = fixIter.next(); + if (!fix.timepoint.before(to)) { + indexOfLast = i; + } else { + LatLng point = null; + if (indexOfFirst == -1) { + if (!fix.timepoint.before(from)) { + indexOfFirst = i; + point = LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg); + } + } else { + point = LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg); + } + if (point != null) { + points.add(point); + } + } + i++; + } + if (indexOfLast == -1) { + indexOfLast = i - 1; + } + if (indexOfFirst != -1 && indexOfLast != -1) { + data.firstShownFix.put(competitorDAO, indexOfFirst); + data.lastShownFix.put(competitorDAO, indexOfLast); + } + PolylineOptions options = PolylineOptions.newInstance( + /* clickable */true, /* geodesic */true); + Polyline result = new Polyline(points.toArray(new LatLng[0]), getColorString(competitorDAO), /* width */3, + /* opacity */0.5, options); + result.addPolylineClickHandler(new PolylineClickHandler() { + @Override + public void onClick(PolylineClickEvent event) { + showCompetitorInfoWindow(competitorDAO, lastMousePosition); + } + }); + result.addPolylineMouseOverHandler(new PolylineMouseOverHandler() { + @Override + public void onMouseOver(PolylineMouseOverEvent event) { + map.setTitle(competitorDAO.name); + } + }); + result.addPolylineMouseOutHandler(new PolylineMouseOutHandler() { + @Override + public void onMouseOut(PolylineMouseOutEvent event) { + map.setTitle(""); + } + }); + data.tails.put(competitorDAO, result); + return result; + } + + protected void removeAllMarkDouglasPeuckerpoints() { + if (data.douglasMarkers != null) { + for (Marker marker : data.douglasMarkers) { + map.removeOverlay(marker); + } + } + data.douglasMarkers = null; + } + + protected void removeAllManeuverMarkers() { + if (data.maneuverMarkers != null) { + for (Marker marker : data.maneuverMarkers) { + map.removeOverlay(marker); + } + data.maneuverMarkers = null; + } + } + + protected void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { + data.douglasMarkers = new HashSet(); + if (map != null && gpsFixPointMapForCompetitors != null) { + Set keySet = gpsFixPointMapForCompetitors.keySet(); + Iterator iter = keySet.iterator(); + while (iter.hasNext()) { + CompetitorDAO competitorDAO = iter.next(); + List gpsFix = gpsFixPointMapForCompetitors.get(competitorDAO); + for (GPSFixDAO fix : gpsFix) { + LatLng latLng = LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg); + MarkerOptions options = MarkerOptions.newInstance(); + options.setTitle(fix.timepoint+": "+fix.position+", "+fix.speedWithBearing.toString()); + Marker marker = new Marker(latLng, options); + data.douglasMarkers.add(marker); + map.addOverlay(marker); + } + } + } + } + + protected void showManeuvers(Map> maneuvers) { + data.maneuverMarkers = new HashSet(); + if (map != null && maneuvers != null) { + Set keySet = maneuvers.keySet(); + Iterator iter = keySet.iterator(); + while (iter.hasNext()) { + CompetitorDAO competitorDAO = iter.next(); + List maneuversForCompetitor = maneuvers.get(competitorDAO); + for (ManeuverDAO maneuver : maneuversForCompetitor) { + boolean showThisManeuver = true; + LatLng latLng = LatLng.newInstance(maneuver.position.latDeg, maneuver.position.lngDeg); + MarkerOptions options = MarkerOptions.newInstance(); + options.setTitle("" + maneuver.timepoint + ": " + maneuver.type + " " + + maneuver.directionChangeInDegrees + "deg from " + maneuver.speedWithBearingBefore + + " to " + maneuver.speedWithBearingAfter); + if (maneuver.type.equals("TACK") && showManeuverTack) { + if (maneuver.newTack.equals("PORT")) { + options.setIcon(tackToPortIcon); + } else { + options.setIcon(tackToStarboardIcon); + } + } else if (maneuver.type.equals("JIBE") && showManeuverJibe) { + if (maneuver.newTack.equals("PORT")) { + options.setIcon(jibeToPortIcon); + } else { + options.setIcon(jibeToStarboardIcon); + } + } else if (maneuver.type.equals("HEAD_UP") && showManeuverHeadUp) { + if (maneuver.newTack.equals("PORT")) { + options.setIcon(headUpOnPortIcon); + } else { + options.setIcon(headUpOnStarboardIcon); + } + } else if (maneuver.type.equals("BEAR_AWAY") && showManeuverBearAway) { + if (maneuver.newTack.equals("PORT")) { + options.setIcon(bearAwayOnPortIcon); + } else { + options.setIcon(bearAwayOnStarboardIcon); + } + } else if (maneuver.type.equals("PENALTY_CIRCLE") && showManeuverPenaltyCircle) { + if (maneuver.newTack.equals("PORT")) { + options.setIcon(penaltyCircleToPortIcon); + } else { + options.setIcon(penaltyCircleToStarboardIcon); + } + } else if (maneuver.type.equals("MARK_PASSING") && showManeuverMarkPassing) { + if (maneuver.newTack.equals("PORT")) { + options.setIcon(markPassingToPortIcon); + } else { + options.setIcon(markPassingToStarboardIcon); + } + } else { + if (maneuver.type.equals("UNKNOWN") && showManeuverOther) { + options.setIcon(unknownManeuverIcon); + } else { + showThisManeuver = false; + } + } + if (showThisManeuver) { + Marker marker = new Marker(latLng, options); + data.maneuverMarkers.add(marker); + map.addOverlay(marker); + } + } + } + } + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapData.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapData.java new file mode 100644 index 00000000000..572b7f12476 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapData.java @@ -0,0 +1,197 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.Date; +import java.util.HashMap; +import java.util.List; +import java.util.ListIterator; +import java.util.Map; +import java.util.Set; + +import com.google.gwt.maps.client.geom.LatLng; +import com.google.gwt.maps.client.overlay.Marker; +import com.google.gwt.maps.client.overlay.Polyline; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.shared.CompetitorDAO; +import com.sap.sailing.gwt.ui.shared.GPSFixDAO; +import com.sap.sailing.gwt.ui.shared.ManeuverDAO; +import com.sap.sailing.gwt.ui.shared.MarkDAO; + +public class RaceMapData { + + /** + * Tails of competitors currently displayed as overlays on the map. + */ + protected final Map tails; + + /** + * Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the first fix shown + * in {@link #tails} is . + */ + protected final Map firstShownFix; + + /** + * Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the last fix shown in + * {@link #tails} is . + */ + protected final Map lastShownFix; + + /** + * Fixes of each competitors tail. If a list is contained for a competitor, the list contains a timely "contiguous" + * list of fixes for the competitor. This means the server has no more data for the time interval covered, unless + * the last fix was {@link GPSFixDAO#extrapolated obtained by extrapolation}. + */ + protected final Map> fixes; + + /** + * Markers used as boat display on the map + */ + protected final Map boatMarkers; + + protected final Map buoyMarkers; + + /** + * markers displayed in response to + * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} + */ + protected Set douglasMarkers; + + /** + * markers displayed in response to + * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} + */ + protected Set maneuverMarkers; + + protected Map> lastManeuverResult; + + protected Map> lastDouglasPeuckerResult; + + public RaceMapData() { + tails = new HashMap(); + firstShownFix = new HashMap(); + lastShownFix = new HashMap(); + buoyMarkers = new HashMap(); + boatMarkers = new HashMap(); + fixes = new HashMap>(); + } + + protected Date getTimepointOfFirstNonExtrapolated(List fixesForCompetitor) { + for (GPSFixDAO fix : fixesForCompetitor) { + if (!fix.extrapolated) { + return fix.timepoint; + } + } + return null; + } + + protected Date getTimepointOfLastNonExtrapolated(List fixesForCompetitor) { + if (!fixesForCompetitor.isEmpty()) { + for (ListIterator fixIter = fixesForCompetitor.listIterator(fixesForCompetitor.size() - 1); fixIter + .hasPrevious();) { + GPSFixDAO fix = fixIter.previous(); + if (!fix.extrapolated) { + return fix.timepoint; + } + } + } + return null; + } + + /** + * While updating the {@link #fixes} for competitorDAO, the invariants for {@link #tails} and + * {@link #firstShownFix} and {@link #lastShownFix} are maintained: each time a fix is inserted, the + * {@link #firstShownFix}/{@link #lastShownFix} records for competitorDAO are incremented if they are + * greater or equal to the insertion index and we have a tail in {@link #tails} for competitorDAO. + * Additionally, if the fix is in between the fixes shown in the competitor's tail, the tail is adjusted by + * inserting the corresponding fix. + */ + protected void mergeFixes(CompetitorDAO competitorDAO, List mergeThis) { + List intoThis = fixes.get(competitorDAO); + int indexOfFirstShownFix = firstShownFix.get(competitorDAO) == null ? -1 : firstShownFix.get(competitorDAO); + int indexOfLastShownFix = lastShownFix.get(competitorDAO) == null ? -1 : lastShownFix.get(competitorDAO); + Polyline tail = tails.get(competitorDAO); + int intoThisIndex = 0; + for (GPSFixDAO mergeThisFix : mergeThis) { + while (intoThisIndex < intoThis.size() + && intoThis.get(intoThisIndex).timepoint.before(mergeThisFix.timepoint)) { + intoThisIndex++; + } + if (intoThisIndex < intoThis.size() && intoThis.get(intoThisIndex).timepoint.equals(mergeThisFix.timepoint)) { + // exactly same time point; replace with fix from mergeThis + intoThis.set(intoThisIndex, mergeThisFix); + } else { + intoThis.add(intoThisIndex, mergeThisFix); + if (indexOfFirstShownFix >= intoThisIndex) { + indexOfFirstShownFix++; + } + if (indexOfLastShownFix >= intoThisIndex) { + indexOfLastShownFix++; + } + if (tail != null && intoThisIndex >= indexOfFirstShownFix && intoThisIndex <= indexOfLastShownFix) { + tail.insertVertex(intoThisIndex - indexOfFirstShownFix, + LatLng.newInstance(mergeThisFix.position.latDeg, mergeThisFix.position.lngDeg)); + } + } + intoThisIndex++; + } + } + + protected GPSFixDAO getBoatFix(CompetitorDAO competitorDAO) { + return fixes.get(competitorDAO).get(lastShownFix.get(competitorDAO)); + } + + /** + * If the tail starts before from, removes leading vertices from tail that are before + * from. This is determined by using the {@link #firstShownFix} index which tells us where in + * {@link #fixes} we find the sequence of fixes currently represented in the tail. + *

+ * + * If the tail starts after from, vertices for those {@link #fixes} for competitorDAO at + * or after time point from and before the time point of the first fix displayed so far in the tail and + * before to are prepended to the tail. + *

+ * + * Now to the end of the tail: if the existing tail's end exceeds to, the vertices in excess are + * removed (aided by {@link #lastShownFix}). Otherwise, for the competitor's fixes starting at the tail's end up to + * to are appended to the tail. + *

+ * + * When this method returns, {@link #firstShownFix} and {@link #lastShownFix} have been updated accordingly. + */ + protected void updateTail(Polyline tail, CompetitorDAO competitorDAO, Date from, Date to) { + List fixesForCompetitor = fixes.get(competitorDAO); + int indexOfFirstShownFix = firstShownFix.get(competitorDAO) == null ? -1 : firstShownFix.get(competitorDAO); + while (indexOfFirstShownFix != -1 && tail.getVertexCount() > 0 + && fixesForCompetitor.get(indexOfFirstShownFix).timepoint.before(from)) { + tail.deleteVertex(0); + indexOfFirstShownFix++; + } + // now the polyline contains no more vertices representing fixes before "from"; + // go back in time starting at indexOfFirstShownFix while the fixes are still at or after "from" + // and insert corresponding vertices into the polyline + while (indexOfFirstShownFix > 0 && !fixesForCompetitor.get(indexOfFirstShownFix - 1).timepoint.before(from)) { + indexOfFirstShownFix--; + GPSFixDAO fix = fixesForCompetitor.get(indexOfFirstShownFix); + tail.insertVertex(0, LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg)); + } + // now adjust the polylines tail: remove excess vertices that are after "to" + int indexOfLastShownFix = lastShownFix.get(competitorDAO) == null ? -1 : lastShownFix.get(competitorDAO); + while (indexOfLastShownFix != -1 && tail.getVertexCount() > 0 + && fixesForCompetitor.get(indexOfLastShownFix).timepoint.after(to)) { + tail.deleteVertex(tail.getVertexCount() - 1); + indexOfLastShownFix--; + } + // now the polyline contains no more vertices representing fixes after "to"; + // go forward in time starting at indexOfLastShownFix while the fixes are still at or before "to" + // and insert corresponding vertices into the polyline + while (indexOfLastShownFix < fixesForCompetitor.size() - 1 + && !fixesForCompetitor.get(indexOfLastShownFix + 1).timepoint.after(to)) { + indexOfLastShownFix++; + GPSFixDAO fix = fixesForCompetitor.get(indexOfLastShownFix); + tail.insertVertex(tail.getVertexCount(), LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg)); + } + firstShownFix.put(competitorDAO, indexOfFirstShownFix); + lastShownFix.put(competitorDAO, indexOfLastShownFix); + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java index 38129369515..f695974a089 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java @@ -3,44 +3,19 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.ArrayList; import java.util.Collection; import java.util.Date; -import java.util.HashMap; import java.util.HashSet; -import java.util.Iterator; import java.util.List; -import java.util.ListIterator; import java.util.Map; import java.util.Set; -import com.google.gwt.core.client.GWT; import com.google.gwt.event.dom.client.ChangeEvent; import com.google.gwt.event.dom.client.ChangeHandler; import com.google.gwt.event.dom.client.ClickEvent; import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; -import com.google.gwt.maps.client.InfoWindowContent; -import com.google.gwt.maps.client.MapWidget; -import com.google.gwt.maps.client.Maps; -import com.google.gwt.maps.client.control.LargeMapControl3D; -import com.google.gwt.maps.client.control.MenuMapTypeControl; -import com.google.gwt.maps.client.control.ScaleControl; -import com.google.gwt.maps.client.event.MapDragEndHandler; -import com.google.gwt.maps.client.event.MapMouseMoveHandler; -import com.google.gwt.maps.client.event.MapZoomEndHandler; -import com.google.gwt.maps.client.event.MarkerClickHandler; -import com.google.gwt.maps.client.event.MarkerMouseOutHandler; -import com.google.gwt.maps.client.event.MarkerMouseOverHandler; -import com.google.gwt.maps.client.event.PolylineClickHandler; -import com.google.gwt.maps.client.event.PolylineMouseOutHandler; -import com.google.gwt.maps.client.event.PolylineMouseOverHandler; import com.google.gwt.maps.client.geom.LatLng; -import com.google.gwt.maps.client.geom.LatLngBounds; -import com.google.gwt.maps.client.geom.Point; -import com.google.gwt.maps.client.overlay.Icon; import com.google.gwt.maps.client.overlay.Marker; -import com.google.gwt.maps.client.overlay.MarkerOptions; -import com.google.gwt.maps.client.overlay.Polyline; -import com.google.gwt.maps.client.overlay.PolylineOptions; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FormPanel; @@ -53,8 +28,6 @@ import com.google.gwt.user.client.ui.ProvidesResize; import com.google.gwt.user.client.ui.RequiresResize; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.domain.common.LegType; -import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.Util.Pair; import com.sap.sailing.domain.common.Util.Triple; import com.sap.sailing.gwt.ui.client.ErrorReporter; @@ -80,138 +53,20 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe private final SailingServiceAsync sailingService; private final ErrorReporter errorReporter; private final Grid grid; - private MapWidget map; private final RacesListBoxPanel newRaceListBox; private final ListBox quickRanksBox; private final List quickRanksList; private final TimePanel timePanel; - /** - * Two sails on downwind leg, wind from port (sails on starboard); no highlighting - */ - private ImageRotator boatIconDownwindPortRotator; - - /** - * Two sails on downwind leg, wind from port (sails on starboard); with highlighting - */ - private ImageRotator boatIconHighlightedDownwindPortRotator; - - /** - * Two sails on downwind leg, wind from starboard (sails on port); no highlighting - */ - private ImageRotator boatIconDownwindStarboardRotator; - - /** - * Two sails on downwind leg, wind from starboard (sails on port); with highlighting - */ - private ImageRotator boatIconHighlightedDownwindStarboardRotator; - - /** - * One sail, wind from port (sails on starboard); no highlighting - */ - private ImageRotator boatIconPortRotator; - - /** - * One sail, wind from port (sails on starboard); with highlighting - */ - private ImageRotator boatIconHighlightedPortRotator; - - /** - * One sail, wind from starboard (sails on port); no highlighting - */ - private ImageRotator boatIconStarboardRotator; - - /** - * One sail, wind from starboard (sails on port); with highlighting - */ - private ImageRotator boatIconHighlightedStarboardRotator; - - private Icon buoyIcon; - private Icon tackToStarboardIcon; - private Icon tackToPortIcon; - private Icon jibeToStarboardIcon; - private Icon jibeToPortIcon; - private Icon markPassingToStarboardIcon; - private Icon markPassingToPortIcon; - private Icon headUpOnStarboardIcon; - private Icon headUpOnPortIcon; - private Icon bearAwayOnStarboardIcon; - private Icon bearAwayOnPortIcon; - private Icon unknownManeuverIcon; - private Icon penaltyCircleToStarboardIcon; - private Icon penaltyCircleToPortIcon; - private LatLng lastMousePosition; private final Set competitorsSelectedInMap; private final Timer timer; private List> checkboxAndType; private CheckBox checkBoxDouglasPeuckerPoints; - private long TAILLENGTHINMILLISECONDS = 30000l; - - /** - * If the user explicitly zoomed or panned the map, don't adjust zoom/pan unless a new race is selected - */ - private boolean mapZoomedOrPannedSinceLastRaceSelectionChange = false; - - /** - * Used to check if the first initial zoom to the buoy markers was already done. - */ - private boolean mapFirstZoomDone = false; - - /** - * Tails of competitors currently displayed as overlays on the map. - */ - private final Map tails; - - /** - * Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the first fix shown - * in {@link #tails} is . - */ - private final Map firstShownFix; - - /** - * Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the last fix shown in - * {@link #tails} is . - */ - private final Map lastShownFix; - - /** - * Fixes of each competitors tail. If a list is contained for a competitor, the list contains a timely "contiguous" - * list of fixes for the competitor. This means the server has no more data for the time interval covered, unless - * the last fix was {@link GPSFixDAO#extrapolated obtained by extrapolation}. - */ - private final Map> fixes; - - /** - * Markers used as boat display on the map - */ - private final Map boatMarkers; - - private final Map buoyMarkers; - - /** - * markers displayed in response to - * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} - */ - private Set douglasMarkers; - - /** - * markers displayed in response to - * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} - */ - private Set maneuverMarkers; - - // key for domain web4sap.com - private final String mapsAPIKey = "ABQIAAAAmvjPh3ZpHbnwuX3a66lDqRRLCigyC_gRDASMpyomD2do5awpNhRCyD_q-27hwxKe_T6ivSZ_0NgbUg"; - private final CheckBox showOnlySelected; private final IntegerBox tailLengthBox; - protected Map> lastManeuverResult; - - protected Map> lastDouglasPeuckerResult; - /** * RPC calls may receive responses out of order if there are multiple calls in-flight at the same time. If the time * slider is moved quickly it generates many requests for boat positions quickly after each other. Sometimes, @@ -228,7 +83,7 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe */ private int startedProcessingRequestID; - private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class); + private final RaceMap raceMap; public RaceMapPanel(SailingServiceAsync sailingService, ErrorReporter errorReporter, final EventRefresher eventRefresher, StringConstants stringConstants) { @@ -236,11 +91,6 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe this.errorReporter = errorReporter; this.timer = new Timer(/* delayBetweenAutoAdvancesInMilliseconds */500); competitorsSelectedInMap = new HashSet(); - tails = new HashMap(); - firstShownFix = new HashMap(); - lastShownFix = new HashMap(); - buoyMarkers = new HashMap(); - boatMarkers = new HashMap(); checkboxAndType = new ArrayList>(); final VerticalPanel verticalCheckBoxPanel = new VerticalPanel(); verticalCheckBoxPanel.add(new Label(stringConstants.maneuverTypes())); @@ -259,46 +109,54 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe checkBoxMarkPassing.setValue(true); checkboxAndType.add(new Pair(checkBoxMarkPassing, "MARK_PASSING")); checkboxAndType.add(new Pair(new CheckBox(stringConstants.otherManeuver()), "OTHER")); + for (Pair pair : checkboxAndType) { pair.getA().addValueChangeHandler(new ValueChangeHandler() { @Override public void onValueChange(ValueChangeEvent event) { - if (!timer.isPlaying() && lastManeuverResult != null) { - removeAllManeuverMarkers(); - showManeuvers(lastManeuverResult); + if (!timer.isPlaying() && raceMap.data.lastManeuverResult != null) { + raceMap.removeAllManeuverMarkers(); + showManeuvers(raceMap.data.lastManeuverResult); } } }); verticalCheckBoxPanel.add(pair.getA()); } + checkBoxDouglasPeuckerPoints = new CheckBox(stringConstants.douglasPeuckerPoints()); checkBoxDouglasPeuckerPoints.addValueChangeHandler(new ValueChangeHandler() { @Override public void onValueChange(ValueChangeEvent event) { - if (!timer.isPlaying() && lastDouglasPeuckerResult != null && event.getValue()) { - removeAllMarkDouglasPeuckerpoints(); - showMarkDouglasPeuckerPoints(lastDouglasPeuckerResult); + if (!timer.isPlaying() && raceMap.data.lastDouglasPeuckerResult != null && event.getValue()) { + raceMap.showDouglasPeuckerPoints = true; + raceMap.removeAllMarkDouglasPeuckerpoints(); + raceMap.showMarkDouglasPeuckerPoints(raceMap.data.lastDouglasPeuckerResult); } else if (!event.getValue()) { - removeAllMarkDouglasPeuckerpoints(); + raceMap.showDouglasPeuckerPoints = false; + raceMap.removeAllMarkDouglasPeuckerpoints(); } } }); verticalCheckBoxPanel.add(checkBoxDouglasPeuckerPoints); - fixes = new HashMap>(); this.grid = new Grid(3, 2); setWidget(grid); grid.setSize("100%", "100%"); grid.getColumnFormatter().setWidth(0, "20%"); grid.getColumnFormatter().setWidth(1, "80%"); grid.getCellFormatter().setHeight(2, 1, "100%"); - loadMapsAPI(); + + raceMap = new RaceMap(sailingService, errorReporter, timer); + raceMap.loadMapsAPI(grid, 2, 1); + + setMapDisplayOptions(); + newRaceListBox = new RacesListBoxPanel(eventRefresher, stringConstants); newRaceListBox.addRaceSelectionChangeListener(this); grid.setWidget(0, 0, newRaceListBox); PositionDAO pos = new PositionDAO(); - if (!boatMarkers.isEmpty()) { - LatLng latLng = boatMarkers.values().iterator().next().getLatLng(); + if (!raceMap.data.boatMarkers.isEmpty()) { + LatLng latLng = raceMap.data.boatMarkers.values().iterator().next().getLatLng(); pos.latDeg = latLng.getLatitude(); pos.lngDeg = latLng.getLongitude(); } @@ -313,11 +171,11 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe HorizontalPanel labelAndTailLengthBox = new HorizontalPanel(); labelAndTailLengthBox.add(new Label(stringConstants.tailLength())); tailLengthBox = new IntegerBox(); - tailLengthBox.setValue((int) (TAILLENGTHINMILLISECONDS / 1000)); + tailLengthBox.setValue((int) (raceMap.tailLengthInMilliSeconds / 1000)); tailLengthBox.addValueChangeHandler(new ValueChangeHandler() { @Override public void onValueChange(ValueChangeEvent event) { - TAILLENGTHINMILLISECONDS = 1000l * event.getValue(); + raceMap.tailLengthInMilliSeconds = 1000l * event.getValue(); refreshMapContents(); } }); @@ -381,21 +239,21 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe private void setSelectedInMap(CompetitorDAO competitorDAO, boolean itemSelected) { if (!itemSelected && competitorsSelectedInMap.contains(competitorDAO)) { // "lowlight" currently selected competitor - Marker highlightedMarker = boatMarkers.get(competitorDAO); + Marker highlightedMarker = raceMap.data.boatMarkers.get(competitorDAO); if (highlightedMarker != null) { - Marker lowlightedMarker = createBoatMarker(competitorDAO, false); - map.removeOverlay(highlightedMarker); - map.addOverlay(lowlightedMarker); - boatMarkers.put(competitorDAO, lowlightedMarker); + Marker lowlightedMarker = raceMap.createBoatMarker(competitorDAO, false); + raceMap.map.removeOverlay(highlightedMarker); + raceMap.map.addOverlay(lowlightedMarker); + raceMap.data.boatMarkers.put(competitorDAO, lowlightedMarker); competitorsSelectedInMap.remove(competitorDAO); } } else if (itemSelected && !competitorsSelectedInMap.contains(competitorDAO)) { - Marker lowlightedMarker = boatMarkers.get(competitorDAO); + Marker lowlightedMarker = raceMap.data.boatMarkers.get(competitorDAO); if (lowlightedMarker != null) { - Marker highlightedMarker = createBoatMarker(competitorDAO, true); - map.removeOverlay(lowlightedMarker); - map.addOverlay(highlightedMarker); - boatMarkers.put(competitorDAO, highlightedMarker); + Marker highlightedMarker = raceMap.createBoatMarker(competitorDAO, true); + raceMap.map.removeOverlay(lowlightedMarker); + raceMap.map.addOverlay(highlightedMarker); + raceMap.data.boatMarkers.put(competitorDAO, highlightedMarker); int selectionIndex = quickRanksList.indexOf(competitorDAO); quickRanksBox.setItemSelected(selectionIndex, true); } @@ -404,99 +262,16 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe } } - private void loadMapsAPI() { - Maps.loadMapsApi(mapsAPIKey, "2", false, new Runnable() { - public void run() { - map = new MapWidget(); - map.addControl(new LargeMapControl3D()); - map.addControl(new MenuMapTypeControl()); - map.addControl(new ScaleControl()); - // Add the map to the HTML host page - grid.setWidget(2, 1, map); - map.setSize("100%", "100%"); - map.setScrollWheelZoomEnabled(true); - map.setContinuousZoom(true); - map.addMapZoomEndHandler(new MapZoomEndHandler() { - @Override - public void onZoomEnd(MapZoomEndEvent event) { - mapZoomedOrPannedSinceLastRaceSelectionChange = true; - } - }); - map.addMapDragEndHandler(new MapDragEndHandler() { - @Override - public void onDragEnd(MapDragEndEvent event) { - mapZoomedOrPannedSinceLastRaceSelectionChange = true; - } - }); - map.addMapMouseMoveHandler(new MapMouseMoveHandler() { - @Override - public void onMouseMove(MapMouseMoveEvent event) { - lastMousePosition = event.getLatLng(); - } - }); - boatIconDownwindPortRotator = new ImageRotator(resources.lowlightedBoatIconDW_Port()); - boatIconHighlightedDownwindPortRotator = new ImageRotator(resources.highlightedBoatIconDW_Port()); - boatIconDownwindStarboardRotator = new ImageRotator(resources.lowlightedBoatIconDW_Starboard()); - boatIconHighlightedDownwindStarboardRotator = new ImageRotator(resources - .highlightedBoatIconDW_Starboard()); - boatIconPortRotator = new ImageRotator(resources.lowlightedBoatIcon_Port()); - boatIconHighlightedPortRotator = new ImageRotator(resources.highlightedBoatIcon_Port()); - boatIconStarboardRotator = new ImageRotator(resources.lowlightedBoatIcon_Starboard()); - boatIconHighlightedStarboardRotator = new ImageRotator(resources.highlightedBoatIcon_Starboard()); - buoyIcon = Icon.newInstance(resources.buoyIcon().getSafeUri().asString()); - buoyIcon.setIconAnchor(Point.newInstance(4, 4)); - tackToStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=T|00FF00|000000"); - tackToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - tackToPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=T|FF0000|000000"); - tackToPortIcon.setIconAnchor(Point.newInstance(10, 33)); - jibeToStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=J|00FF00|000000"); - jibeToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - jibeToPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=J|FF0000|000000"); - jibeToPortIcon.setIconAnchor(Point.newInstance(10, 33)); - headUpOnStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=H|00FF00|000000"); - headUpOnStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - headUpOnPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=H|FF0000|000000"); - headUpOnPortIcon.setIconAnchor(Point.newInstance(10, 33)); - bearAwayOnStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=B|00FF00|000000"); - bearAwayOnStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - bearAwayOnPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=B|FF0000|000000"); - bearAwayOnPortIcon.setIconAnchor(Point.newInstance(10, 33)); - markPassingToStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=M|00FF00|000000"); - markPassingToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - markPassingToPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=M|FF0000|000000"); - markPassingToPortIcon.setIconAnchor(Point.newInstance(10, 33)); - unknownManeuverIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=?|FFFFFF|000000"); - unknownManeuverIcon.setIconAnchor(Point.newInstance(10, 33)); - penaltyCircleToStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=P|00FF00|000000"); - penaltyCircleToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - penaltyCircleToPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=P|FF0000|000000"); - penaltyCircleToPortIcon.setIconAnchor(Point.newInstance(10, 33)); - } - }); - } - @Override public void fillEvents(List result) { newRaceListBox.fillEvents(result); + raceMap.selectedEventAndRace = newRaceListBox.getSelectedEventAndRace(); } @Override public void onRaceSelectionChange(List> selectedRaces) { - mapFirstZoomDone = false; - mapZoomedOrPannedSinceLastRaceSelectionChange = false; + raceMap.mapFirstZoomDone = false; + raceMap.mapZoomedOrPannedSinceLastRaceSelectionChange = false; if (!selectedRaces.isEmpty() && selectedRaces.get(selectedRaces.size() - 1) != null) { RaceDAO raceDAO = selectedRaces.get(selectedRaces.size() - 1).getC(); if (raceDAO.startOfRace != null) { @@ -530,7 +305,7 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe EventDAO event = selection.get(selection.size() - 1).getA(); RaceDAO race = selection.get(selection.size() - 1).getC(); if (event != null && race != null) { - final Triple, Map, Map> fromAndToAndOverlap = computeFromAndTo(date); + final Triple, Map, Map> fromAndToAndOverlap = raceMap.computeFromAndTo(date,this.getCompetitorsToShow()); final int requestID = boatPositionRequestIDCounter++; sailingService.getBoatPositions(new EventNameAndRaceName(event.name, race.name), fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), true, @@ -545,14 +320,14 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe // process response only if not received out of order if (startedProcessingRequestID < requestID) { startedProcessingRequestID = requestID; - Date from = new Date(date.getTime() - TAILLENGTHINMILLISECONDS); - updateFixes(result, fromAndToAndOverlap.getC()); - showBoatsOnMap(from, date); - if (douglasMarkers != null) { - removeAllMarkDouglasPeuckerpoints(); + Date from = new Date(date.getTime() - raceMap.tailLengthInMilliSeconds); + raceMap.updateFixes(result, fromAndToAndOverlap.getC()); + raceMap.showBoatsOnMap(from, date, getCompetitorsToShow(), competitorsSelectedInMap); + if (raceMap.data.douglasMarkers != null) { + raceMap.removeAllMarkDouglasPeuckerpoints(); } - if (maneuverMarkers != null) { - removeAllManeuverMarkers(); + if (raceMap.data.maneuverMarkers != null) { + raceMap.removeAllManeuverMarkers(); } } } @@ -567,7 +342,7 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe @Override public void onSuccess(List result) { - showMarksOnMap(result); + raceMap.showMarksOnMap(result); } }); sailingService.getQuickRanks(new EventNameAndRaceName(event.name, race.name), date, @@ -587,157 +362,6 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe } } - /** - * From {@link #fixes} as well as the selection of {@link #getCompetitorsToShow competitors to show}, computes the - * from/to times for which to request GPS fixes from the server. No update is performed here to {@link #fixes}. The - * result guarantees that, when used in - * {@link SailingServiceAsync#getBoatPositions(String, String, Map, Map, boolean, AsyncCallback)}, for each - * competitor from {@link #competitorsToShow} there are all fixes known by the server for that competitor starting - * at upTo-{@link #TAILLENGTHINMILLISECONDS} and ending at upTo (exclusive). - * - * @return a triple whose {@link Triple#getA() first} component contains the "from", and whose {@link Triple#getB() - * second} component contains the "to" times for the competitors whose trails / positions to show; the - * {@link Triple#getC() third} component tells whether the existing fixes can remain and be augmented by - * those requested or need to be replaced - */ - private Triple, Map, Map> computeFromAndTo( - Date upTo) { - Date tailStart = new Date(upTo.getTime() - TAILLENGTHINMILLISECONDS); - Map from = new HashMap(); - Map to = new HashMap(); - Map overlapWithKnownFixes = new HashMap(); - for (CompetitorDAO competitor : getCompetitorsToShow()) { - List fixesForCompetitor = fixes.get(competitor); - Date fromDate; - Date toDate; - Date timepointOfLastKnownFix = fixesForCompetitor == null ? null - : getTimepointOfLastNonExtrapolated(fixesForCompetitor); - Date timepointOfFirstKnownFix = fixesForCompetitor == null ? null - : getTimepointOfFirstNonExtrapolated(fixesForCompetitor); - boolean overlap = false; - if (fixesForCompetitor != null && timepointOfFirstKnownFix != null - && !tailStart.before(timepointOfFirstKnownFix) && timepointOfLastKnownFix != null - && !tailStart.after(timepointOfLastKnownFix)) { - // the beginning of what we need is contained in the interval we already have; skip what we already have - fromDate = timepointOfLastKnownFix; - overlap = true; - } else { - fromDate = tailStart; - } - if (fixesForCompetitor != null && timepointOfFirstKnownFix != null - && !upTo.before(timepointOfFirstKnownFix) && timepointOfLastKnownFix != null - && !upTo.after(timepointOfLastKnownFix)) { - // the end of what we need is contained in the interval we already have; skip what we already have - toDate = timepointOfFirstKnownFix; - overlap = true; - } else { - toDate = upTo; - } - // only request something for the competitor if we're missing information at all - if (fromDate.before(toDate) || fromDate.equals(toDate)) { - from.put(competitor, fromDate); - to.put(competitor, toDate); - overlapWithKnownFixes.put(competitor, overlap); - } - } - return new Triple, Map, Map>(from, to, - overlapWithKnownFixes); - } - - private Date getTimepointOfFirstNonExtrapolated(List fixesForCompetitor) { - for (GPSFixDAO fix : fixesForCompetitor) { - if (!fix.extrapolated) { - return fix.timepoint; - } - } - return null; - } - - private Date getTimepointOfLastNonExtrapolated(List fixesForCompetitor) { - if (!fixesForCompetitor.isEmpty()) { - for (ListIterator fixIter = fixesForCompetitor.listIterator(fixesForCompetitor.size() - 1); fixIter - .hasPrevious();) { - GPSFixDAO fix = fixIter.previous(); - if (!fix.extrapolated) { - return fix.timepoint; - } - } - } - return null; - } - - /** - * Adds the fixes received in result to {@link #fixes} and ensures they are still contiguous for each - * competitor. If overlapsWithKnownFixes indicates that the fixes received in result - * overlap with those already known, the fixes are merged into the list of already known fixes for the competitor. - * Otherwise, the fixes received in result replace those known so far for the respective competitor. - */ - private void updateFixes(Map> result, - Map overlapsWithKnownFixes) { - for (Map.Entry> e : result.entrySet()) { - if (e.getValue() != null && !e.getValue().isEmpty()) { - List fixesForCompetitor = fixes.get(e.getKey()); - if (fixesForCompetitor == null) { - fixesForCompetitor = new ArrayList(); - fixes.put(e.getKey(), fixesForCompetitor); - } - if (!overlapsWithKnownFixes.get(e.getKey())) { - fixesForCompetitor.clear(); - // to re-establish the invariants for tails, firstShownFix and lastShownFix, we now need to remove - // all - // points from the competitor's polyline and clear the entries in firstShownFix and lastShownFix - if (map != null && tails.containsKey(e.getKey())) { - map.removeOverlay(tails.remove(e.getKey())); - } - firstShownFix.remove(e.getKey()); - lastShownFix.remove(e.getKey()); - fixesForCompetitor.addAll(e.getValue()); - } else { - mergeFixes(e.getKey(), e.getValue()); - } - } - } - } - - /** - * While updating the {@link #fixes} for competitorDAO, the invariants for {@link #tails} and - * {@link #firstShownFix} and {@link #lastShownFix} are maintained: each time a fix is inserted, the - * {@link #firstShownFix}/{@link #lastShownFix} records for competitorDAO are incremented if they are - * greater or equal to the insertion index and we have a tail in {@link #tails} for competitorDAO. - * Additionally, if the fix is in between the fixes shown in the competitor's tail, the tail is adjusted by - * inserting the corresponding fix. - */ - private void mergeFixes(CompetitorDAO competitorDAO, List mergeThis) { - List intoThis = fixes.get(competitorDAO); - int indexOfFirstShownFix = firstShownFix.get(competitorDAO) == null ? -1 : firstShownFix.get(competitorDAO); - int indexOfLastShownFix = lastShownFix.get(competitorDAO) == null ? -1 : lastShownFix.get(competitorDAO); - Polyline tail = tails.get(competitorDAO); - int intoThisIndex = 0; - for (GPSFixDAO mergeThisFix : mergeThis) { - while (intoThisIndex < intoThis.size() - && intoThis.get(intoThisIndex).timepoint.before(mergeThisFix.timepoint)) { - intoThisIndex++; - } - if (intoThisIndex < intoThis.size() && intoThis.get(intoThisIndex).timepoint.equals(mergeThisFix.timepoint)) { - // exactly same time point; replace with fix from mergeThis - intoThis.set(intoThisIndex, mergeThisFix); - } else { - intoThis.add(intoThisIndex, mergeThisFix); - if (indexOfFirstShownFix >= intoThisIndex) { - indexOfFirstShownFix++; - } - if (indexOfLastShownFix >= intoThisIndex) { - indexOfLastShownFix++; - } - if (tail != null && intoThisIndex >= indexOfFirstShownFix && intoThisIndex <= indexOfLastShownFix) { - tail.insertVertex(intoThisIndex - indexOfFirstShownFix, - LatLng.newInstance(mergeThisFix.position.latDeg, mergeThisFix.position.lngDeg)); - } - } - intoThisIndex++; - } - } - private void showQuickRanks(List result) { quickRanksBox.clear(); quickRanksList.clear(); @@ -754,199 +378,6 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe } } - private void showMarksOnMap(List result) { - if (map != null) { - Set toRemove = new HashSet(buoyMarkers.keySet()); - for (MarkDAO markDAO : result) { - Marker buoyMarker = buoyMarkers.get(markDAO); - if (buoyMarker == null) { - buoyMarker = createBuoyMarker(markDAO); - buoyMarkers.put(markDAO, buoyMarker); - map.addOverlay(buoyMarker); - } else { - buoyMarker.setLatLng(LatLng.newInstance(markDAO.position.latDeg, markDAO.position.lngDeg)); - toRemove.remove(markDAO); - } - } - for (MarkDAO toRemoveMarkDAO : toRemove) { - Marker marker = buoyMarkers.remove(toRemoveMarkDAO); - map.removeOverlay(marker); - } - zoomMapFirstTimeToMarks(buoyMarkers.keySet()); - } - } - -/** - * Zooms the map to the given marks if the map was not zoomed yet. If the map zoom to the marks was successful one - * time, the initial zoom to these marks can not be done again except the race selection is changed via {@link RaceMapPanel#onRaceSelectionChange(List). - * - * @param marksToZoomAt - * the marks to zoom at - */ - private void zoomMapFirstTimeToMarks(Set marksToZoomAt) { - if (!mapZoomedOrPannedSinceLastRaceSelectionChange && !mapFirstZoomDone) { - LatLngBounds newBounds = null; - if (marksToZoomAt != null && !marksToZoomAt.isEmpty()) { - for (MarkDAO markDAO : marksToZoomAt) { - LatLng latLngZoomFirstTime = LatLng.newInstance(markDAO.position.latDeg, markDAO.position.lngDeg); - LatLngBounds bounds = LatLngBounds.newInstance(latLngZoomFirstTime, latLngZoomFirstTime); - if (newBounds == null) { - newBounds = bounds; - } else { - newBounds.extend(bounds.getNorthEast()); - newBounds.extend(bounds.getSouthWest()); - } - } - - } - if (newBounds != null) { - map.setZoomLevel(map.getBoundsZoomLevel(newBounds)); - map.setCenter(newBounds.getCenter()); - map.checkResizeAndCenter(); - mapFirstZoomDone = true; - /* - * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that the map was just zoomed - * to the bounds of the buoys, it was not a zoom or pan triggered by the user. As a consequence the - * mapZoomedOrPannedSinceLastRaceSelection option has to reset again. - */ - mapZoomedOrPannedSinceLastRaceSelectionChange = false; - } - } - } - - /** - * @param from - * time point for first fix to show in tails - * @param to - * time point for last fix to show in tails - */ - private void showBoatsOnMap(Date from, Date to) { - if (map != null) { - LatLngBounds newMapBounds = null; - Set competitorDAOsOfUnusedTails = new HashSet(tails.keySet()); - Set competitorDAOsOfUnusedMarkers = new HashSet(boatMarkers.keySet()); - Collection competitorsToShow = getCompetitorsToShow(); - for (CompetitorDAO competitorDAO : competitorsToShow) { - if (fixes.containsKey(competitorDAO)) { - Polyline tail = tails.get(competitorDAO); - if (tail == null) { - tail = createTailAndUpdateIndices(competitorDAO, from, to); - map.addOverlay(tail); - } else { - updateTail(tail, competitorDAO, from, to); - competitorDAOsOfUnusedTails.remove(competitorDAO); - } - LatLngBounds bounds = tail.getBounds(); - if (newMapBounds == null) { - newMapBounds = bounds; - } else { - newMapBounds.extend(bounds.getNorthEast()); - newMapBounds.extend(bounds.getSouthWest()); - } - if (lastShownFix.containsKey(competitorDAO) && lastShownFix.get(competitorDAO) != -1) { - GPSFixDAO lastPos = getBoatFix(competitorDAO); - Marker boatMarker = boatMarkers.get(competitorDAO); - if (boatMarker == null) { - boatMarker = createBoatMarker(competitorDAO, false); - map.addOverlay(boatMarker); - boatMarkers.put(competitorDAO, boatMarker); - } else { - competitorDAOsOfUnusedMarkers.remove(competitorDAO); - // check if anchors match; re-use marker with setImage only if anchors match - Point newAnchor = getBoatImageRotator(lastPos, - competitorsSelectedInMap.contains(competitorDAO)).getAnchor(); - Point oldAnchor = boatMarker.getIcon().getIconAnchor(); - if (oldAnchor.getX() == newAnchor.getX() && oldAnchor.getY() == newAnchor.getY()) { - boatMarker.setLatLng(LatLng.newInstance(lastPos.position.latDeg, - lastPos.position.lngDeg)); - boatMarker.setImage(getBoatImageURL(lastPos, - competitorsSelectedInMap.contains(competitorDAO))); - } else { - // anchors don't match; replace marker - map.removeOverlay(boatMarker); - boatMarker = createBoatMarker(competitorDAO, - competitorsSelectedInMap.contains(competitorDAO)); - map.addOverlay(boatMarker); - boatMarkers.put(competitorDAO, boatMarker); - } - } - } - } - } - if (!mapZoomedOrPannedSinceLastRaceSelectionChange && newMapBounds != null) { - map.setZoomLevel(map.getBoundsZoomLevel(newMapBounds)); - map.setCenter(newMapBounds.getCenter()); - mapFirstZoomDone = true; - } - for (CompetitorDAO unusedMarkerCompetitorDAO : competitorDAOsOfUnusedMarkers) { - map.removeOverlay(boatMarkers.remove(unusedMarkerCompetitorDAO)); - } - for (CompetitorDAO unusedTailCompetitorDAO : competitorDAOsOfUnusedTails) { - map.removeOverlay(tails.remove(unusedTailCompetitorDAO)); - } - } - } - - private String getBoatImageURL(GPSFixDAO boatFix, boolean highlighted) { - return getBoatImageURL(getBoatImageRotator(boatFix, highlighted), boatFix); - } - - private String getBoatImageURL(ImageRotator boatImageRotator, GPSFixDAO boatFix) { - // the possible zoom level range is 0 to 21 (zoom level 0 would show the whole world) - int zoomLevel = map.getZoomLevel(); - double minScaleFactor = 0.1; - double maxScaleFactor = 1.0; - - double scaleDiffPerZoomLevel = (maxScaleFactor - minScaleFactor) / 21.0; - - double scaleFactor = minScaleFactor + scaleDiffPerZoomLevel * zoomLevel; - - return boatImageRotator.getRotatedImageURL(boatFix.speedWithBearing.bearingInDegrees, scaleFactor); - } - - private Icon getBoatImageIcon(GPSFixDAO boatFix, boolean highlighted) { - ImageRotator boatImageRotator = getBoatImageRotator(boatFix, highlighted); - Icon icon = Icon.newInstance(getBoatImageURL(boatImageRotator, boatFix)); - icon.setIconAnchor(boatImageRotator.getAnchor()); - return icon; - } - - private ImageRotator getBoatImageRotator(GPSFixDAO boatFix, boolean highlighted) { - if (boatFix.tack == Tack.PORT) { - if (LegType.DOWNWIND == boatFix.legType) { - if (highlighted) { - return boatIconHighlightedDownwindStarboardRotator; - } else { - return boatIconDownwindStarboardRotator; - } - } else { - if (highlighted) { - return boatIconHighlightedStarboardRotator; - } else { - return boatIconStarboardRotator; - } - } - } else { - if (LegType.DOWNWIND == boatFix.legType) { - if (highlighted) { - return boatIconHighlightedDownwindPortRotator; - } else { - return boatIconDownwindPortRotator; - } - } else { - if (highlighted) { - return boatIconHighlightedPortRotator; - } else { - return boatIconPortRotator; - } - } - } - } - - private GPSFixDAO getBoatFix(CompetitorDAO competitorDAO) { - return fixes.get(competitorDAO).get(lastShownFix.get(competitorDAO)); - } - private Collection getCompetitorsToShow() { if (showOnlySelected.getValue()) { return competitorsSelectedInMap; @@ -956,259 +387,6 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe } } - /** - * If the tail starts before from, removes leading vertices from tail that are before - * from. This is determined by using the {@link #firstShownFix} index which tells us where in - * {@link #fixes} we find the sequence of fixes currently represented in the tail. - *

- * - * If the tail starts after from, vertices for those {@link #fixes} for competitorDAO at - * or after time point from and before the time point of the first fix displayed so far in the tail and - * before to are prepended to the tail. - *

- * - * Now to the end of the tail: if the existing tail's end exceeds to, the vertices in excess are - * removed (aided by {@link #lastShownFix}). Otherwise, for the competitor's fixes starting at the tail's end up to - * to are appended to the tail. - *

- * - * When this method returns, {@link #firstShownFix} and {@link #lastShownFix} have been updated accordingly. - */ - private void updateTail(Polyline tail, CompetitorDAO competitorDAO, Date from, Date to) { - List fixesForCompetitor = fixes.get(competitorDAO); - int indexOfFirstShownFix = firstShownFix.get(competitorDAO) == null ? -1 : firstShownFix.get(competitorDAO); - while (indexOfFirstShownFix != -1 && tail.getVertexCount() > 0 - && fixesForCompetitor.get(indexOfFirstShownFix).timepoint.before(from)) { - tail.deleteVertex(0); - indexOfFirstShownFix++; - } - // now the polyline contains no more vertices representing fixes before "from"; - // go back in time starting at indexOfFirstShownFix while the fixes are still at or after "from" - // and insert corresponding vertices into the polyline - while (indexOfFirstShownFix > 0 && !fixesForCompetitor.get(indexOfFirstShownFix - 1).timepoint.before(from)) { - indexOfFirstShownFix--; - GPSFixDAO fix = fixesForCompetitor.get(indexOfFirstShownFix); - tail.insertVertex(0, LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg)); - } - // now adjust the polylines tail: remove excess vertices that are after "to" - int indexOfLastShownFix = lastShownFix.get(competitorDAO) == null ? -1 : lastShownFix.get(competitorDAO); - while (indexOfLastShownFix != -1 && tail.getVertexCount() > 0 - && fixesForCompetitor.get(indexOfLastShownFix).timepoint.after(to)) { - tail.deleteVertex(tail.getVertexCount() - 1); - indexOfLastShownFix--; - } - // now the polyline contains no more vertices representing fixes after "to"; - // go forward in time starting at indexOfLastShownFix while the fixes are still at or before "to" - // and insert corresponding vertices into the polyline - while (indexOfLastShownFix < fixesForCompetitor.size() - 1 - && !fixesForCompetitor.get(indexOfLastShownFix + 1).timepoint.after(to)) { - indexOfLastShownFix++; - GPSFixDAO fix = fixesForCompetitor.get(indexOfLastShownFix); - tail.insertVertex(tail.getVertexCount(), LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg)); - } - firstShownFix.put(competitorDAO, indexOfFirstShownFix); - lastShownFix.put(competitorDAO, indexOfLastShownFix); - } - - private Marker createBuoyMarker(final MarkDAO markDAO) { - MarkerOptions options = MarkerOptions.newInstance(); - if (buoyIcon != null) { - options.setIcon(buoyIcon); - } - options.setTitle(markDAO.name); - final Marker buoyMarker = new Marker(LatLng.newInstance(markDAO.position.latDeg, markDAO.position.lngDeg), - options); - buoyMarker.addMarkerClickHandler(new MarkerClickHandler() { - @Override - public void onClick(MarkerClickEvent event) { - LatLng latlng = buoyMarker.getLatLng(); - showMarkInfoWindow(markDAO, latlng); - } - }); - return buoyMarker; - } - - private Marker createBoatMarker(final CompetitorDAO competitorDAO, boolean highlighted) { - GPSFixDAO boatFix = getBoatFix(competitorDAO); - double latDeg = boatFix.position.latDeg; - double lngDeg = boatFix.position.lngDeg; - MarkerOptions options = MarkerOptions.newInstance(); - Icon icon = getBoatImageIcon(boatFix, highlighted); - options.setIcon(icon); - options.setTitle(competitorDAO.name); - final Marker boatMarker = new Marker(LatLng.newInstance(latDeg, lngDeg), options); - boatMarker.addMarkerClickHandler(new MarkerClickHandler() { - @Override - public void onClick(MarkerClickEvent event) { - LatLng latlng = boatMarker.getLatLng(); - showCompetitorInfoWindow(competitorDAO, latlng); - } - }); - boatMarker.addMarkerMouseOverHandler(new MarkerMouseOverHandler() { - @Override - public void onMouseOver(MarkerMouseOverEvent event) { - map.setTitle(competitorDAO.name); - // setSelectedInMap(competitorDAO, true); - // quickRanksBox.setItemSelected(quickRanksList.indexOf(competitorDAO), true); - } - }); - boatMarker.addMarkerMouseOutHandler(new MarkerMouseOutHandler() { - @Override - public void onMouseOut(MarkerMouseOutEvent event) { - map.setTitle(""); - } - }); - return boatMarker; - } - - private void showMarkInfoWindow(MarkDAO markDAO, LatLng latlng) { - map.getInfoWindow().open(latlng, new InfoWindowContent(getInfoWindowContent(markDAO))); - } - - private void showCompetitorInfoWindow(final CompetitorDAO competitorDAO, LatLng where) { - GPSFixDAO latestFixForCompetitor = getBoatFix(competitorDAO); - map.getInfoWindow().open(where, - new InfoWindowContent(getInfoWindowContent(competitorDAO, latestFixForCompetitor))); - } - - private Widget getInfoWindowContent(MarkDAO markDAO) { - VerticalPanel result = new VerticalPanel(); - result.add(new Label("Mark " + markDAO.name)); - result.add(new Label("" + markDAO.position)); - return result; - } - - private Widget getInfoWindowContent(CompetitorDAO competitorDAO, GPSFixDAO lastFix) { - final VerticalPanel result = new VerticalPanel(); - result.add(new Label("Competitor " + competitorDAO.name)); - result.add(new Label("" + lastFix.position)); - result.add(new Label(lastFix.speedWithBearing.speedInKnots + "kts " + lastFix.speedWithBearing.bearingInDegrees - + "deg")); - result.add(new Label("Tack: " + lastFix.tack.name())); - List> selection = newRaceListBox.getSelectedEventAndRace(); - if (!selection.isEmpty()) { - EventDAO event = selection.get(selection.size() - 1).getA(); - RaceDAO race = selection.get(selection.size() - 1).getC(); - if (event != null && race != null) { - Map from = new HashMap(); - from.put(competitorDAO, fixes.get(competitorDAO).get(firstShownFix.get(competitorDAO)).timepoint); - Map to = new HashMap(); - to.put(competitorDAO, getBoatFix(competitorDAO).timepoint); - /* currently not showing Douglas-Peucker points; TODO use checkboxes to select what to show (Bug #6) */ - sailingService.getDouglasPoints(new EventNameAndRaceName(event.name, race.name), from, to, 3, - new AsyncCallback>>() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage()); - } - - @Override - public void onSuccess(Map> result) { - RaceMapPanel.this.lastDouglasPeuckerResult = result; - if (douglasMarkers != null) { - removeAllMarkDouglasPeuckerpoints(); - } - if (!timer.isPlaying()) { - if (checkBoxDouglasPeuckerPoints.getValue()) { - showMarkDouglasPeuckerPoints(result); - } - } - } - }); - sailingService.getManeuvers(new EventNameAndRaceName(event.name, race.name), from, to, - new AsyncCallback>>() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage()); - } - - @Override - public void onSuccess(Map> result) { - RaceMapPanel.this.lastManeuverResult = result; - if (maneuverMarkers != null) { - removeAllManeuverMarkers(); - } - if (!timer.isPlaying()) { - showManeuvers(result); - } - } - }); - - } - } - return result; - } - - private String getColorString(CompetitorDAO competitorDAO) { - // TODO green no more than 70, red no less than 120 - return "#" + (Integer.toHexString(competitorDAO.hashCode()) + "000000").substring(0, 4).toUpperCase() + "00"; - } - - /** - * Creates a polyline for the competitor represented by competitorDAO, taking the fixes from - * {@link #fixes fixes.get(competitorDAO)} and using the fixes starting at time point from (inclusive) - * up to the last fix with time point before to. The polyline is returned. Updates are applied to - * {@link #lastShownFix}, {@link #firstShownFix} and {@link #tails}. - */ - private Polyline createTailAndUpdateIndices(final CompetitorDAO competitorDAO, Date from, Date to) { - List points = new ArrayList(); - List fixesForCompetitor = fixes.get(competitorDAO); - int indexOfFirst = -1; - int indexOfLast = -1; - int i = 0; - for (Iterator fixIter = fixesForCompetitor.iterator(); fixIter.hasNext() && indexOfLast == -1;) { - GPSFixDAO fix = fixIter.next(); - if (!fix.timepoint.before(to)) { - indexOfLast = i; - } else { - LatLng point = null; - if (indexOfFirst == -1) { - if (!fix.timepoint.before(from)) { - indexOfFirst = i; - point = LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg); - } - } else { - point = LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg); - } - if (point != null) { - points.add(point); - } - } - i++; - } - if (indexOfLast == -1) { - indexOfLast = i - 1; - } - if (indexOfFirst != -1 && indexOfLast != -1) { - firstShownFix.put(competitorDAO, indexOfFirst); - lastShownFix.put(competitorDAO, indexOfLast); - } - PolylineOptions options = PolylineOptions.newInstance( - /* clickable */true, /* geodesic */true); - Polyline result = new Polyline(points.toArray(new LatLng[0]), getColorString(competitorDAO), /* width */3, - /* opacity */0.5, options); - result.addPolylineClickHandler(new PolylineClickHandler() { - @Override - public void onClick(PolylineClickEvent event) { - showCompetitorInfoWindow(competitorDAO, lastMousePosition); - } - }); - result.addPolylineMouseOverHandler(new PolylineMouseOverHandler() { - @Override - public void onMouseOver(PolylineMouseOverEvent event) { - map.setTitle(competitorDAO.name); - } - }); - result.addPolylineMouseOutHandler(new PolylineMouseOutHandler() { - @Override - public void onMouseOut(PolylineMouseOutEvent event) { - map.setTitle(""); - } - }); - tails.put(competitorDAO, result); - return result; - } - /* * (non-Javadoc) * @@ -1222,114 +400,27 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe ((RequiresResize) child).onResize(); } // and ensure the map (indirect child) is also informed about resize - if (this.map != null) { - this.map.onResize(); + if (raceMap.map != null) { + raceMap.map.onResize(); } } - private void removeAllMarkDouglasPeuckerpoints() { - if (douglasMarkers != null) { - for (Marker marker : douglasMarkers) { - map.removeOverlay(marker); - } - } - douglasMarkers = null; + private void setMapDisplayOptions() + { + raceMap.showManeuverTack = getCheckboxValueManeuver("TACK"); + raceMap.showManeuverJibe = getCheckboxValueManeuver("JIBE"); + raceMap.showManeuverHeadUp = getCheckboxValueManeuver("HEAD_UP"); + raceMap.showManeuverBearAway = getCheckboxValueManeuver("BEAR_AWAY"); + raceMap.showManeuverPenaltyCircle = getCheckboxValueManeuver("PENALTY_CIRCLE"); + raceMap.showManeuverMarkPassing = getCheckboxValueManeuver("MARK_PASSING"); + raceMap.showManeuverOther = getCheckboxValueManeuver("OTHER"); + + raceMap.showDouglasPeuckerPoints = checkBoxDouglasPeuckerPoints.getValue(); } - - private void removeAllManeuverMarkers() { - if (maneuverMarkers != null) { - for (Marker marker : maneuverMarkers) { - map.removeOverlay(marker); - } - maneuverMarkers = null; - } - } - - private void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { - douglasMarkers = new HashSet(); - if (map != null && gpsFixPointMapForCompetitors != null) { - Set keySet = gpsFixPointMapForCompetitors.keySet(); - Iterator iter = keySet.iterator(); - while (iter.hasNext()) { - CompetitorDAO competitorDAO = iter.next(); - List gpsFix = gpsFixPointMapForCompetitors.get(competitorDAO); - for (GPSFixDAO fix : gpsFix) { - LatLng latLng = LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg); - MarkerOptions options = MarkerOptions.newInstance(); - options.setTitle(fix.timepoint+": "+fix.position+", "+fix.speedWithBearing.toString()); - Marker marker = new Marker(latLng, options); - douglasMarkers.add(marker); - map.addOverlay(marker); - } - } - } - } - + private void showManeuvers(Map> maneuvers) { - maneuverMarkers = new HashSet(); - if (map != null && maneuvers != null) { - Set keySet = maneuvers.keySet(); - Iterator iter = keySet.iterator(); - while (iter.hasNext()) { - CompetitorDAO competitorDAO = iter.next(); - List maneuversForCompetitor = maneuvers.get(competitorDAO); - for (ManeuverDAO maneuver : maneuversForCompetitor) { - boolean showThisManeuver = true; - LatLng latLng = LatLng.newInstance(maneuver.position.latDeg, maneuver.position.lngDeg); - MarkerOptions options = MarkerOptions.newInstance(); - options.setTitle("" + maneuver.timepoint + ": " + maneuver.type + " " - + maneuver.directionChangeInDegrees + "deg from " + maneuver.speedWithBearingBefore - + " to " + maneuver.speedWithBearingAfter); - if (maneuver.type.equals("TACK") && getCheckboxValueManeuver("TACK")) { - if (maneuver.newTack.equals("PORT")) { - options.setIcon(tackToPortIcon); - } else { - options.setIcon(tackToStarboardIcon); - } - } else if (maneuver.type.equals("JIBE") && getCheckboxValueManeuver("JIBE")) { - if (maneuver.newTack.equals("PORT")) { - options.setIcon(jibeToPortIcon); - } else { - options.setIcon(jibeToStarboardIcon); - } - } else if (maneuver.type.equals("HEAD_UP") && getCheckboxValueManeuver("HEAD_UP")) { - if (maneuver.newTack.equals("PORT")) { - options.setIcon(headUpOnPortIcon); - } else { - options.setIcon(headUpOnStarboardIcon); - } - } else if (maneuver.type.equals("BEAR_AWAY") && getCheckboxValueManeuver("BEAR_AWAY")) { - if (maneuver.newTack.equals("PORT")) { - options.setIcon(bearAwayOnPortIcon); - } else { - options.setIcon(bearAwayOnStarboardIcon); - } - } else if (maneuver.type.equals("PENALTY_CIRCLE") && getCheckboxValueManeuver("PENALTY_CIRCLE")) { - if (maneuver.newTack.equals("PORT")) { - options.setIcon(penaltyCircleToPortIcon); - } else { - options.setIcon(penaltyCircleToStarboardIcon); - } - } else if (maneuver.type.equals("MARK_PASSING") && getCheckboxValueManeuver("MARK_PASSING")) { - if (maneuver.newTack.equals("PORT")) { - options.setIcon(markPassingToPortIcon); - } else { - options.setIcon(markPassingToStarboardIcon); - } - } else { - if (maneuver.type.equals("UNKNOWN") && getCheckboxValueManeuver("OTHER")) { - options.setIcon(unknownManeuverIcon); - } else { - showThisManeuver = false; - } - } - if (showThisManeuver) { - Marker marker = new Marker(latLng, options); - maneuverMarkers.add(marker); - map.addOverlay(marker); - } - } - } - } + setMapDisplayOptions(); + + raceMap.showManeuvers(maneuvers); } } From fe9d22dcf09db1883bd3a1da2fc41171a8ad010b Mon Sep 17 00:00:00 2001 From: fmittag Date: Tue, 10 Jan 2012 15:00:04 +0100 Subject: [PATCH 09/33] removed unused variable --- .../java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java index e2920590d31..76ec6274cfd 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java @@ -178,8 +178,8 @@ public class RaceMap { map.setScrollWheelZoomEnabled(true); map.setContinuousZoom(true); - MapType currentMapType = map.getCurrentMapType(); - Projection projection = currentMapType.getProjection(); +// MapType currentMapType = map.getCurrentMapType(); +// Projection projection = currentMapType.getProjection(); grid.setWidget(gridRow, gridColumn, map); From aee84f85c148600ac4740e950f05ece4dffdb486 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 10 Jan 2012 15:20:41 +0100 Subject: [PATCH 10/33] introduced factory pattern for ConfidenceBasedAverager --- .../sailing/domain/test/ConfidenceTest.java | 69 ++++--------------- .../confidence/AbstractScalarValue.java | 11 +++ .../confidence/ConfidenceBasedAverager.java | 5 ++ .../ConfidenceBasedAveragerFactory.java | 9 +++ .../ConfidenceBasedAveragerFactoryImpl.java | 13 ++++ .../impl/ConfidenceBasedAveragerImpl.java | 7 +- .../confidence/impl/ScalableDouble.java | 33 +++++++++ .../impl/ScalableDoubleWithConfidence.java | 22 ++++++ 8 files changed, 111 insertions(+), 58 deletions(-) create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/AbstractScalarValue.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAverager.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAveragerFactory.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDouble.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDoubleWithConfidence.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java index 58c2757e9f5..4b391b48eaa 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java @@ -9,58 +9,13 @@ import com.sap.sailing.domain.base.Bearing; import com.sap.sailing.domain.base.impl.DegreeBearingImpl; import com.sap.sailing.domain.base.impl.RadianBearingImpl; import com.sap.sailing.domain.common.Util.Pair; +import com.sap.sailing.domain.confidence.ConfidenceBasedAverager; +import com.sap.sailing.domain.confidence.ConfidenceBasedAveragerFactory; import com.sap.sailing.domain.confidence.HasConfidence; import com.sap.sailing.domain.confidence.ScalableValue; -import com.sap.sailing.domain.confidence.impl.ConfidenceBasedAveragerImpl; +import com.sap.sailing.domain.confidence.impl.ScalableDoubleWithConfidence; public class ConfidenceTest { - private static class ScalableDouble implements ScalableValue { - private final Double d; - - public ScalableDouble(Double d) { - this.d = d; - } - - @Override - public ScalableValue multiply(double factor) { - return new ScalableDouble(d * factor); - } - - @Override - public ScalableValue add(ScalableValue t) { - return new ScalableDouble(d+t.getValue()); - } - - @Override - public Double divide(double divisor) { - return d / divisor; - } - - @Override - public Double getValue() { - return d; - } - } - - private static class ScalableDoubleWithConfidence extends ScalableDouble implements HasConfidence { - private final double confidence; - - public ScalableDoubleWithConfidence(double d, double confidence) { - super(d); - this.confidence = confidence; - } - - @Override - public double getConfidence() { - return confidence; - } - - @Override - public ScalableValue getScalableValue() { - return this; - } - } - private static class ScalableBearing implements ScalableValue, Bearing> { private final double sin; private final double cos; @@ -128,14 +83,16 @@ public class ConfidenceTest { @Test public void testAveragingWithEmptyListYieldsNull() { + ConfidenceBasedAverager averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); @SuppressWarnings("unchecked") - Double average = new ConfidenceBasedAveragerImpl().getAverage(); + Double average = averager.getAverage(); assertNull(average); } @Test public void testAveragingWithNullArrayYieldsNull() { - Double average = new ConfidenceBasedAveragerImpl().getAverage((HasConfidence[]) null); + ConfidenceBasedAverager averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); + Double average = averager.getAverage((HasConfidence[]) null); assertNull(average); } @@ -143,8 +100,9 @@ public class ConfidenceTest { public void testAveragingWithTwoDoubles() { ScalableDoubleWithConfidence d1 = new ScalableDoubleWithConfidence(1., 0.5); ScalableDoubleWithConfidence d2 = new ScalableDoubleWithConfidence(2., 0.5); + ConfidenceBasedAverager averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); @SuppressWarnings("unchecked") - Double average = new ConfidenceBasedAveragerImpl().getAverage(d1, d2); + Double average = averager.getAverage(d1, d2); assertEquals(1.5, average, 0.00000001); } @@ -153,8 +111,9 @@ public class ConfidenceTest { ScalableDoubleWithConfidence d1 = new ScalableDoubleWithConfidence(1., 1.); ScalableDoubleWithConfidence d2 = new ScalableDoubleWithConfidence(2., 1.); ScalableDoubleWithConfidence d3 = new ScalableDoubleWithConfidence(3., 2.); + ConfidenceBasedAverager averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); @SuppressWarnings("unchecked") - Double average = new ConfidenceBasedAveragerImpl().getAverage(d1, d2, d3); + Double average = averager.getAverage(d1, d2, d3); assertEquals(2.25, average, 0.00000001); } @@ -162,8 +121,9 @@ public class ConfidenceTest { public void testAveragingWithTwoBearings() { ScalableBearingWithConfidence d1 = new ScalableBearingWithConfidence(new DegreeBearingImpl(350.), 1.); ScalableBearingWithConfidence d2 = new ScalableBearingWithConfidence(new DegreeBearingImpl(10.), 1.); + ConfidenceBasedAverager, Bearing> averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); @SuppressWarnings("unchecked") - Bearing average = new ConfidenceBasedAveragerImpl, Bearing>().getAverage(d1, d2); + Bearing average = averager.getAverage(d1, d2); assertEquals(0, average.getDegrees(), 0.00000001); } @@ -172,8 +132,9 @@ public class ConfidenceTest { ScalableBearingWithConfidence d1 = new ScalableBearingWithConfidence(new DegreeBearingImpl(350.), 1.); ScalableBearingWithConfidence d2 = new ScalableBearingWithConfidence(new DegreeBearingImpl(10.), 1.); ScalableBearingWithConfidence d3 = new ScalableBearingWithConfidence(new DegreeBearingImpl(20.), 2.); + ConfidenceBasedAverager, Bearing> averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); @SuppressWarnings("unchecked") - Bearing average = new ConfidenceBasedAveragerImpl, Bearing>().getAverage(d1, d2, d3); + Bearing average = averager.getAverage(d1, d2, d3); assertEquals(10, average.getDegrees(), 0.1); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/AbstractScalarValue.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/AbstractScalarValue.java new file mode 100755 index 00000000000..8866576e8aa --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/AbstractScalarValue.java @@ -0,0 +1,11 @@ +package com.sap.sailing.domain.confidence; + +/** + * A {@link ScalableValue} where the ValueType and the AveragesTo type are both the same because + * the value type is itself scalable, offering addition, multiplication and division. + * + * @author Axel Uhl (d043530) + * + */ +public interface AbstractScalarValue extends ScalableValue { +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAverager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAverager.java new file mode 100755 index 00000000000..47459d6c6b8 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAverager.java @@ -0,0 +1,5 @@ +package com.sap.sailing.domain.confidence; + +public interface ConfidenceBasedAverager { + AveragesTo getAverage(HasConfidence... values); +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAveragerFactory.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAveragerFactory.java new file mode 100755 index 00000000000..f490d8f1c4b --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAveragerFactory.java @@ -0,0 +1,9 @@ +package com.sap.sailing.domain.confidence; + +import com.sap.sailing.domain.confidence.impl.ConfidenceBasedAveragerFactoryImpl; + +public interface ConfidenceBasedAveragerFactory { + ConfidenceBasedAveragerFactory INSTANCE = new ConfidenceBasedAveragerFactoryImpl(); + + ConfidenceBasedAverager createAverager(); +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java new file mode 100755 index 00000000000..29676ff0943 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java @@ -0,0 +1,13 @@ +package com.sap.sailing.domain.confidence.impl; + +import com.sap.sailing.domain.confidence.ConfidenceBasedAverager; +import com.sap.sailing.domain.confidence.ConfidenceBasedAveragerFactory; + +public class ConfidenceBasedAveragerFactoryImpl implements ConfidenceBasedAveragerFactory { + + @Override + public ConfidenceBasedAverager createAverager() { + return new ConfidenceBasedAveragerImpl(); + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java index ffbeeb41231..550101dab06 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java @@ -1,5 +1,6 @@ package com.sap.sailing.domain.confidence.impl; +import com.sap.sailing.domain.confidence.ConfidenceBasedAverager; import com.sap.sailing.domain.confidence.HasConfidence; import com.sap.sailing.domain.confidence.ScalableValue; @@ -25,11 +26,9 @@ import com.sap.sailing.domain.confidence.ScalableValue; * devices produce some statistical errors, no matter how small (cf. Heisenberg ;-) ). * * @author Axel Uhl (d043530) - * - * @param - * the type of value to be scaled / averaged using their confidence */ -public class ConfidenceBasedAveragerImpl { +public class ConfidenceBasedAveragerImpl implements ConfidenceBasedAverager { + @Override public AveragesTo getAverage(HasConfidence... values) { if (values == null || values.length == 0) { return null; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDouble.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDouble.java new file mode 100755 index 00000000000..3727713e958 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDouble.java @@ -0,0 +1,33 @@ +package com.sap.sailing.domain.confidence.impl; + +import com.sap.sailing.domain.confidence.AbstractScalarValue; +import com.sap.sailing.domain.confidence.ScalableValue; + +public class ScalableDouble implements AbstractScalarValue { + private final double value; + + public ScalableDouble(double value) { + this.value = value; + } + + @Override + public ScalableValue multiply(double factor) { + return new ScalableDouble(factor*getValue()); + } + + @Override + public ScalableDouble add(ScalableValue t) { + return new ScalableDouble(getValue()+t.getValue()); + } + + @Override + public Double divide(double divisor) { + return getValue()/divisor; + } + + @Override + public Double getValue() { + return value; + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDoubleWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDoubleWithConfidence.java new file mode 100755 index 00000000000..3f96f736833 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDoubleWithConfidence.java @@ -0,0 +1,22 @@ +package com.sap.sailing.domain.confidence.impl; + +import com.sap.sailing.domain.confidence.HasConfidence; + +public class ScalableDoubleWithConfidence extends ScalableDouble implements HasConfidence { + private final double confidence; + + public ScalableDoubleWithConfidence(double d, double confidence) { + super(d); + this.confidence = confidence; + } + + @Override + public double getConfidence() { + return confidence; + } + + @Override + public ScalableDouble getScalableValue() { + return this; + } +} From 00cb2a1348e636b35b0e6e0cee5b4e8b36d62ed5 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 10 Jan 2012 18:14:08 +0100 Subject: [PATCH 11/33] prepared confidence handling for bearing --- .../META-INF/MANIFEST.MF | 3 +- .../META-INF/MANIFEST.MF | 3 +- .../domain/base/BearingWithConfidence.java | 7 +++ .../domain/base/impl/AbstractBearing.java | 63 ++++++++++++++++++- .../domain/confidence/HasConfidence.java | 37 ++++++++--- .../ConfidenceBasedAveragerFactoryImpl.java | 2 - .../impl/CompactGPSFixMovingImpl.java | 5 ++ .../sailing/gwt/ui/adminconsole/RaceMap.java | 2 - 8 files changed, 107 insertions(+), 15 deletions(-) create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java diff --git a/java/com.sap.sailing.declination.test/META-INF/MANIFEST.MF b/java/com.sap.sailing.declination.test/META-INF/MANIFEST.MF index d8b85efa7cc..dfc885cc2b0 100755 --- a/java/com.sap.sailing.declination.test/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.declination.test/META-INF/MANIFEST.MF @@ -6,6 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Require-Bundle: com.sap.sailing.declination, - com.sap.sailing.domain + com.sap.sailing.domain, + com.sap.sailing.domain.common Import-Package: junit.framework;version="4.8.1", org.junit;version="4.8.1" diff --git a/java/com.sap.sailing.declination/META-INF/MANIFEST.MF b/java/com.sap.sailing.declination/META-INF/MANIFEST.MF index 0094411b2df..8713b0a6f0c 100755 --- a/java/com.sap.sailing.declination/META-INF/MANIFEST.MF +++ b/java/com.sap.sailing.declination/META-INF/MANIFEST.MF @@ -6,6 +6,7 @@ Bundle-Version: 1.0.0.qualifier Bundle-Vendor: SAP Bundle-RequiredExecutionEnvironment: JavaSE-1.6 Bundle-ActivationPolicy: lazy -Require-Bundle: com.sap.sailing.domain +Require-Bundle: com.sap.sailing.domain, + com.sap.sailing.domain.common Export-Package: com.sap.sailing.declination, com.sap.sailing.declination.impl;x-friends:="com.sap.sailing.declination.test" diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java new file mode 100755 index 00000000000..041a52b9638 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java @@ -0,0 +1,7 @@ +package com.sap.sailing.domain.base; + +import com.sap.sailing.domain.common.Util.Pair; +import com.sap.sailing.domain.confidence.HasConfidence; + +public interface BearingWithConfidence extends Bearing, HasConfidence, Bearing> { +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java index 737301c4317..5395a35b05c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java @@ -1,8 +1,13 @@ package com.sap.sailing.domain.base.impl; import com.sap.sailing.domain.base.Bearing; +import com.sap.sailing.domain.base.BearingWithConfidence; +import com.sap.sailing.domain.common.Util.Pair; +import com.sap.sailing.domain.confidence.ScalableValue; + +public abstract class AbstractBearing implements BearingWithConfidence { + protected static final double DEFAULT_BEARING_CONFIDENCE = 0.9; -public abstract class AbstractBearing implements Bearing { @Override public Bearing reverse() { if (getDegrees() >= 180) { @@ -57,4 +62,60 @@ public abstract class AbstractBearing implements Bearing { public boolean equals(Object object) { return object != null && object instanceof Bearing && getDegrees() == ((Bearing) object).getDegrees(); } + + + @Override + public double getConfidence() { + return DEFAULT_BEARING_CONFIDENCE; + } + + @Override + public ScalableValue, Bearing> getScalableValue() { + return new ScalableBearing(this); + } + + private static class ScalableBearing implements ScalableValue, Bearing> { + private final double sin; + private final double cos; + + public ScalableBearing(Bearing bearing) { + this.sin = Math.sin(bearing.getRadians()); + this.cos = Math.cos(bearing.getRadians()); + } + + private ScalableBearing(double sin, double cos) { + this.sin = sin; + this.cos = cos; + } + + @Override + public ScalableValue, Bearing> multiply(double factor) { + Pair pair = getValue(); + return new ScalableBearing(factor*pair.getA(), factor*pair.getB()); + } + + @Override + public ScalableValue, Bearing> add(ScalableValue, Bearing> t) { + Pair value = getValue(); + Pair tValue = t.getValue(); + return new ScalableBearing(value.getA()+tValue.getA(), value.getB()+tValue.getB()); + } + + @Override + public Bearing divide(double divisor) { + double angle; + if (cos == 0) { + angle = sin >= 0 ? Math.PI / 2 : -Math.PI / 2; + } else { + angle = Math.atan2(sin, cos); + } + Bearing result = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); + return result; + } + + @Override + public Pair getValue() { + return new Pair(sin, cos); + } + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java index 2e42a7cbb9a..bd51e7ff0e9 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java @@ -1,18 +1,39 @@ package com.sap.sailing.domain.confidence; +import com.sap.sailing.domain.base.Distance; + /** - * Some values, particularly those obtained from real-world measurements, are not always accurate. Some - * values are derived by interpolating or extrapolating data series obtained through measurement or - * even estimation. Some values are simply guessed by humans and entered into the system.

+ * Some values, particularly those obtained from real-world measurements, are not always accurate. Some values are + * derived by interpolating or extrapolating data series obtained through measurement or even estimation. Some values + * are simply guessed by humans and entered into the system. + *

* - * All those values have a certain level of confidence. In case multiple sources of information about the - * same entity or phenomenon are available, knowing the confidence of each value helps in weighing and - * averaging these values more properly than would be possible without a confidence value. + * All those values have a certain level of confidence. In case multiple sources of information about the same entity or + * phenomenon are available, knowing the confidence of each value helps in weighing and averaging these values more + * properly than would be possible without a confidence value. + *

+ * + * In simple cases, the type used to compute a weighed average over the things equipped with a confidence level is the + * same as the type of the things themselves. In particular, this is the case for scalar types such as {@link Double} + * and {@link Distance}. For non-scalar values, averaging may be non-trivial. For example, averaging a bearing cannot + * simply happen by computing the arithmetic average of the bearing's angles. Instead, an intermediate structure + * providing the sinus and cosinus values of the bearing's angle is used to compute the weighed average tangens. + *

+ * + * Generally, the relationship between the type implementing this interface, the ValueType and the + * AveragesTo type is this: an instance of the implementing type can transform itself into a + * {@link ScalableValue} which is then used for computing a weighed sum. The values' weight is their + * {@link #getConfidence() confidence}. The sum (which is still a {@link ScalableValue} because + * {@link ScalableValue#add(ScalableValue)} returns again a {@link ScalableValue}) is then + * {@link ScalableValue#divide(double) divided} by the sum of the confidences. This "division" is expected to + * produce an object of type AveragesTo. Usually, AveragesTo would be the same as + * the class implementing this interface. * * @author Axel Uhl (d043530) * - * @param the type of the scalable value. See particularly the {@link #scale()} method. - * + * @param + * the type of the scalable value. See particularly the {@link #scale()} method. + * */ public interface HasConfidence { /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java index 29676ff0943..fd8bd9f89f6 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerFactoryImpl.java @@ -4,10 +4,8 @@ import com.sap.sailing.domain.confidence.ConfidenceBasedAverager; import com.sap.sailing.domain.confidence.ConfidenceBasedAveragerFactory; public class ConfidenceBasedAveragerFactoryImpl implements ConfidenceBasedAveragerFactory { - @Override public ConfidenceBasedAverager createAverager() { return new ConfidenceBasedAveragerImpl(); } - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CompactGPSFixMovingImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CompactGPSFixMovingImpl.java index 249760475a8..4c456c3f48d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CompactGPSFixMovingImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/CompactGPSFixMovingImpl.java @@ -66,6 +66,11 @@ public class CompactGPSFixMovingImpl extends CompactGPSFixImpl implements GPSFix } private class CompactBearing extends AbstractBearing { + @Override + public double getConfidence() { + return DEFAULT_BEARING_CONFIDENCE; + } + @Override public double getDegrees() { return degBearing; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java index 76ec6274cfd..7f1675df247 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java @@ -12,7 +12,6 @@ import java.util.Set; import com.google.gwt.core.client.GWT; import com.google.gwt.maps.client.InfoWindowContent; -import com.google.gwt.maps.client.MapType; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.Maps; import com.google.gwt.maps.client.control.LargeMapControl3D; @@ -30,7 +29,6 @@ import com.google.gwt.maps.client.event.PolylineMouseOverHandler; import com.google.gwt.maps.client.geom.LatLng; import com.google.gwt.maps.client.geom.LatLngBounds; import com.google.gwt.maps.client.geom.Point; -import com.google.gwt.maps.client.geom.Projection; import com.google.gwt.maps.client.overlay.Icon; import com.google.gwt.maps.client.overlay.Marker; import com.google.gwt.maps.client.overlay.MarkerOptions; From 4298162ca14950ed82c9a3f6c9ec201a089d3df7 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jan 2012 08:39:31 +0100 Subject: [PATCH 12/33] generalized BearingCluster into a generic type, enabling non-invasive BearingWithConfidenceCluster --- .../sailing/domain/test/ConfidenceTest.java | 27 ++-- .../domain/base/BearingWithConfidence.java | 2 +- .../domain/base/impl/AbstractBearing.java | 12 +- .../confidence/ConfidenceBasedAverager.java | 2 +- .../impl/ConfidenceBasedAveragerImpl.java | 20 ++- .../domain/tracking/impl/BearingCluster.java | 121 +------------- .../impl/BearingWithConfidenceCluster.java | 37 +++++ .../tracking/impl/GenericBearingCluster.java | 147 ++++++++++++++++++ 8 files changed, 224 insertions(+), 144 deletions(-) create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GenericBearingCluster.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java index 4b391b48eaa..efd019c925a 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java @@ -3,6 +3,11 @@ package com.sap.sailing.domain.test; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertNull; +import java.util.Arrays; +import java.util.Collections; +import java.util.List; +import java.util.Set; + import org.junit.Test; import com.sap.sailing.domain.base.Bearing; @@ -84,15 +89,15 @@ public class ConfidenceTest { @Test public void testAveragingWithEmptyListYieldsNull() { ConfidenceBasedAverager averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); - @SuppressWarnings("unchecked") - Double average = averager.getAverage(); + Set> emptySet = Collections.emptySet(); + Double average = averager.getAverage(emptySet); assertNull(average); } @Test public void testAveragingWithNullArrayYieldsNull() { ConfidenceBasedAverager averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); - Double average = averager.getAverage((HasConfidence[]) null); + Double average = averager.getAverage(null); assertNull(average); } @@ -101,8 +106,8 @@ public class ConfidenceTest { ScalableDoubleWithConfidence d1 = new ScalableDoubleWithConfidence(1., 0.5); ScalableDoubleWithConfidence d2 = new ScalableDoubleWithConfidence(2., 0.5); ConfidenceBasedAverager averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); - @SuppressWarnings("unchecked") - Double average = averager.getAverage(d1, d2); + List list = Arrays.asList(d1, d2); + Double average = averager.getAverage(list); assertEquals(1.5, average, 0.00000001); } @@ -112,8 +117,8 @@ public class ConfidenceTest { ScalableDoubleWithConfidence d2 = new ScalableDoubleWithConfidence(2., 1.); ScalableDoubleWithConfidence d3 = new ScalableDoubleWithConfidence(3., 2.); ConfidenceBasedAverager averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); - @SuppressWarnings("unchecked") - Double average = averager.getAverage(d1, d2, d3); + List list = Arrays.asList(d1, d2, d3); + Double average = averager.getAverage(list); assertEquals(2.25, average, 0.00000001); } @@ -122,8 +127,8 @@ public class ConfidenceTest { ScalableBearingWithConfidence d1 = new ScalableBearingWithConfidence(new DegreeBearingImpl(350.), 1.); ScalableBearingWithConfidence d2 = new ScalableBearingWithConfidence(new DegreeBearingImpl(10.), 1.); ConfidenceBasedAverager, Bearing> averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); - @SuppressWarnings("unchecked") - Bearing average = averager.getAverage(d1, d2); + List list = Arrays.asList(d1, d2); + Bearing average = averager.getAverage(list); assertEquals(0, average.getDegrees(), 0.00000001); } @@ -133,8 +138,8 @@ public class ConfidenceTest { ScalableBearingWithConfidence d2 = new ScalableBearingWithConfidence(new DegreeBearingImpl(10.), 1.); ScalableBearingWithConfidence d3 = new ScalableBearingWithConfidence(new DegreeBearingImpl(20.), 2.); ConfidenceBasedAverager, Bearing> averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); - @SuppressWarnings("unchecked") - Bearing average = averager.getAverage(d1, d2, d3); + List list = Arrays.asList(d1, d2, d3); + Bearing average = averager.getAverage(list); assertEquals(10, average.getDegrees(), 0.1); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java index 041a52b9638..fd5b1be920f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java @@ -3,5 +3,5 @@ package com.sap.sailing.domain.base; import com.sap.sailing.domain.common.Util.Pair; import com.sap.sailing.domain.confidence.HasConfidence; -public interface BearingWithConfidence extends Bearing, HasConfidence, Bearing> { +public interface BearingWithConfidence extends Bearing, HasConfidence, BearingWithConfidence> { } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java index 5395a35b05c..2b6d8d55d9e 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java @@ -70,11 +70,11 @@ public abstract class AbstractBearing implements BearingWithConfidence { } @Override - public ScalableValue, Bearing> getScalableValue() { + public ScalableValue, BearingWithConfidence> getScalableValue() { return new ScalableBearing(this); } - private static class ScalableBearing implements ScalableValue, Bearing> { + private static class ScalableBearing implements ScalableValue, BearingWithConfidence> { private final double sin; private final double cos; @@ -89,27 +89,27 @@ public abstract class AbstractBearing implements BearingWithConfidence { } @Override - public ScalableValue, Bearing> multiply(double factor) { + public ScalableValue, BearingWithConfidence> multiply(double factor) { Pair pair = getValue(); return new ScalableBearing(factor*pair.getA(), factor*pair.getB()); } @Override - public ScalableValue, Bearing> add(ScalableValue, Bearing> t) { + public ScalableValue, BearingWithConfidence> add(ScalableValue, BearingWithConfidence> t) { Pair value = getValue(); Pair tValue = t.getValue(); return new ScalableBearing(value.getA()+tValue.getA(), value.getB()+tValue.getB()); } @Override - public Bearing divide(double divisor) { + public BearingWithConfidence divide(double divisor) { double angle; if (cos == 0) { angle = sin >= 0 ? Math.PI / 2 : -Math.PI / 2; } else { angle = Math.atan2(sin, cos); } - Bearing result = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); + BearingWithConfidence result = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); return result; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAverager.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAverager.java index 47459d6c6b8..0dd404e98da 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAverager.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ConfidenceBasedAverager.java @@ -1,5 +1,5 @@ package com.sap.sailing.domain.confidence; public interface ConfidenceBasedAverager { - AveragesTo getAverage(HasConfidence... values); + AveragesTo getAverage(Iterable> values); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java index 550101dab06..80186aa61ed 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java @@ -1,5 +1,8 @@ package com.sap.sailing.domain.confidence.impl; +import java.util.Iterator; + +import com.sap.sailing.domain.common.Util; import com.sap.sailing.domain.confidence.ConfidenceBasedAverager; import com.sap.sailing.domain.confidence.HasConfidence; import com.sap.sailing.domain.confidence.ScalableValue; @@ -29,15 +32,18 @@ import com.sap.sailing.domain.confidence.ScalableValue; */ public class ConfidenceBasedAveragerImpl implements ConfidenceBasedAverager { @Override - public AveragesTo getAverage(HasConfidence... values) { - if (values == null || values.length == 0) { + public AveragesTo getAverage(Iterable> values) { + if (values == null || Util.isEmpty(values)) { return null; } else { - ScalableValue numerator = values[0].getScalableValue().multiply(values[0].getConfidence()); - double confidenceSum = values[0].getConfidence(); - for (int i=1; i> iter = values.iterator(); + HasConfidence next = iter.next(); + ScalableValue numerator = next.getScalableValue().multiply(next.getConfidence()); + double confidenceSum = next.getConfidence(); + while (iter.hasNext()) { + next = iter.next(); + numerator = numerator.add(next.getScalableValue().multiply(next.getConfidence())); + confidenceSum += next.getConfidence(); } AveragesTo result = numerator.divide(confidenceSum); return result; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java index 7b5986d35ee..6c8470c6f68 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java @@ -1,11 +1,6 @@ package com.sap.sailing.domain.tracking.impl; -import java.util.ArrayList; -import java.util.List; - import com.sap.sailing.domain.base.Bearing; -import com.sap.sailing.domain.base.impl.RadianBearingImpl; -import com.sap.sailing.domain.common.Util.Pair; /** * Contains a number of {@link Bearing} objects and maintains the average bearing. For a given {@link Bearing} it @@ -20,120 +15,10 @@ import com.sap.sailing.domain.common.Util.Pair; * @author Axel Uhl (d043530) * */ -public class BearingCluster { - private final List bearings; - private double sumSin; - private double sumCos; - - public BearingCluster() { - bearings = new ArrayList(); - sumSin = 0.0; - sumCos = 0.0; - } - - /** - * Finds the two bearings in the cluster that are farthest apart (at least minimumDegreeDifferenceBetweenTacks). - * Then, the remaining bearings in this cluster are associated with the one of the two extreme bearings to which they are - * closer. The two resulting clusters are returned. - * - * @param minimumDegreeDifferenceBetweenTacks - * tells the minimum degree difference that must exist between the two extreme bearings before they are - * considered to represent boats on different tacks. If more than one bearing exists in this cluster - * but no two bearings are at least minimumDegreeDifferenceBetweenTacks degrees apart from - * each other, only fir first of the two clusters returned will contain bearings while the second one - * remains empty. - * @return two bearing clusters; both empty if this cluster is empty; only the second one empty if this cluster - * contains only one bearing. Otherwise, the two bearings farthest apart (greatest absolute - * {@link Bearing#getDifferenceTo(Bearing) difference}) are guaranteed not to be in different clusters, and - * all other bearings contained in this cluster will be contained in the cluster that contains the extreme - * bearing to which it's closer. - */ - public BearingCluster[] splitInTwo(double minimumDegreeDifferenceBetweenTacks) { - BearingCluster[] result = new BearingCluster[2]; - result[0] = new BearingCluster(); - result[1] = new BearingCluster(); - if (bearings.size() >= 2) { - Pair extremeBearings = getExtremeBearings(minimumDegreeDifferenceBetweenTacks); - if (extremeBearings != null) { - result[0].add(extremeBearings.getA()); - result[1].add(extremeBearings.getB()); - } - for (Bearing bearing : bearings) { - if (extremeBearings == null || (bearing != extremeBearings.getA() && bearing != extremeBearings.getB())) { - if (extremeBearings == null || result[0].getDifferenceFromAverage(bearing) <= result[1].getDifferenceFromAverage(bearing)) { - result[0].add(bearing); - } else { - result[1].add(bearing); - } - } - } - } else if (!bearings.isEmpty()) { - // add the only bearing to the first of the two resulting clusters - result[0].add(bearings.get(0)); - } - return result; - } - - private Pair getExtremeBearings(double minimumDegreeDifferenceBetweenTacks) { - assert bearings.size() >= 2; - double maxAbsDegDiff = minimumDegreeDifferenceBetweenTacks; - Pair result = null; - for (int i=0; i= maxAbsDegDiff) { - result = new Pair(bearings.get(i), bearings.get(j)); - maxAbsDegDiff = Math.abs(bearings.get(i).getDifferenceTo(bearings.get(j)).getDegrees()); - assert Math.abs(result.getA().getDegrees()-result.getB().getDegrees()) <= 180.; - } - } - } - return result; - } +public class BearingCluster extends GenericBearingCluster { - public boolean isEmpty() { - return bearings.isEmpty(); - } - - public int size() { - return bearings.size(); - } - - public void add(Bearing bearing) { - bearings.add(bearing); - sumSin += Math.sin(bearing.getRadians()); - sumCos += Math.cos(bearing.getRadians()); - } - - /** - * If the cluster contains no bearings, null is returned. Otherwise, the average angle is computed - * by adding up the sin and cos values of the individual bearings, then computing the atan2 of the ratio. - */ - public Bearing getAverage() { - Bearing result = null; - if (!bearings.isEmpty()) { - double angle; - if (sumCos == 0) { - angle = sumSin >= 0 ? Math.PI / 2 : -Math.PI / 2; - } else { - angle = Math.atan2(sumSin, sumCos); - } - result = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); - } - return result; - } - - /** - * Absolute difference to {@link #getAverage() this cluster's average bearing} in degrees. If there is no bearing stored in - * this cluster yet, 0.0 is returned. - * - * @return a value >=0.0 - */ - private double getDifferenceFromAverage(Bearing bearing) { - return bearings.size() == 0 ? 0.0 : Math.abs(getAverage().getDifferenceTo(bearing).getDegrees()); - } - @Override - public String toString() { - return bearings.toString(); + public BearingCluster[] splitInTwo(double minimumDegreeDifferenceBetweenTacks) { + return (BearingCluster[]) super.splitInTwo(minimumDegreeDifferenceBetweenTacks); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java new file mode 100755 index 00000000000..f98ea7ac8e9 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java @@ -0,0 +1,37 @@ +package com.sap.sailing.domain.tracking.impl; + +import com.sap.sailing.domain.base.Bearing; +import com.sap.sailing.domain.base.BearingWithConfidence; +import com.sap.sailing.domain.common.Util.Pair; +import com.sap.sailing.domain.confidence.ConfidenceBasedAverager; +import com.sap.sailing.domain.confidence.ConfidenceBasedAveragerFactory; + +/** + * Contains a number of {@link Bearing} objects and maintains the average bearing. For a given {@link Bearing} it + * can determine the difference to this cluster's average bearing. It can also split the cluster into two, based + * on the two bearings farthest apart. The cluster can contain multiple occurrences of the same and also + * multiple occurrences of mutually equal {@link Bearing} objects which is one possible way of computing a + * weighted average.

+ * + * It is assumed that bearings added to this cluster are no further than 180 degrees apart. Violating this + * rule will lead to unpredictable results. + * + * @author Axel Uhl (d043530) + * + */ +public class BearingWithConfidenceCluster extends GenericBearingCluster { + /** + * If the cluster contains no bearings, null is returned. Otherwise, the average angle is computed + * by adding up the sin and cos values of the individual bearings, then computing the atan2 of the ratio. + */ + @Override + public BearingWithConfidence getAverage() { + ConfidenceBasedAverager, BearingWithConfidence> averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); + return averager.getAverage(getBearings()); + } + + @Override + public BearingWithConfidenceCluster[] splitInTwo(double minimumDegreeDifferenceBetweenTacks) { + return (BearingWithConfidenceCluster[]) super.splitInTwo(minimumDegreeDifferenceBetweenTacks); + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GenericBearingCluster.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GenericBearingCluster.java new file mode 100755 index 00000000000..ed952550d9d --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/GenericBearingCluster.java @@ -0,0 +1,147 @@ +package com.sap.sailing.domain.tracking.impl; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; + +import com.sap.sailing.domain.base.Bearing; +import com.sap.sailing.domain.base.impl.RadianBearingImpl; +import com.sap.sailing.domain.common.Util.Pair; + +/** + * Contains a number of {@link Bearing} objects (or a subtype thereof) and maintains the average bearing. For a given + * {@link Bearing} it can determine the difference to this cluster's average bearing. It can also split the cluster into + * two, based on the two bearings farthest apart. The cluster can contain multiple occurrences of the same and also + * multiple occurrences of mutually equal {@link Bearing} objects which is one possible way of computing a weighted + * average. + *

+ * + * It is assumed that bearings added to this cluster are no further than 180 degrees apart. Violating this rule will + * lead to unpredictable results. + * + * @author Axel Uhl (d043530) + * + * @param the class used for the bearing objects + */ +public class GenericBearingCluster { + private final List bearings; + private double sumSin; + private double sumCos; + + public GenericBearingCluster() { + bearings = new ArrayList(); + sumSin = 0.0; + sumCos = 0.0; + } + + /** + * Finds the two bearings in the cluster that are farthest apart (at least minimumDegreeDifferenceBetweenTacks). + * Then, the remaining bearings in this cluster are associated with the one of the two extreme bearings to which they are + * closer. The two resulting clusters are returned. + * + * @param minimumDegreeDifferenceBetweenTacks + * tells the minimum degree difference that must exist between the two extreme bearings before they are + * considered to represent boats on different tacks. If more than one bearing exists in this cluster + * but no two bearings are at least minimumDegreeDifferenceBetweenTacks degrees apart from + * each other, only fir first of the two clusters returned will contain bearings while the second one + * remains empty. + * @return two bearing clusters; both empty if this cluster is empty; only the second one empty if this cluster + * contains only one bearing. Otherwise, the two bearings farthest apart (greatest absolute + * {@link Bearing#getDifferenceTo(Bearing) difference}) are guaranteed not to be in different clusters, and + * all other bearings contained in this cluster will be contained in the cluster that contains the extreme + * bearing to which it's closer. + */ + public GenericBearingCluster[] splitInTwo(double minimumDegreeDifferenceBetweenTacks) { + @SuppressWarnings("unchecked") + GenericBearingCluster[] result = (GenericBearingCluster[]) new GenericBearingCluster[2]; + result[0] = new GenericBearingCluster(); + result[1] = new GenericBearingCluster(); + if (bearings.size() >= 2) { + Pair extremeBearings = getExtremeBearings(minimumDegreeDifferenceBetweenTacks); + if (extremeBearings != null) { + result[0].add(extremeBearings.getA()); + result[1].add(extremeBearings.getB()); + } + for (BearingType bearing : bearings) { + if (extremeBearings == null || (bearing != extremeBearings.getA() && bearing != extremeBearings.getB())) { + if (extremeBearings == null || result[0].getDifferenceFromAverage(bearing) <= result[1].getDifferenceFromAverage(bearing)) { + result[0].add(bearing); + } else { + result[1].add(bearing); + } + } + } + } else if (!bearings.isEmpty()) { + // add the only bearing to the first of the two resulting clusters + result[0].add(bearings.get(0)); + } + return result; + } + + private Pair getExtremeBearings(double minimumDegreeDifferenceBetweenTacks) { + assert bearings.size() >= 2; + double maxAbsDegDiff = minimumDegreeDifferenceBetweenTacks; + Pair result = null; + for (int i=0; i= maxAbsDegDiff) { + result = new Pair(bearings.get(i), bearings.get(j)); + maxAbsDegDiff = Math.abs(bearings.get(i).getDifferenceTo(bearings.get(j)).getDegrees()); + assert Math.abs(result.getA().getDegrees()-result.getB().getDegrees()) <= 180.; + } + } + } + return result; + } + + public boolean isEmpty() { + return bearings.isEmpty(); + } + + public int size() { + return bearings.size(); + } + + public void add(BearingType bearing) { + bearings.add(bearing); + sumSin += Math.sin(bearing.getRadians()); + sumCos += Math.cos(bearing.getRadians()); + } + + /** + * If the cluster contains no bearings, null is returned. Otherwise, the average angle is computed + * by adding up the sin and cos values of the individual bearings, then computing the atan2 of the ratio. + */ + public Bearing getAverage() { + Bearing result = null; + if (!bearings.isEmpty()) { + double angle; + if (sumCos == 0) { + angle = sumSin >= 0 ? Math.PI / 2 : -Math.PI / 2; + } else { + angle = Math.atan2(sumSin, sumCos); + } + result = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); + } + return result; + } + + /** + * Absolute difference to {@link #getAverage() this cluster's average bearing} in degrees. If there is no bearing stored in + * this cluster yet, 0.0 is returned. + * + * @return a value >=0.0 + */ + private double getDifferenceFromAverage(Bearing bearing) { + return bearings.size() == 0 ? 0.0 : Math.abs(getAverage().getDifferenceTo(bearing).getDegrees()); + } + + protected Iterable getBearings() { + return Collections.unmodifiableCollection(bearings); + } + + @Override + public String toString() { + return bearings.toString(); + } +} From bf361f5552a715f0b4b37dfcbbcfbb183625d7f8 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 11 Jan 2012 08:49:54 +0100 Subject: [PATCH 13/33] duplicated BearingCluster tests to also test BearingWithConfidenceCluster --- .../WindEstimationOnStoredTracksTest.java | 85 +++++++++++++++++++ .../domain/tracking/impl/BearingCluster.java | 10 ++- .../impl/BearingWithConfidenceCluster.java | 10 +++ .../tracking/impl/GenericBearingCluster.java | 16 +++- 4 files changed, 116 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnStoredTracksTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnStoredTracksTest.java index 13bcf058512..10ae5a3f70e 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnStoredTracksTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnStoredTracksTest.java @@ -11,6 +11,7 @@ import java.io.IOException; import org.junit.Test; import com.sap.sailing.domain.base.Bearing; +import com.sap.sailing.domain.base.BearingWithConfidence; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.TimePoint; import com.sap.sailing.domain.base.impl.DegreeBearingImpl; @@ -21,8 +22,92 @@ import com.sap.sailing.domain.tracking.GPSFixMoving; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.Wind; import com.sap.sailing.domain.tracking.impl.BearingCluster; +import com.sap.sailing.domain.tracking.impl.BearingWithConfidenceCluster; public class WindEstimationOnStoredTracksTest extends StoredTrackBasedTestWithTrackedRace { + // ---------------- BearingWithConfidenceCluster ----------------- + + @Test + public void testEmptyBearingWithConfidenceClusterHasNullAverage() { + assertNull(new BearingWithConfidenceCluster().getAverage()); + } + + @Test + public void testBearingWithConfidenceClusterSplit() { + BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); + for (double bearingInDegrees : new double[] { 32.31650532600039, 16.99636033752683, 37.59302174779672, + 27.2860810183163, 319.47157698009613, 325.1617832132204, 31.678409742672212, 35.00547108150359, + 23.934778873669256, 29.76599976685808, 33.19487072661667, 19.0, 33.29318052266396, 32.7371445230587, + 38.26627143611533 }) { + cluster.add(new DegreeBearingImpl(bearingInDegrees)); + } + BearingWithConfidenceCluster[] splitResult = cluster.splitInTwo(/* minimumDegreeDifferenceBetweenTacks */ 15); + assertEquals(2, splitResult.length); + assertNotNull(splitResult[0]); + assertNotNull(splitResult[1]); + assertFalse(splitResult[0].isEmpty()); + assertFalse(splitResult[1].isEmpty()); + assertEquals(322, splitResult[0].getAverage().getDegrees(), 1); + assertEquals(30, splitResult[1].getAverage().getDegrees(), 1); + } + + @Test + public void testBearingWithConfidenceClusterAverageAcrossZero() { + BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); + BearingWithConfidence b1 = new DegreeBearingImpl(355); + BearingWithConfidence b2 = new DegreeBearingImpl(5); + cluster.add(b1); + cluster.add(b2); + BearingWithConfidence average = cluster.getAverage(); + assertEquals(0, average.getDegrees(), 0.00000001); + } + + @Test + public void testBearingWithConfidenceClusterAverageWithZeroSinus() { + BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); + BearingWithConfidence b1 = new DegreeBearingImpl(0); + BearingWithConfidence b2 = new DegreeBearingImpl(0); + cluster.add(b1); + cluster.add(b2); + BearingWithConfidence average = cluster.getAverage(); + assertEquals(0, average.getDegrees(), 0.00000001); + } + + @Test + public void testBearingWithConfidenceClusterAverageWithZeroSinusAndNegativeCosinus() { + BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); + BearingWithConfidence b1 = new DegreeBearingImpl(180); + BearingWithConfidence b2 = new DegreeBearingImpl(180); + cluster.add(b1); + cluster.add(b2); + BearingWithConfidence average = cluster.getAverage(); + assertEquals(180, average.getDegrees(), 0.00000001); + } + + @Test + public void testBearingWithConfidenceClusterAverageWithZeroCosinus() { + BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); + BearingWithConfidence b1 = new DegreeBearingImpl(90); + BearingWithConfidence b2 = new DegreeBearingImpl(90); + cluster.add(b1); + cluster.add(b2); + BearingWithConfidence average = cluster.getAverage(); + assertEquals(90, average.getDegrees(), 0.00000001); + } + + @Test + public void testBearingWithConfidenceClusterAverageWithZeroCosinusAndNegativeSinus() { + BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); + BearingWithConfidence b1 = new DegreeBearingImpl(270); + BearingWithConfidence b2 = new DegreeBearingImpl(270); + cluster.add(b1); + cluster.add(b2); + BearingWithConfidence average = cluster.getAverage(); + assertEquals(270, average.getDegrees(), 0.00000001); + } + + // ---------------- Classical BearingCluster ----------------- + @Test public void testEmptyClusterHasNullAverage() { assertNull(new BearingCluster().getAverage()); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java index 6c8470c6f68..1132d9d6723 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java @@ -16,8 +16,16 @@ import com.sap.sailing.domain.base.Bearing; * */ public class BearingCluster extends GenericBearingCluster { - @Override + protected BearingCluster[] createBearingClusterArraySizeTwo() { + return new BearingCluster[2]; + } + + @Override + protected BearingCluster createEmptyCluster() { + return new BearingCluster(); + } + public BearingCluster[] splitInTwo(double minimumDegreeDifferenceBetweenTacks) { return (BearingCluster[]) super.splitInTwo(minimumDegreeDifferenceBetweenTacks); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java index f98ea7ac8e9..5b94e601152 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java @@ -30,6 +30,16 @@ public class BearingWithConfidenceCluster extends GenericBearingCluster { * bearing to which it's closer. */ public GenericBearingCluster[] splitInTwo(double minimumDegreeDifferenceBetweenTacks) { - @SuppressWarnings("unchecked") - GenericBearingCluster[] result = (GenericBearingCluster[]) new GenericBearingCluster[2]; - result[0] = new GenericBearingCluster(); - result[1] = new GenericBearingCluster(); + GenericBearingCluster[] result = createBearingClusterArraySizeTwo(); + result[0] = createEmptyCluster(); + result[1] = createEmptyCluster(); if (bearings.size() >= 2) { Pair extremeBearings = getExtremeBearings(minimumDegreeDifferenceBetweenTacks); if (extremeBearings != null) { @@ -77,6 +76,15 @@ public class GenericBearingCluster { } return result; } + + protected GenericBearingCluster createEmptyCluster() { + return new GenericBearingCluster(); + } + + @SuppressWarnings("unchecked") + protected GenericBearingCluster[] createBearingClusterArraySizeTwo() { + return (GenericBearingCluster[]) new GenericBearingCluster[2]; + } private Pair getExtremeBearings(double minimumDegreeDifferenceBetweenTacks) { assert bearings.size() >= 2; From b5c493b92964baee3a0f808199e9fe5101a23468 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Thu, 12 Jan 2012 19:40:05 +0100 Subject: [PATCH 14/33] more ideas for confidence-based speeds and bearings; probably another re-design will be required in order to extract confidence as an attachment/tag which can non-invasively attached to any kind of object. This wouldn't require any changes to the existing speed/distance/bearing objects --- .../base/SpeedWithBearingWithConfidence.java | 8 +++ .../domain/base/SpeedWithConfidence.java | 6 ++ .../impl/AbstractSpeedWithBearingImpl.java | 66 ++++++++++++++++++- .../impl/AbstractSpeedWithConfidence.java | 43 ++++++++++++ .../base/impl/KilometersPerHourSpeedImpl.java | 22 ++++++- .../domain/base/impl/KnotSpeedImpl.java | 22 ++++++- .../base/impl/KnotSpeedWithBearingImpl.java | 12 ++++ .../SailingServer (Proxy).launch | 2 +- 8 files changed, 175 insertions(+), 6 deletions(-) create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithBearingWithConfidence.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithConfidence.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithConfidence.java diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithBearingWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithBearingWithConfidence.java new file mode 100755 index 00000000000..1750e4c7180 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithBearingWithConfidence.java @@ -0,0 +1,8 @@ +package com.sap.sailing.domain.base; + +import com.sap.sailing.domain.common.Util.Triple; +import com.sap.sailing.domain.confidence.HasConfidence; + +public interface SpeedWithBearingWithConfidence extends SpeedWithBearing, + HasConfidence, SpeedWithBearingWithConfidence> { +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithConfidence.java new file mode 100755 index 00000000000..c4cd41ba4b8 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithConfidence.java @@ -0,0 +1,6 @@ +package com.sap.sailing.domain.base; + +import com.sap.sailing.domain.confidence.HasConfidence; + +public interface SpeedWithConfidence extends Speed, HasConfidence { +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithBearingImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithBearingImpl.java index f58376ae6e9..733a843e035 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithBearingImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithBearingImpl.java @@ -1,12 +1,17 @@ package com.sap.sailing.domain.base.impl; import com.sap.sailing.domain.base.Bearing; +import com.sap.sailing.domain.base.BearingWithConfidence; import com.sap.sailing.domain.base.CourseChange; import com.sap.sailing.domain.base.Position; import com.sap.sailing.domain.base.SpeedWithBearing; +import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; +import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.TimePoint; +import com.sap.sailing.domain.common.Util.Triple; +import com.sap.sailing.domain.confidence.ScalableValue; -public abstract class AbstractSpeedWithBearingImpl extends AbstractSpeedImpl implements SpeedWithBearing { +public abstract class AbstractSpeedWithBearingImpl extends AbstractSpeedImpl implements SpeedWithBearingWithConfidence { private final Bearing bearing; protected AbstractSpeedWithBearingImpl(Bearing bearing) { @@ -72,4 +77,63 @@ public abstract class AbstractSpeedWithBearingImpl extends AbstractSpeedImpl imp return new KnotSpeedWithBearingImpl(newSpeedInKnots, newBearing); } + @Override + public double getConfidence() { + return AbstractSpeedWithConfidence.DEFAULT_SPEED_CONFIDENCE; + } + + /** + * The scalable value used for averaging confidence-based objects of this type is a triple whose first component + * holds the speed with a confidence while the second and third element are the sine and cosine values of the bearing's + * angle. + */ + @Override + public ScalableValue, SpeedWithBearingWithConfidence> getScalableValue() { + return new ScalableSpeedWithBearing(new KnotSpeedImpl(getKnots()), Math.sin(getBearing().getRadians()), Math.cos(getBearing().getRadians())); + } + + private static class ScalableSpeedWithBearing implements ScalableValue, SpeedWithBearingWithConfidence> { + private final SpeedWithConfidence speedWithConfidence; + private final double sin; + private final double cos; + + public ScalableSpeedWithBearing(SpeedWithConfidence speedWithConfidence, double sin, double cos) { + this.speedWithConfidence = speedWithConfidence; + this.sin = sin; + this.cos = cos; + } + + @Override + public ScalableValue, SpeedWithBearingWithConfidence> multiply( + double factor) { + SpeedWithConfidence newSpeedWithConfidence = speedWithConfidence.getScalableValue().multiply(factor).getValue(); + return new ScalableSpeedWithBearing(newSpeedWithConfidence, factor*sin, factor*cos); + } + + @Override + public ScalableValue, SpeedWithBearingWithConfidence> add( + ScalableValue, SpeedWithBearingWithConfidence> t) { + SpeedWithConfidence newSpeedWithConfidence = speedWithConfidence.getScalableValue().add(t.getValue().getA().getScalableValue()).getValue(); + return new ScalableSpeedWithBearing(newSpeedWithConfidence, sin+t.getValue().getB(), cos+t.getValue().getC()); + } + + @Override + public SpeedWithBearingWithConfidence divide(double divisor) { + SpeedWithConfidence newSpeedWithConfidence = speedWithConfidence.getScalableValue().divide(divisor); + double angle; + if (cos == 0) { + angle = sin >= 0 ? Math.PI / 2 : -Math.PI / 2; + } else { + angle = Math.atan2(sin, cos); + } + BearingWithConfidence bearing = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); + return new KnotSpeedWithBearingImpl(newSpeedWithConfidence.getKnots(), bearing); + } + + @Override + public Triple getValue() { + return new Triple(speedWithConfidence, sin, cos); + } + + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithConfidence.java new file mode 100755 index 00000000000..39f55220d08 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithConfidence.java @@ -0,0 +1,43 @@ +package com.sap.sailing.domain.base.impl; + +import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.confidence.ScalableValue; + +public abstract class AbstractSpeedWithConfidence extends AbstractSpeedImpl implements SpeedWithConfidence, + ScalableValue { + protected static final double DEFAULT_SPEED_CONFIDENCE = 0.9; + + protected abstract AbstractSpeedWithConfidence createInstanceOfSameType(double metersPerSecond, double confidence); + + @Override + public double getConfidence() { + return DEFAULT_SPEED_CONFIDENCE; + } + + @Override + public ScalableValue multiply(double factor) { + return createInstanceOfSameType(factor*getMetersPerSecond(), getConfidence()); + } + + @Override + public AbstractSpeedWithConfidence add(ScalableValue t) { + return createInstanceOfSameType(getMetersPerSecond() + t.getValue().getMetersPerSecond(), (getConfidence() + t + .getValue().getConfidence()) / 2); + } + + @Override + public SpeedWithConfidence divide(double divisor) { + return createInstanceOfSameType(getMetersPerSecond()/divisor, getConfidence()); + } + + @Override + public SpeedWithConfidence getValue() { + return this; + } + + @Override + public ScalableValue getScalableValue() { + return this; + } + +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KilometersPerHourSpeedImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KilometersPerHourSpeedImpl.java index a7ea445b6b7..a10541b2d1a 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KilometersPerHourSpeedImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KilometersPerHourSpeedImpl.java @@ -1,13 +1,26 @@ package com.sap.sailing.domain.base.impl; import com.sap.sailing.domain.base.Mile; -import com.sap.sailing.domain.base.Speed; +import com.sap.sailing.domain.base.SpeedWithConfidence; -public class KilometersPerHourSpeedImpl extends AbstractSpeedImpl implements Speed { +public class KilometersPerHourSpeedImpl extends AbstractSpeedWithConfidence implements SpeedWithConfidence { private final double speedInKilometersPerHour; + + private final double confidence; public KilometersPerHourSpeedImpl(double speedInKilometersPerHour) { this.speedInKilometersPerHour = speedInKilometersPerHour; + this.confidence = DEFAULT_SPEED_CONFIDENCE; + } + + private KilometersPerHourSpeedImpl(double speedInKilometersPerHour, double confidence) { + this.speedInKilometersPerHour = speedInKilometersPerHour; + this.confidence = confidence; + } + + @Override + public double getConfidence() { + return confidence; } @Override @@ -25,4 +38,9 @@ public class KilometersPerHourSpeedImpl extends AbstractSpeedImpl implements Spe return speedInKilometersPerHour; } + @Override + protected KilometersPerHourSpeedImpl createInstanceOfSameType(double metersPerSecond, double confidence) { + return new KilometersPerHourSpeedImpl(metersPerSecond/3.6, confidence); + } + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedImpl.java index b6520baeb50..ace4ef1b439 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedImpl.java @@ -1,16 +1,34 @@ package com.sap.sailing.domain.base.impl; -import com.sap.sailing.domain.base.Speed; +import com.sap.sailing.domain.base.Mile; +import com.sap.sailing.domain.base.SpeedWithConfidence; -public class KnotSpeedImpl extends AbstractSpeedImpl implements Speed { +public class KnotSpeedImpl extends AbstractSpeedWithConfidence implements SpeedWithConfidence { private final double knots; + private final double confidence; + public KnotSpeedImpl(double knots) { + this(knots, DEFAULT_SPEED_CONFIDENCE); + } + + private KnotSpeedImpl(double knots, double confidence) { this.knots = knots; + this.confidence = confidence; + } + + @Override + public double getConfidence() { + return confidence; } @Override public double getKnots() { return knots; } + + @Override + protected KnotSpeedImpl createInstanceOfSameType(double metersPerSecond, double confidence) { + return new KnotSpeedImpl(getKnots() * Mile.METERS_PER_SEA_MILE / 3600, confidence); + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedWithBearingImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedWithBearingImpl.java index 5fcd5a55b43..52fbdc86c60 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedWithBearingImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedWithBearingImpl.java @@ -5,9 +5,21 @@ import com.sap.sailing.domain.base.Bearing; public class KnotSpeedWithBearingImpl extends AbstractSpeedWithBearingImpl { private final double speedInKnots; + private final double confidence; + public KnotSpeedWithBearingImpl(double speedInKnots, Bearing bearing) { + this(speedInKnots, bearing, AbstractSpeedWithConfidence.DEFAULT_SPEED_CONFIDENCE); + } + + public KnotSpeedWithBearingImpl(double speedInKnots, Bearing bearing, double confidence) { super(bearing); this.speedInKnots = speedInKnots; + this.confidence = confidence; + } + + @Override + public double getConfidence() { + return confidence; } @Override diff --git a/java/com.sap.sailing.server/SailingServer (Proxy).launch b/java/com.sap.sailing.server/SailingServer (Proxy).launch index 9c3788cbba0..95312da5daf 100755 --- a/java/com.sap.sailing.server/SailingServer (Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (Proxy).launch @@ -17,7 +17,7 @@ - + From 5ede3343f221162496988c3625075583d27d4b06 Mon Sep 17 00:00:00 2001 From: fmittag Date: Fri, 13 Jan 2012 10:42:46 +0100 Subject: [PATCH 15/33] refactoring of RaceMap and adding of CompetitorSelectionChangeListener --- .../ui/adminconsole/CompetitorsDisplayer.java | 9 + ...geRotator.java => ImageTransformator.java} | 172 ++--- .../QuickRanksListBoxComposite.java | 133 ++++ .../sailing/gwt/ui/adminconsole/RaceMap.java | 671 +++++++++++------- .../gwt/ui/adminconsole/RaceMapData.java | 197 ----- .../gwt/ui/adminconsole/RaceMapPanel.java | 244 ++----- .../gwt/ui/adminconsole/RaceMapResources.java | 204 ++++++ .../gwt/ui/adminconsole/RaceMapSettings.java | 105 +++ .../CompetitorSelectionChangeListener.java | 19 + .../client/CompetitorSelectionProvider.java | 17 + 10 files changed, 1042 insertions(+), 729 deletions(-) create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorsDisplayer.java rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/{ImageRotator.java => ImageTransformator.java} (60%) mode change 100755 => 100644 create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/QuickRanksListBoxComposite.java delete mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapData.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapResources.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapSettings.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionChangeListener.java create mode 100644 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionProvider.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorsDisplayer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorsDisplayer.java new file mode 100644 index 00000000000..f0c44d99453 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CompetitorsDisplayer.java @@ -0,0 +1,9 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.List; + +import com.sap.sailing.gwt.ui.shared.CompetitorDAO; + +public interface CompetitorsDisplayer { + void fillCompetitors(List competitors); +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageTransformator.java old mode 100755 new mode 100644 similarity index 60% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageTransformator.java index 38316668348..71483654f59 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageRotator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageTransformator.java @@ -1,81 +1,91 @@ -package com.sap.sailing.gwt.ui.adminconsole; - -import com.google.gwt.canvas.client.Canvas; -import com.google.gwt.canvas.dom.client.Context2d; -import com.google.gwt.dom.client.ImageElement; -import com.google.gwt.event.dom.client.LoadEvent; -import com.google.gwt.event.dom.client.LoadHandler; -import com.google.gwt.maps.client.geom.Point; -import com.google.gwt.resources.client.ImageResource; -import com.google.gwt.user.client.ui.Image; - -/** - * Loads an image from a URL, draws it to a {@link Canvas}, rotates it by a specified angle and allows clients - * to obtain a URL for the rotated image contents to be used in other contexts requiring an image URL. - * - * @author Axel Uhl (d043530) - * - */ -public class ImageRotator { - private final String unrotatedImageURL; - private final Canvas canvas; - private final int imageWidth; - private final int imageHeight; - private final int canvasRadius; - - private ImageElement imageElement; - - private final Context2d context; - - public ImageRotator(ImageResource unrotatedImage) { - this.unrotatedImageURL = unrotatedImage.getSafeUri().asString(); - canvas = Canvas.createIfSupported(); - imageWidth = unrotatedImage.getWidth(); - imageHeight = unrotatedImage.getHeight(); - canvasRadius = (int) Math.sqrt(imageWidth*imageWidth/4 + imageHeight*imageHeight/4); - canvas.setSize(""+2*canvasRadius+"px", ""+2*canvasRadius+"px"); - canvas.setCoordinateSpaceWidth(2*canvasRadius); - canvas.setCoordinateSpaceHeight(2*canvasRadius); - context = canvas.getContext2d(); - final Image image = new Image(unrotatedImageURL.toString()); - imageElement = (ImageElement) image.getElement().cast(); - if (imageElement == null) { - image.addLoadHandler(new LoadHandler() { - @Override - public void onLoad(LoadEvent event) { - imageElement = (ImageElement) image.getElement().cast(); - } - }); - } - } - - public Point getAnchor() { - return Point.newInstance(canvasRadius, canvasRadius); - } - - private String getUnrotatedImageURL() { - return unrotatedImageURL; - } - - /** - * If the platform supports the {@link Canvas} element, returns a view rotated by degrees degrees. Otherwise, - * the {@link #getUnrotatedImageURL() unrotated image's URL} is returned. - */ - public String getRotatedImageURL(double angleInDegrees, double scaleFactor) { - String result = getUnrotatedImageURL(); - if (canvas != null) { - if (imageElement != null) { - double angleInRadians = angleInDegrees/180.*Math.PI; - context.clearRect(0, 0, 2*canvasRadius, 2*canvasRadius); - context.save(); - context.translate(canvasRadius, canvasRadius); - context.rotate(angleInRadians); - context.scale(scaleFactor, scaleFactor); - context.drawImage(imageElement, (-imageWidth/2) * scaleFactor, (-imageHeight/2) * scaleFactor); - result = canvas.toDataUrl("image/png"); - context.restore(); - } - } - return result; - } -} +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.canvas.client.Canvas; +import com.google.gwt.canvas.dom.client.Context2d; +import com.google.gwt.dom.client.ImageElement; +import com.google.gwt.event.dom.client.LoadEvent; +import com.google.gwt.event.dom.client.LoadHandler; +import com.google.gwt.maps.client.geom.Point; +import com.google.gwt.resources.client.ImageResource; +import com.google.gwt.user.client.ui.Image; + +/** + * Loads an image from a URL, draws it to a {@link Canvas}, rotates (specified angle) and scales it and allows clients + * to obtain a URL for the rotated image contents to be used in other contexts requiring an image URL. + * + * @author Axel Uhl (d043530) + * + */ +public class ImageTransformator { + private final String untransformedImageURL; + private final Canvas canvas; + private final int imageWidth; + private final int imageHeight; + private int canvasRadius; + private int canvasWidth; + private int canvasHeight; + + private ImageElement imageElement; + + private Context2d context; + + public ImageTransformator(ImageResource untransformedImage) { + this.untransformedImageURL = untransformedImage.getSafeUri().asString(); + canvas = Canvas.createIfSupported(); + imageWidth = untransformedImage.getWidth(); + imageHeight = untransformedImage.getHeight(); + calculateCanvasSize(1.0); + final Image image = new Image(untransformedImageURL.toString()); + imageElement = (ImageElement) image.getElement().cast(); + if (imageElement == null) { + image.addLoadHandler(new LoadHandler() { + @Override + public void onLoad(LoadEvent event) { + imageElement = (ImageElement) image.getElement().cast(); + } + }); + } + } + + private void calculateCanvasSize(double scaleFactor) + { + canvasWidth = (int) Math.round(imageWidth * scaleFactor); + canvasHeight = (int) Math.round(imageHeight * scaleFactor); + canvasRadius = (int) Math.sqrt(canvasWidth*canvasWidth/4 + canvasHeight*canvasHeight/4); + canvas.setSize(""+2*canvasRadius+"px", ""+2*canvasRadius+"px"); + canvas.setCoordinateSpaceWidth(2*canvasRadius); + canvas.setCoordinateSpaceHeight(2*canvasRadius); + context = canvas.getContext2d(); + } + + public Point getAnchor() { + return Point.newInstance(canvasRadius, canvasRadius); + } + + private String getUntransformedImageURL() { + return untransformedImageURL; + } + + /** + * If the platform supports the {@link Canvas} element, returns a view rotated by degrees degrees. Otherwise, + * the {@link #getUntransformedImageURL() untransformed image's URL} is returned. + */ + public String getTransformedImageURL(double angleInDegrees, double scaleFactor) { + String result = getUntransformedImageURL(); + if (canvas != null) { + if (imageElement != null) { + double angleInRadians = angleInDegrees/180.*Math.PI; + calculateCanvasSize(scaleFactor); + context.clearRect(0, 0, 2*canvasRadius, 2*canvasRadius); + context.save(); + context.translate(canvasRadius, canvasRadius); + context.rotate(angleInRadians); + context.scale(scaleFactor, scaleFactor); + context.drawImage(imageElement, (-imageWidth/2), (-imageHeight/2)); + result = canvas.toDataUrl("image/png"); + context.restore(); + } + } + return result; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/QuickRanksListBoxComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/QuickRanksListBoxComposite.java new file mode 100644 index 00000000000..1995f84ad6b --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/QuickRanksListBoxComposite.java @@ -0,0 +1,133 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import java.util.ArrayList; +import java.util.Collections; +import java.util.Comparator; +import java.util.HashSet; +import java.util.List; +import java.util.Set; + +import com.google.gwt.event.dom.client.ChangeEvent; +import com.google.gwt.event.dom.client.ChangeHandler; +import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.dom.client.ClickHandler; +import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.ListBox; +import com.google.gwt.user.client.ui.VerticalPanel; +import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; +import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; +import com.sap.sailing.gwt.ui.shared.CompetitorDAO; +import com.sap.sailing.gwt.ui.shared.QuickRankDAO; + +public class QuickRanksListBoxComposite extends Composite implements CompetitorSelectionProvider { + private final Set competitorSelectionChangeListeners; + private final List quickRankList; + private final List competitorList; + private final ListBox quickRankListBox; + + public QuickRanksListBoxComposite(boolean hasMultiSelection) { + this.competitorSelectionChangeListeners = new HashSet(); + quickRankListBox = new ListBox(hasMultiSelection); + quickRankListBox.addChangeHandler(new ChangeHandler() { + @Override + public void onChange(ChangeEvent event) { + fireCompetitorSelectionChanged(Collections.singletonList(getSelectedCompetitor())); + } + }); + quickRankListBox.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + CompetitorDAO selectedCompetitor = getSelectedCompetitor(); + List selectedCompetitorsCollection = Collections.emptyList(); + if (selectedCompetitor != null) { + Collections.singletonList(selectedCompetitor); + } + fireCompetitorSelectionChanged(selectedCompetitorsCollection); + } + }); + quickRankList = new ArrayList(); + competitorList = new ArrayList(); + VerticalPanel vp = new VerticalPanel(); + vp.add(quickRankListBox); + + // All composites must call initWidget() in their constructors. + initWidget(vp); + } + + public void fillQuickRanks(List quickRanks, boolean isSorted) { + quickRankList.clear(); + competitorList.clear(); + quickRankListBox.clear(); + for (QuickRankDAO quickRank: quickRanks) { + quickRankList.add(quickRank); + competitorList.add(quickRank.competitor); + } + if(!isSorted) { + Collections.sort(quickRankList, new Comparator() { + @Override + public int compare(QuickRankDAO o1, QuickRankDAO o2) { + Integer rank1 = o1.rank; + Integer rank2 = o2.rank; + return rank1.compareTo(rank2); + } + + }); + } + for (QuickRankDAO c : quickRankList) { + quickRankListBox.addItem(toString(c)); + } + fireCompetitorSelectionChanged(getSelectedCompetitors()); + } + + public List getCompetitors() { + return competitorList; + } + + private CompetitorDAO getSelectedCompetitor() { + int i = quickRankListBox.getSelectedIndex(); + CompetitorDAO result = null; + if (i >= 0) { + result = quickRankList.get(i).competitor; + } + return result; + } + + @Override + public List getSelectedCompetitors() { + int i=0; + List result = new ArrayList(); + for (QuickRankDAO quickRank: quickRankList) { + if (quickRankListBox.isItemSelected(i)) { + result.add(quickRank.competitor); + } + i++; + } + return result; + } + + @Override + public void addCompetitorSelectionChangeListener(CompetitorSelectionChangeListener listener) { + competitorSelectionChangeListeners.add(listener); + } + + @Override + public void removeCompetitorSelectionChangeListener(CompetitorSelectionChangeListener listener) { + competitorSelectionChangeListeners.remove(listener); + } + + private void fireCompetitorSelectionChanged(List selectedCompetitors) { + for (CompetitorSelectionChangeListener listener : competitorSelectionChangeListeners) { + listener.onCompetitorSelectionChange(selectedCompetitors); + } + } + + private String toString(QuickRankDAO quickRank) { + return quickRank.rank + ". " + quickRank.competitor.name + " (" + + quickRank.competitor.threeLetterIocCountryCode + ") in leg #" + (quickRank.legNumber + 1); + } + + public ListBox getListBox() { + return quickRankListBox; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java index 76ec6274cfd..d9d5adb5123 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java @@ -7,12 +7,11 @@ import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.List; +import java.util.ListIterator; import java.util.Map; import java.util.Set; -import com.google.gwt.core.client.GWT; import com.google.gwt.maps.client.InfoWindowContent; -import com.google.gwt.maps.client.MapType; import com.google.gwt.maps.client.MapWidget; import com.google.gwt.maps.client.Maps; import com.google.gwt.maps.client.control.LargeMapControl3D; @@ -30,7 +29,6 @@ import com.google.gwt.maps.client.event.PolylineMouseOverHandler; import com.google.gwt.maps.client.geom.LatLng; import com.google.gwt.maps.client.geom.LatLngBounds; import com.google.gwt.maps.client.geom.Point; -import com.google.gwt.maps.client.geom.Projection; import com.google.gwt.maps.client.overlay.Icon; import com.google.gwt.maps.client.overlay.Marker; import com.google.gwt.maps.client.overlay.MarkerOptions; @@ -41,11 +39,12 @@ import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.domain.common.LegType; -import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.Util.Triple; +import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; import com.sap.sailing.gwt.ui.client.ErrorReporter; +import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.TimeListener; import com.sap.sailing.gwt.ui.client.Timer; import com.sap.sailing.gwt.ui.shared.CompetitorDAO; import com.sap.sailing.gwt.ui.shared.EventDAO; @@ -56,116 +55,134 @@ import com.sap.sailing.gwt.ui.shared.RaceDAO; import com.sap.sailing.gwt.ui.shared.RegattaDAO; import com.sap.sailing.server.api.EventNameAndRaceName; -public class RaceMap { +public class RaceMap implements TimeListener, CompetitorSelectionChangeListener, RaceSelectionChangeListener, CompetitorsDisplayer { protected MapWidget map; private final SailingServiceAsync sailingService; private final ErrorReporter errorReporter; /** - * Two sails on downwind leg, wind from port (sails on starboard); no highlighting + * Tails of competitors currently displayed as overlays on the map. */ - protected ImageRotator boatIconDownwindPortRotator; + private final Map tails; /** - * Two sails on downwind leg, wind from port (sails on starboard); with highlighting + * Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the first fix shown + * in {@link #tails} is . */ - protected ImageRotator boatIconHighlightedDownwindPortRotator; + private final Map firstShownFix; /** - * Two sails on downwind leg, wind from starboard (sails on port); no highlighting + * Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the last fix shown in + * {@link #tails} is . */ - protected ImageRotator boatIconDownwindStarboardRotator; + private final Map lastShownFix; /** - * Two sails on downwind leg, wind from starboard (sails on port); with highlighting + * Fixes of each competitors tail. If a list is contained for a competitor, the list contains a timely "contiguous" + * list of fixes for the competitor. This means the server has no more data for the time interval covered, unless + * the last fix was {@link GPSFixDAO#extrapolated obtained by extrapolation}. */ - protected ImageRotator boatIconHighlightedDownwindStarboardRotator; + private final Map> fixes; /** - * One sail, wind from port (sails on starboard); no highlighting + * Markers used as boat display on the map */ - protected ImageRotator boatIconPortRotator; + protected final Map boatMarkers; + + private final Map buoyMarkers; /** - * One sail, wind from port (sails on starboard); with highlighting + * markers displayed in response to + * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} */ - protected ImageRotator boatIconHighlightedPortRotator; + protected Set douglasMarkers; /** - * One sail, wind from starboard (sails on port); no highlighting + * markers displayed in response to + * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} */ - protected ImageRotator boatIconStarboardRotator; + protected Set maneuverMarkers; - /** - * One sail, wind from starboard (sails on port); with highlighting - */ - protected ImageRotator boatIconHighlightedStarboardRotator; + protected Map> lastManeuverResult; - protected Icon buoyIcon; - protected Icon tackToStarboardIcon; - protected Icon tackToPortIcon; - protected Icon jibeToStarboardIcon; - protected Icon jibeToPortIcon; - protected Icon markPassingToStarboardIcon; - protected Icon markPassingToPortIcon; - protected Icon headUpOnStarboardIcon; - protected Icon headUpOnPortIcon; - protected Icon bearAwayOnStarboardIcon; - protected Icon bearAwayOnPortIcon; - protected Icon unknownManeuverIcon; - protected Icon penaltyCircleToStarboardIcon; - protected Icon penaltyCircleToPortIcon; - protected LatLng lastMousePosition; + protected Map> lastDouglasPeuckerResult; + + private LatLng lastMousePosition; - protected long tailLengthInMilliSeconds = 30000l; + private List allCompetitors; - protected List> selectedEventAndRace; + private Set selectedMapCompetitors; + + private List> selectedEventAndRace; /** * If the user explicitly zoomed or panned the map, don't adjust zoom/pan unless a new race is selected */ - protected boolean mapZoomedOrPannedSinceLastRaceSelectionChange = false; + private boolean mapZoomedOrPannedSinceLastRaceSelectionChange = false; /** * Used to check if the first initial zoom to the buoy markers was already done. */ - protected boolean mapFirstZoomDone = false; - + private boolean mapFirstZoomDone = false; // key for domain web4sap.com - protected final String mapsAPIKey = "ABQIAAAAmvjPh3ZpHbnwuX3a66lDqRRLCigyC_gRDASMpyomD2do5awpNhRCyD_q-27hwxKe_T6ivSZ_0NgbUg"; + private final String mapsAPIKey = "ABQIAAAAmvjPh3ZpHbnwuX3a66lDqRRLCigyC_gRDASMpyomD2do5awpNhRCyD_q-27hwxKe_T6ivSZ_0NgbUg"; - protected static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class); - - protected final RaceMapData data; - private final Timer timer; - protected boolean showDouglasPeuckerPoints = false; + /** + * RPC calls may receive responses out of order if there are multiple calls in-flight at the same time. If the time + * slider is moved quickly it generates many requests for boat positions quickly after each other. Sometimes, + * responses for requests send later may return before the responses to all earlier requests have been received and + * processed. This counter is used to number the requests. When processing of a response for a later request has + * already begun, responses to earlier requests will be ignored. + */ + private int boatPositionRequestIDCounter; - protected boolean showManeuverHeadUp = false; + /** + * Corresponds to {@link #boatPositionRequestIDCounter}. As soon as the processing of a response for a request ID + * begins, this attribute is set to the ID. A response won't be processed if a later response is already being + * processed. + */ + private int startedProcessingRequestID; - protected boolean showManeuverBearAway = false; + private RaceMapResources imageResources; - protected boolean showManeuverTack = false; - - protected boolean showManeuverJibe = false; - - protected boolean showManeuverPenaltyCircle = false; - - protected boolean showManeuverMarkPassing = false; - - protected boolean showManeuverOther = false; + private RaceMapSettings settings; public RaceMap(SailingServiceAsync sailingService, ErrorReporter errorReporter, Timer timer) { this.sailingService = sailingService; this.errorReporter = errorReporter; this.timer = timer; - data = new RaceMapData(); + tails = new HashMap(); + firstShownFix = new HashMap(); + lastShownFix = new HashMap(); + buoyMarkers = new HashMap(); + boatMarkers = new HashMap(); + fixes = new HashMap>(); + + selectedMapCompetitors = new HashSet(); + allCompetitors = new ArrayList(); + + settings = new RaceMapSettings(); } + public double calculateDistance(double lat1, double lng1, double lat2, double lng2) { + // the calculation is based on the Haversine formula + double earthRadius = 6371; // in km + double dLat = Math.toRadians(lat2-lat1); + double dLng = Math.toRadians(lng2-lng1); + double a = Math.sin(dLat/2) * Math.sin(dLat/2) + + Math.cos(Math.toRadians(lat1)) * Math.cos(Math.toRadians(lat2)) * + Math.sin(dLng/2) * Math.sin(dLng/2); + double c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a)); + double dist = earthRadius * c; + + return dist; + } + public void loadMapsAPI(final Grid grid, final int gridRow, final int gridColumn) { Maps.loadMapsApi(mapsAPIKey, "2", false, new Runnable() { public void run() { @@ -178,11 +195,10 @@ public class RaceMap { map.setScrollWheelZoomEnabled(true); map.setContinuousZoom(true); -// MapType currentMapType = map.getCurrentMapType(); -// Projection projection = currentMapType.getProjection(); - grid.setWidget(gridRow, gridColumn, map); + imageResources = new RaceMapResources(map); + map.addMapZoomEndHandler(new MapZoomEndHandler() { @Override public void onZoomEnd(MapZoomEndEvent event) { @@ -201,63 +217,112 @@ public class RaceMap { lastMousePosition = event.getLatLng(); } }); - boatIconDownwindPortRotator = new ImageRotator(resources.lowlightedBoatIconDW_Port()); - boatIconHighlightedDownwindPortRotator = new ImageRotator(resources.highlightedBoatIconDW_Port()); - boatIconDownwindStarboardRotator = new ImageRotator(resources.lowlightedBoatIconDW_Starboard()); - boatIconHighlightedDownwindStarboardRotator = new ImageRotator(resources - .highlightedBoatIconDW_Starboard()); - boatIconPortRotator = new ImageRotator(resources.lowlightedBoatIcon_Port()); - boatIconHighlightedPortRotator = new ImageRotator(resources.highlightedBoatIcon_Port()); - boatIconStarboardRotator = new ImageRotator(resources.lowlightedBoatIcon_Starboard()); - boatIconHighlightedStarboardRotator = new ImageRotator(resources.highlightedBoatIcon_Starboard()); - buoyIcon = Icon.newInstance(resources.buoyIcon().getSafeUri().asString()); - buoyIcon.setIconAnchor(Point.newInstance(4, 4)); - tackToStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=T|00FF00|000000"); - tackToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - tackToPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=T|FF0000|000000"); - tackToPortIcon.setIconAnchor(Point.newInstance(10, 33)); - jibeToStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=J|00FF00|000000"); - jibeToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - jibeToPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=J|FF0000|000000"); - jibeToPortIcon.setIconAnchor(Point.newInstance(10, 33)); - headUpOnStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=H|00FF00|000000"); - headUpOnStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - headUpOnPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=H|FF0000|000000"); - headUpOnPortIcon.setIconAnchor(Point.newInstance(10, 33)); - bearAwayOnStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=B|00FF00|000000"); - bearAwayOnStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - bearAwayOnPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=B|FF0000|000000"); - bearAwayOnPortIcon.setIconAnchor(Point.newInstance(10, 33)); - markPassingToStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=M|00FF00|000000"); - markPassingToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - markPassingToPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=M|FF0000|000000"); - markPassingToPortIcon.setIconAnchor(Point.newInstance(10, 33)); - unknownManeuverIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=?|FFFFFF|000000"); - unknownManeuverIcon.setIconAnchor(Point.newInstance(10, 33)); - penaltyCircleToStarboardIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=P|00FF00|000000"); - penaltyCircleToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); - penaltyCircleToPortIcon = Icon - .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=P|FF0000|000000"); - penaltyCircleToPortIcon.setIconAnchor(Point.newInstance(10, 33)); } }); } + public void redraw() { + timeChanged(timer.getTime()); + } + + @Override + public void onRaceSelectionChange(List> selectedRaces) { + mapFirstZoomDone = false; + mapZoomedOrPannedSinceLastRaceSelectionChange = false; + + this.selectedEventAndRace = selectedRaces; + } + + @Override + public void onCompetitorSelectionChange(List newSelectedCompetitors) { + + for (CompetitorDAO competitorDAO : newSelectedCompetitors) { + + if (selectedMapCompetitors.contains(competitorDAO)) { + // "lowlight" currently selected competitor + Marker highlightedMarker = boatMarkers.get(competitorDAO); + if (highlightedMarker != null) { + Marker lowlightedMarker = createBoatMarker(competitorDAO, false); + map.removeOverlay(highlightedMarker); + map.addOverlay(lowlightedMarker); + boatMarkers.put(competitorDAO, lowlightedMarker); + selectedMapCompetitors.remove(competitorDAO); + } + } else { + Marker lowlightedMarker = boatMarkers.get(competitorDAO); + if (lowlightedMarker != null) { + Marker highlightedMarker = createBoatMarker(competitorDAO, true); + map.removeOverlay(lowlightedMarker); + map.addOverlay(highlightedMarker); + boatMarkers.put(competitorDAO, highlightedMarker); + } + // add the competitor even if not currently contained in map + selectedMapCompetitors.add(competitorDAO); + } + } + } + + @Override + public void timeChanged(final Date date) { + if (date != null) { + if (selectedEventAndRace != null && !selectedEventAndRace.isEmpty()) { + EventDAO event = selectedEventAndRace.get(selectedEventAndRace.size() - 1).getA(); + RaceDAO race = selectedEventAndRace.get(selectedEventAndRace.size() - 1).getC(); + if (event != null && race != null) { + final Triple, Map, Map> fromAndToAndOverlap = + computeFromAndTo(date, getCompetitorsToShow()); + final int requestID = boatPositionRequestIDCounter++; + sailingService.getBoatPositions(new EventNameAndRaceName(event.name, race.name), + fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), true, + new AsyncCallback>>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error obtaining boat positions: " + caught.getMessage()); + } + + @Override + public void onSuccess(Map> result) { + // process response only if not received out of order + if (startedProcessingRequestID < requestID) { + startedProcessingRequestID = requestID; + Date from = new Date(date.getTime() - settings.getTailLengthInMilliSeconds()); + updateFixes(result, fromAndToAndOverlap.getC()); + showBoatsOnMap(from, date, getCompetitorsToShow()); + if (douglasMarkers != null) { + removeAllMarkDouglasPeuckerpoints(); + } + if (maneuverMarkers != null) { + removeAllManeuverMarkers(); + } + } + } + }); + sailingService.getMarkPositions(new EventNameAndRaceName(event.name, race.name), date, + new AsyncCallback>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error trying to obtain mark positions: " + + caught.getMessage()); + } + + @Override + public void onSuccess(List result) { + showMarksOnMap(result); + } + }); + } + } + } + } + + public void fillCompetitors(List competitors) { + allCompetitors.clear(); + allCompetitors.addAll(competitors); + } + /** - * From {@link #data.fixes} as well as the selection of {@link #getCompetitorsToShow competitors to show}, computes the - * from/to times for which to request GPS raceMapData.fixes from the server. No update is performed here to {@link #data.fixes}. The + * From {@link #fixes} as well as the selection of {@link #getCompetitorsToShow competitors to show}, computes the + * from/to times for which to request GPS raceMapData.fixes from the server. No update is performed here to {@link #fixes}. The * result guarantees that, when used in * {@link SailingServiceAsync#getBoatPositions(String, String, Map, Map, boolean, AsyncCallback)}, for each * competitor from {@link #competitorsToShow} there are all raceMapData.fixes known by the server for that competitor starting @@ -270,19 +335,17 @@ public class RaceMap { */ protected Triple, Map, Map> computeFromAndTo( Date upTo, Collection competitorsToShow) { - Date tailstart = new Date(upTo.getTime() - tailLengthInMilliSeconds); + Date tailstart = new Date(upTo.getTime() - settings.getTailLengthInMilliSeconds()); Map from = new HashMap(); Map to = new HashMap(); Map overlapWithKnownFixes = new HashMap(); for (CompetitorDAO competitor : competitorsToShow) { - List fixesForCompetitor = data.fixes.get(competitor); + List fixesForCompetitor = fixes.get(competitor); Date fromDate; Date toDate; - Date timepointOfLastKnownFix = fixesForCompetitor == null ? null - : data.getTimepointOfLastNonExtrapolated(fixesForCompetitor); - Date timepointOfFirstKnownFix = fixesForCompetitor == null ? null - : data.getTimepointOfFirstNonExtrapolated(fixesForCompetitor); + Date timepointOfLastKnownFix = fixesForCompetitor == null ? null : getTimepointOfLastNonExtrapolated(fixesForCompetitor); + Date timepointOfFirstKnownFix = fixesForCompetitor == null ? null : getTimepointOfFirstNonExtrapolated(fixesForCompetitor); boolean overlap = false; if (fixesForCompetitor != null && timepointOfFirstKnownFix != null && !tailstart.before(timepointOfFirstKnownFix) && timepointOfLastKnownFix != null @@ -315,7 +378,7 @@ public class RaceMap { /** - * Adds the raceMapData.fixes received in result to {@link #data.fixes} and ensures they are still contiguous for each + * Adds the raceMapData.fixes received in result to {@link #fixes} and ensures they are still contiguous for each * competitor. If overlapsWithKnownraceMapData.fixes indicates that the raceMapData.fixes received in result * overlap with those already known, the raceMapData.fixes are merged into the list of already known raceMapData.fixes for the competitor. * Otherwise, the raceMapData.fixes received in result replace those known so far for the respective competitor. @@ -324,24 +387,24 @@ public class RaceMap { Map overlapsWithKnownFixes) { for (Map.Entry> e : result.entrySet()) { if (e.getValue() != null && !e.getValue().isEmpty()) { - List fixesForCompetitor = data.fixes.get(e.getKey()); + List fixesForCompetitor = fixes.get(e.getKey()); if (fixesForCompetitor == null) { fixesForCompetitor = new ArrayList(); - data.fixes.put(e.getKey(), fixesForCompetitor); + fixes.put(e.getKey(), fixesForCompetitor); } if (!overlapsWithKnownFixes.get(e.getKey())) { fixesForCompetitor.clear(); // to re-establish the invariants for raceMapData.tails, raceMapData.firstShownFix and raceMapData.lastShownFix, we now need to remove // all // points from the competitor's polyline and clear the entries in raceMapData.firstShownFix and raceMapData.lastShownFix - if (map != null && data.tails.containsKey(e.getKey())) { - map.removeOverlay(data.tails.remove(e.getKey())); + if (map != null && tails.containsKey(e.getKey())) { + map.removeOverlay(tails.remove(e.getKey())); } - data.firstShownFix.remove(e.getKey()); - data.lastShownFix.remove(e.getKey()); + firstShownFix.remove(e.getKey()); + lastShownFix.remove(e.getKey()); fixesForCompetitor.addAll(e.getValue()); } else { - data.mergeFixes(e.getKey(), e.getValue()); + mergeFixes(e.getKey(), e.getValue()); } } } @@ -349,12 +412,12 @@ public class RaceMap { protected void showMarksOnMap(List result) { if (map != null) { - Set toRemove = new HashSet(data.buoyMarkers.keySet()); + Set toRemove = new HashSet(buoyMarkers.keySet()); for (MarkDAO markDAO : result) { - Marker buoyMarker = data.buoyMarkers.get(markDAO); + Marker buoyMarker = buoyMarkers.get(markDAO); if (buoyMarker == null) { buoyMarker = createBuoyMarker(markDAO); - data.buoyMarkers.put(markDAO, buoyMarker); + buoyMarkers.put(markDAO, buoyMarker); map.addOverlay(buoyMarker); } else { buoyMarker.setLatLng(LatLng.newInstance(markDAO.position.latDeg, markDAO.position.lngDeg)); @@ -362,10 +425,10 @@ public class RaceMap { } } for (MarkDAO toRemoveMarkDAO : toRemove) { - Marker marker = data.buoyMarkers.remove(toRemoveMarkDAO); + Marker marker = buoyMarkers.remove(toRemoveMarkDAO); map.removeOverlay(marker); } - zoomMapFirstTimeToMarks(data.buoyMarkers.keySet()); + zoomMapFirstTimeToMarks(buoyMarkers.keySet()); } } @@ -409,25 +472,24 @@ public class RaceMap { /** * @param from - * time point for first fix to show in raceMapData.tails + * time point for first fix to show in tails * @param to - * time point for last fix to show in raceMapData.tails + * time point for last fix to show in tails */ - protected void showBoatsOnMap(Date from, Date to, Collection competitorsToShow, - Set competitorsSelectedInMap) { + protected void showBoatsOnMap(Date from, Date to, Collection competitorsToShow) { if (map != null) { LatLngBounds newMapBounds = null; - Set competitorDAOsOfUnusedTails = new HashSet(data.tails.keySet()); - Set competitorDAOsOfUnusedMarkers = new HashSet(data.boatMarkers.keySet()); + Set competitorDAOsOfUnusedTails = new HashSet(tails.keySet()); + Set competitorDAOsOfUnusedMarkers = new HashSet(boatMarkers.keySet()); for (CompetitorDAO competitorDAO : competitorsToShow) { - if (data.fixes.containsKey(competitorDAO)) { - Polyline tail = data.tails.get(competitorDAO); + if (fixes.containsKey(competitorDAO)) { + Polyline tail = tails.get(competitorDAO); if (tail == null) { tail = createTailAndUpdateIndices(competitorDAO, from, to); map.addOverlay(tail); } else { - data.updateTail(tail, competitorDAO, from, to); + updateTail(tail, competitorDAO, from, to); competitorDAOsOfUnusedTails.remove(competitorDAO); } LatLngBounds bounds = tail.getBounds(); @@ -437,31 +499,31 @@ public class RaceMap { newMapBounds.extend(bounds.getNorthEast()); newMapBounds.extend(bounds.getSouthWest()); } - if (data.lastShownFix.containsKey(competitorDAO) && data.lastShownFix.get(competitorDAO) != -1) { - GPSFixDAO lastPos = data.getBoatFix(competitorDAO); - Marker boatMarker = data.boatMarkers.get(competitorDAO); + if (lastShownFix.containsKey(competitorDAO) && lastShownFix.get(competitorDAO) != -1) { + GPSFixDAO lastPos = getBoatFix(competitorDAO); + Marker boatMarker = boatMarkers.get(competitorDAO); if (boatMarker == null) { boatMarker = createBoatMarker(competitorDAO, false); map.addOverlay(boatMarker); - data.boatMarkers.put(competitorDAO, boatMarker); + boatMarkers.put(competitorDAO, boatMarker); } else { competitorDAOsOfUnusedMarkers.remove(competitorDAO); // check if anchors match; re-use marker with setImage only if anchors match - Point newAnchor = getBoatImageRotator(lastPos, - competitorsSelectedInMap.contains(competitorDAO)).getAnchor(); + Point newAnchor = imageResources.getBoatImageTransformator(lastPos, + selectedMapCompetitors.contains(competitorDAO)).getAnchor(); Point oldAnchor = boatMarker.getIcon().getIconAnchor(); if (oldAnchor.getX() == newAnchor.getX() && oldAnchor.getY() == newAnchor.getY()) { boatMarker.setLatLng(LatLng.newInstance(lastPos.position.latDeg, lastPos.position.lngDeg)); - boatMarker.setImage(getBoatImageURL(lastPos, - competitorsSelectedInMap.contains(competitorDAO))); + boatMarker.setImage(imageResources.getBoatImageURL(lastPos, + selectedMapCompetitors.contains(competitorDAO))); } else { // anchors don't match; replace marker map.removeOverlay(boatMarker); boatMarker = createBoatMarker(competitorDAO, - competitorsSelectedInMap.contains(competitorDAO)); + selectedMapCompetitors.contains(competitorDAO)); map.addOverlay(boatMarker); - data.boatMarkers.put(competitorDAO, boatMarker); + boatMarkers.put(competitorDAO, boatMarker); } } } @@ -473,75 +535,18 @@ public class RaceMap { mapFirstZoomDone = true; } for (CompetitorDAO unusedMarkerCompetitorDAO : competitorDAOsOfUnusedMarkers) { - map.removeOverlay(data.boatMarkers.remove(unusedMarkerCompetitorDAO)); + map.removeOverlay(boatMarkers.remove(unusedMarkerCompetitorDAO)); } for (CompetitorDAO unusedTailCompetitorDAO : competitorDAOsOfUnusedTails) { - map.removeOverlay(data.tails.remove(unusedTailCompetitorDAO)); + map.removeOverlay(tails.remove(unusedTailCompetitorDAO)); } } } - - private String getBoatImageURL(GPSFixDAO boatFix, boolean highlighted) { - return getBoatImageURL(getBoatImageRotator(boatFix, highlighted), boatFix); - } - - private String getBoatImageURL(ImageRotator boatImageRotator, GPSFixDAO boatFix) { - // the possible zoom level range is 0 to 21 (zoom level 0 would show the whole world) - int zoomLevel = map.getZoomLevel(); - double minScaleFactor = 0.1; - double maxScaleFactor = 1.0; - - double scaleDiffPerZoomLevel = (maxScaleFactor - minScaleFactor) / 21.0; - - double scaleFactor = minScaleFactor + scaleDiffPerZoomLevel * zoomLevel; - - return boatImageRotator.getRotatedImageURL(boatFix.speedWithBearing.bearingInDegrees, scaleFactor); - } - - private Icon getBoatImageIcon(GPSFixDAO boatFix, boolean highlighted) { - ImageRotator boatImageRotator = getBoatImageRotator(boatFix, highlighted); - Icon icon = Icon.newInstance(getBoatImageURL(boatImageRotator, boatFix)); - icon.setIconAnchor(boatImageRotator.getAnchor()); - return icon; - } - - private ImageRotator getBoatImageRotator(GPSFixDAO boatFix, boolean highlighted) { - if (boatFix.tack == Tack.PORT) { - if (LegType.DOWNWIND == boatFix.legType) { - if (highlighted) { - return boatIconHighlightedDownwindStarboardRotator; - } else { - return boatIconDownwindStarboardRotator; - } - } else { - if (highlighted) { - return boatIconHighlightedStarboardRotator; - } else { - return boatIconStarboardRotator; - } - } - } else { - if (LegType.DOWNWIND == boatFix.legType) { - if (highlighted) { - return boatIconHighlightedDownwindPortRotator; - } else { - return boatIconDownwindPortRotator; - } - } else { - if (highlighted) { - return boatIconHighlightedPortRotator; - } else { - return boatIconPortRotator; - } - } - } - } - protected Marker createBuoyMarker(final MarkDAO markDAO) { MarkerOptions options = MarkerOptions.newInstance(); - if (buoyIcon != null) { - options.setIcon(buoyIcon); + if (imageResources.buoyIcon != null) { + options.setIcon(imageResources.buoyIcon); } options.setTitle(markDAO.name); final Marker buoyMarker = new Marker(LatLng.newInstance(markDAO.position.latDeg, markDAO.position.lngDeg), @@ -557,11 +562,11 @@ public class RaceMap { } protected Marker createBoatMarker(final CompetitorDAO competitorDAO, boolean highlighted) { - GPSFixDAO boatFix = data.getBoatFix(competitorDAO); + GPSFixDAO boatFix = getBoatFix(competitorDAO); double latDeg = boatFix.position.latDeg; double lngDeg = boatFix.position.lngDeg; MarkerOptions options = MarkerOptions.newInstance(); - Icon icon = getBoatImageIcon(boatFix, highlighted); + Icon icon = imageResources.getBoatImageIcon(boatFix, highlighted); options.setIcon(icon); options.setTitle(competitorDAO.name); final Marker boatMarker = new Marker(LatLng.newInstance(latDeg, lngDeg), options); @@ -594,7 +599,7 @@ public class RaceMap { } private void showCompetitorInfoWindow(final CompetitorDAO competitorDAO, LatLng where) { - GPSFixDAO latestFixForCompetitor = data.getBoatFix(competitorDAO); + GPSFixDAO latestFixForCompetitor = getBoatFix(competitorDAO); map.getInfoWindow().open(where, new InfoWindowContent(getInfoWindowContent(competitorDAO, latestFixForCompetitor))); } @@ -618,9 +623,9 @@ public class RaceMap { RaceDAO race = selectedEventAndRace.get(selectedEventAndRace.size() - 1).getC(); if (event != null && race != null) { Map from = new HashMap(); - from.put(competitorDAO, data.fixes.get(competitorDAO).get(data.firstShownFix.get(competitorDAO)).timepoint); + from.put(competitorDAO, fixes.get(competitorDAO).get(firstShownFix.get(competitorDAO)).timepoint); Map to = new HashMap(); - to.put(competitorDAO, data.getBoatFix(competitorDAO).timepoint); + to.put(competitorDAO, getBoatFix(competitorDAO).timepoint); /* currently not showing Douglas-Peucker points; TODO use checkboxes to select what to show (Bug #6) */ sailingService.getDouglasPoints(new EventNameAndRaceName(event.name, race.name), from, to, 3, new AsyncCallback>>() { @@ -631,12 +636,12 @@ public class RaceMap { @Override public void onSuccess(Map> result) { - data.lastDouglasPeuckerResult = result; - if (data.douglasMarkers != null) { + lastDouglasPeuckerResult = result; + if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); } if (!timer.isPlaying()) { - if (showDouglasPeuckerPoints) { + if (settings.isShowDouglasPeuckerPoints()) { showMarkDouglasPeuckerPoints(result); } } @@ -651,8 +656,8 @@ public class RaceMap { @Override public void onSuccess(Map> result) { - data.lastManeuverResult = result; - if (data.maneuverMarkers != null) { + lastManeuverResult = result; + if (maneuverMarkers != null) { removeAllManeuverMarkers(); } if (!timer.isPlaying()) { @@ -666,20 +671,28 @@ public class RaceMap { return result; } + private Collection getCompetitorsToShow() { + if (settings.isShowOnlySelectedCompetitors()) { + return selectedMapCompetitors; + } else { + return allCompetitors; + } + } + private String getColorString(CompetitorDAO competitorDAO) { // TODO green no more than 70, red no less than 120 return "#" + (Integer.toHexString(competitorDAO.hashCode()) + "000000").substring(0, 4).toUpperCase() + "00"; } /** - * Creates a polyline for the competitor represented by competitorDAO, taking the raceMapData.fixes from - * {@link #data.fixes raceMapData.fixes.get(competitorDAO)} and using the raceMapData.fixes starting at time point from (inclusive) + * Creates a polyline for the competitor represented by competitorDAO, taking the fixes from + * {@link #fixes fixes.get(competitorDAO)} and using the fixes starting at time point from (inclusive) * up to the last fix with time point before to. The polyline is returned. Updates are applied to - * {@link #data.lastShownFix}, {@link #data.firstShownFix} and {@link #data.tails}. + * {@link #lastShownFix}, {@link #firstShownFix} and {@link #tails}. */ protected Polyline createTailAndUpdateIndices(final CompetitorDAO competitorDAO, Date from, Date to) { List points = new ArrayList(); - List fixesForCompetitor = data.fixes.get(competitorDAO); + List fixesForCompetitor = fixes.get(competitorDAO); int indexOfFirst = -1; int indexOfLast = -1; int i = 0; @@ -707,8 +720,8 @@ public class RaceMap { indexOfLast = i - 1; } if (indexOfFirst != -1 && indexOfLast != -1) { - data.firstShownFix.put(competitorDAO, indexOfFirst); - data.lastShownFix.put(competitorDAO, indexOfLast); + firstShownFix.put(competitorDAO, indexOfFirst); + lastShownFix.put(competitorDAO, indexOfLast); } PolylineOptions options = PolylineOptions.newInstance( /* clickable */true, /* geodesic */true); @@ -732,30 +745,30 @@ public class RaceMap { map.setTitle(""); } }); - data.tails.put(competitorDAO, result); + tails.put(competitorDAO, result); return result; } protected void removeAllMarkDouglasPeuckerpoints() { - if (data.douglasMarkers != null) { - for (Marker marker : data.douglasMarkers) { + if (douglasMarkers != null) { + for (Marker marker : douglasMarkers) { map.removeOverlay(marker); } } - data.douglasMarkers = null; + douglasMarkers = null; } protected void removeAllManeuverMarkers() { - if (data.maneuverMarkers != null) { - for (Marker marker : data.maneuverMarkers) { + if (maneuverMarkers != null) { + for (Marker marker : maneuverMarkers) { map.removeOverlay(marker); } - data.maneuverMarkers = null; + maneuverMarkers = null; } } protected void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { - data.douglasMarkers = new HashSet(); + douglasMarkers = new HashSet(); if (map != null && gpsFixPointMapForCompetitors != null) { Set keySet = gpsFixPointMapForCompetitors.keySet(); Iterator iter = keySet.iterator(); @@ -767,7 +780,7 @@ public class RaceMap { MarkerOptions options = MarkerOptions.newInstance(); options.setTitle(fix.timepoint+": "+fix.position+", "+fix.speedWithBearing.toString()); Marker marker = new Marker(latLng, options); - data.douglasMarkers.add(marker); + douglasMarkers.add(marker); map.addOverlay(marker); } } @@ -775,7 +788,7 @@ public class RaceMap { } protected void showManeuvers(Map> maneuvers) { - data.maneuverMarkers = new HashSet(); + maneuverMarkers = new HashSet(); if (map != null && maneuvers != null) { Set keySet = maneuvers.keySet(); Iterator iter = keySet.iterator(); @@ -789,56 +802,188 @@ public class RaceMap { options.setTitle("" + maneuver.timepoint + ": " + maneuver.type + " " + maneuver.directionChangeInDegrees + "deg from " + maneuver.speedWithBearingBefore + " to " + maneuver.speedWithBearingAfter); - if (maneuver.type.equals("TACK") && showManeuverTack) { + if (maneuver.type.equals("TACK") && settings.isShowManeuverTack()) { if (maneuver.newTack.equals("PORT")) { - options.setIcon(tackToPortIcon); + options.setIcon(imageResources.tackToPortIcon); } else { - options.setIcon(tackToStarboardIcon); + options.setIcon(imageResources.tackToStarboardIcon); } - } else if (maneuver.type.equals("JIBE") && showManeuverJibe) { + } else if (maneuver.type.equals("JIBE") && settings.isShowManeuverJibe()) { if (maneuver.newTack.equals("PORT")) { - options.setIcon(jibeToPortIcon); + options.setIcon(imageResources.jibeToPortIcon); } else { - options.setIcon(jibeToStarboardIcon); + options.setIcon(imageResources.jibeToStarboardIcon); } - } else if (maneuver.type.equals("HEAD_UP") && showManeuverHeadUp) { + } else if (maneuver.type.equals("HEAD_UP") && settings.isShowManeuverHeadUp()) { if (maneuver.newTack.equals("PORT")) { - options.setIcon(headUpOnPortIcon); + options.setIcon(imageResources.headUpOnPortIcon); } else { - options.setIcon(headUpOnStarboardIcon); + options.setIcon(imageResources.headUpOnStarboardIcon); } - } else if (maneuver.type.equals("BEAR_AWAY") && showManeuverBearAway) { + } else if (maneuver.type.equals("BEAR_AWAY") && settings.isShowManeuverBearAway()) { if (maneuver.newTack.equals("PORT")) { - options.setIcon(bearAwayOnPortIcon); + options.setIcon(imageResources.bearAwayOnPortIcon); } else { - options.setIcon(bearAwayOnStarboardIcon); + options.setIcon(imageResources.bearAwayOnStarboardIcon); } - } else if (maneuver.type.equals("PENALTY_CIRCLE") && showManeuverPenaltyCircle) { + } else if (maneuver.type.equals("PENALTY_CIRCLE") && settings.isShowManeuverPenaltyCircle()) { if (maneuver.newTack.equals("PORT")) { - options.setIcon(penaltyCircleToPortIcon); + options.setIcon(imageResources.penaltyCircleToPortIcon); } else { - options.setIcon(penaltyCircleToStarboardIcon); + options.setIcon(imageResources.penaltyCircleToStarboardIcon); } - } else if (maneuver.type.equals("MARK_PASSING") && showManeuverMarkPassing) { + } else if (maneuver.type.equals("MARK_PASSING") && settings.isShowManeuverMarkPassing()) { if (maneuver.newTack.equals("PORT")) { - options.setIcon(markPassingToPortIcon); + options.setIcon(imageResources.markPassingToPortIcon); } else { - options.setIcon(markPassingToStarboardIcon); + options.setIcon(imageResources.markPassingToStarboardIcon); } } else { - if (maneuver.type.equals("UNKNOWN") && showManeuverOther) { - options.setIcon(unknownManeuverIcon); + if (maneuver.type.equals("UNKNOWN") && settings.isShowManeuverOther()) { + options.setIcon(imageResources.unknownManeuverIcon); } else { showThisManeuver = false; } } if (showThisManeuver) { Marker marker = new Marker(latLng, options); - data.maneuverMarkers.add(marker); + maneuverMarkers.add(marker); map.addOverlay(marker); } } } } } + + protected Date getTimepointOfFirstNonExtrapolated(List fixesForCompetitor) { + for (GPSFixDAO fix : fixesForCompetitor) { + if (!fix.extrapolated) { + return fix.timepoint; + } + } + return null; + } + + protected Date getTimepointOfLastNonExtrapolated(List fixesForCompetitor) { + if (!fixesForCompetitor.isEmpty()) { + for (ListIterator fixIter = fixesForCompetitor.listIterator(fixesForCompetitor.size() - 1); fixIter + .hasPrevious();) { + GPSFixDAO fix = fixIter.previous(); + if (!fix.extrapolated) { + return fix.timepoint; + } + } + } + return null; + } + + /** + * While updating the {@link #fixes} for competitorDAO, the invariants for {@link #tails} and + * {@link #firstShownFix} and {@link #lastShownFix} are maintained: each time a fix is inserted, the + * {@link #firstShownFix}/{@link #lastShownFix} records for competitorDAO are incremented if they are + * greater or equal to the insertion index and we have a tail in {@link #tails} for competitorDAO. + * Additionally, if the fix is in between the fixes shown in the competitor's tail, the tail is adjusted by + * inserting the corresponding fix. + */ + protected void mergeFixes(CompetitorDAO competitorDAO, List mergeThis) { + List intoThis = fixes.get(competitorDAO); + int indexOfFirstShownFix = firstShownFix.get(competitorDAO) == null ? -1 : firstShownFix.get(competitorDAO); + int indexOfLastShownFix = lastShownFix.get(competitorDAO) == null ? -1 : lastShownFix.get(competitorDAO); + Polyline tail = tails.get(competitorDAO); + int intoThisIndex = 0; + for (GPSFixDAO mergeThisFix : mergeThis) { + while (intoThisIndex < intoThis.size() + && intoThis.get(intoThisIndex).timepoint.before(mergeThisFix.timepoint)) { + intoThisIndex++; + } + if (intoThisIndex < intoThis.size() && intoThis.get(intoThisIndex).timepoint.equals(mergeThisFix.timepoint)) { + // exactly same time point; replace with fix from mergeThis + intoThis.set(intoThisIndex, mergeThisFix); + } else { + intoThis.add(intoThisIndex, mergeThisFix); + if (indexOfFirstShownFix >= intoThisIndex) { + indexOfFirstShownFix++; + } + if (indexOfLastShownFix >= intoThisIndex) { + indexOfLastShownFix++; + } + if (tail != null && intoThisIndex >= indexOfFirstShownFix && intoThisIndex <= indexOfLastShownFix) { + tail.insertVertex(intoThisIndex - indexOfFirstShownFix, + LatLng.newInstance(mergeThisFix.position.latDeg, mergeThisFix.position.lngDeg)); + } + } + intoThisIndex++; + } + } + + protected GPSFixDAO getBoatFix(CompetitorDAO competitorDAO) { + return fixes.get(competitorDAO).get(lastShownFix.get(competitorDAO)); + } + + /** + * If the tail starts before from, removes leading vertices from tail that are before + * from. This is determined by using the {@link #firstShownFix} index which tells us where in + * {@link #fixes} we find the sequence of fixes currently represented in the tail. + *

+ * + * If the tail starts after from, vertices for those {@link #fixes} for competitorDAO at + * or after time point from and before the time point of the first fix displayed so far in the tail and + * before to are prepended to the tail. + *

+ * + * Now to the end of the tail: if the existing tail's end exceeds to, the vertices in excess are + * removed (aided by {@link #lastShownFix}). Otherwise, for the competitor's fixes starting at the tail's end up to + * to are appended to the tail. + *

+ * + * When this method returns, {@link #firstShownFix} and {@link #lastShownFix} have been updated accordingly. + */ + protected void updateTail(Polyline tail, CompetitorDAO competitorDAO, Date from, Date to) { + List fixesForCompetitor = fixes.get(competitorDAO); + int indexOfFirstShownFix = firstShownFix.get(competitorDAO) == null ? -1 : firstShownFix.get(competitorDAO); + while (indexOfFirstShownFix != -1 && tail.getVertexCount() > 0 + && fixesForCompetitor.get(indexOfFirstShownFix).timepoint.before(from)) { + tail.deleteVertex(0); + indexOfFirstShownFix++; + } + // now the polyline contains no more vertices representing fixes before "from"; + // go back in time starting at indexOfFirstShownFix while the fixes are still at or after "from" + // and insert corresponding vertices into the polyline + while (indexOfFirstShownFix > 0 && !fixesForCompetitor.get(indexOfFirstShownFix - 1).timepoint.before(from)) { + indexOfFirstShownFix--; + GPSFixDAO fix = fixesForCompetitor.get(indexOfFirstShownFix); + tail.insertVertex(0, LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg)); + } + // now adjust the polylines tail: remove excess vertices that are after "to" + int indexOfLastShownFix = lastShownFix.get(competitorDAO) == null ? -1 : lastShownFix.get(competitorDAO); + while (indexOfLastShownFix != -1 && tail.getVertexCount() > 0 + && fixesForCompetitor.get(indexOfLastShownFix).timepoint.after(to)) { + tail.deleteVertex(tail.getVertexCount() - 1); + indexOfLastShownFix--; + } + // now the polyline contains no more vertices representing fixes after "to"; + // go forward in time starting at indexOfLastShownFix while the fixes are still at or before "to" + // and insert corresponding vertices into the polyline + while (indexOfLastShownFix < fixesForCompetitor.size() - 1 + && !fixesForCompetitor.get(indexOfLastShownFix + 1).timepoint.after(to)) { + indexOfLastShownFix++; + GPSFixDAO fix = fixesForCompetitor.get(indexOfLastShownFix); + tail.insertVertex(tail.getVertexCount(), LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg)); + } + firstShownFix.put(competitorDAO, indexOfFirstShownFix); + lastShownFix.put(competitorDAO, indexOfLastShownFix); + } + + public Set getSelectedMapCompetitors() { + return selectedMapCompetitors; + } + + public RaceMapSettings getSettings() { + return settings; + } + + public void setSettings(RaceMapSettings settings) { + this.settings = settings; + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapData.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapData.java deleted file mode 100644 index 572b7f12476..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapData.java +++ /dev/null @@ -1,197 +0,0 @@ -package com.sap.sailing.gwt.ui.adminconsole; - -import java.util.Date; -import java.util.HashMap; -import java.util.List; -import java.util.ListIterator; -import java.util.Map; -import java.util.Set; - -import com.google.gwt.maps.client.geom.LatLng; -import com.google.gwt.maps.client.overlay.Marker; -import com.google.gwt.maps.client.overlay.Polyline; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.shared.CompetitorDAO; -import com.sap.sailing.gwt.ui.shared.GPSFixDAO; -import com.sap.sailing.gwt.ui.shared.ManeuverDAO; -import com.sap.sailing.gwt.ui.shared.MarkDAO; - -public class RaceMapData { - - /** - * Tails of competitors currently displayed as overlays on the map. - */ - protected final Map tails; - - /** - * Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the first fix shown - * in {@link #tails} is . - */ - protected final Map firstShownFix; - - /** - * Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the last fix shown in - * {@link #tails} is . - */ - protected final Map lastShownFix; - - /** - * Fixes of each competitors tail. If a list is contained for a competitor, the list contains a timely "contiguous" - * list of fixes for the competitor. This means the server has no more data for the time interval covered, unless - * the last fix was {@link GPSFixDAO#extrapolated obtained by extrapolation}. - */ - protected final Map> fixes; - - /** - * Markers used as boat display on the map - */ - protected final Map boatMarkers; - - protected final Map buoyMarkers; - - /** - * markers displayed in response to - * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} - */ - protected Set douglasMarkers; - - /** - * markers displayed in response to - * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} - */ - protected Set maneuverMarkers; - - protected Map> lastManeuverResult; - - protected Map> lastDouglasPeuckerResult; - - public RaceMapData() { - tails = new HashMap(); - firstShownFix = new HashMap(); - lastShownFix = new HashMap(); - buoyMarkers = new HashMap(); - boatMarkers = new HashMap(); - fixes = new HashMap>(); - } - - protected Date getTimepointOfFirstNonExtrapolated(List fixesForCompetitor) { - for (GPSFixDAO fix : fixesForCompetitor) { - if (!fix.extrapolated) { - return fix.timepoint; - } - } - return null; - } - - protected Date getTimepointOfLastNonExtrapolated(List fixesForCompetitor) { - if (!fixesForCompetitor.isEmpty()) { - for (ListIterator fixIter = fixesForCompetitor.listIterator(fixesForCompetitor.size() - 1); fixIter - .hasPrevious();) { - GPSFixDAO fix = fixIter.previous(); - if (!fix.extrapolated) { - return fix.timepoint; - } - } - } - return null; - } - - /** - * While updating the {@link #fixes} for competitorDAO, the invariants for {@link #tails} and - * {@link #firstShownFix} and {@link #lastShownFix} are maintained: each time a fix is inserted, the - * {@link #firstShownFix}/{@link #lastShownFix} records for competitorDAO are incremented if they are - * greater or equal to the insertion index and we have a tail in {@link #tails} for competitorDAO. - * Additionally, if the fix is in between the fixes shown in the competitor's tail, the tail is adjusted by - * inserting the corresponding fix. - */ - protected void mergeFixes(CompetitorDAO competitorDAO, List mergeThis) { - List intoThis = fixes.get(competitorDAO); - int indexOfFirstShownFix = firstShownFix.get(competitorDAO) == null ? -1 : firstShownFix.get(competitorDAO); - int indexOfLastShownFix = lastShownFix.get(competitorDAO) == null ? -1 : lastShownFix.get(competitorDAO); - Polyline tail = tails.get(competitorDAO); - int intoThisIndex = 0; - for (GPSFixDAO mergeThisFix : mergeThis) { - while (intoThisIndex < intoThis.size() - && intoThis.get(intoThisIndex).timepoint.before(mergeThisFix.timepoint)) { - intoThisIndex++; - } - if (intoThisIndex < intoThis.size() && intoThis.get(intoThisIndex).timepoint.equals(mergeThisFix.timepoint)) { - // exactly same time point; replace with fix from mergeThis - intoThis.set(intoThisIndex, mergeThisFix); - } else { - intoThis.add(intoThisIndex, mergeThisFix); - if (indexOfFirstShownFix >= intoThisIndex) { - indexOfFirstShownFix++; - } - if (indexOfLastShownFix >= intoThisIndex) { - indexOfLastShownFix++; - } - if (tail != null && intoThisIndex >= indexOfFirstShownFix && intoThisIndex <= indexOfLastShownFix) { - tail.insertVertex(intoThisIndex - indexOfFirstShownFix, - LatLng.newInstance(mergeThisFix.position.latDeg, mergeThisFix.position.lngDeg)); - } - } - intoThisIndex++; - } - } - - protected GPSFixDAO getBoatFix(CompetitorDAO competitorDAO) { - return fixes.get(competitorDAO).get(lastShownFix.get(competitorDAO)); - } - - /** - * If the tail starts before from, removes leading vertices from tail that are before - * from. This is determined by using the {@link #firstShownFix} index which tells us where in - * {@link #fixes} we find the sequence of fixes currently represented in the tail. - *

- * - * If the tail starts after from, vertices for those {@link #fixes} for competitorDAO at - * or after time point from and before the time point of the first fix displayed so far in the tail and - * before to are prepended to the tail. - *

- * - * Now to the end of the tail: if the existing tail's end exceeds to, the vertices in excess are - * removed (aided by {@link #lastShownFix}). Otherwise, for the competitor's fixes starting at the tail's end up to - * to are appended to the tail. - *

- * - * When this method returns, {@link #firstShownFix} and {@link #lastShownFix} have been updated accordingly. - */ - protected void updateTail(Polyline tail, CompetitorDAO competitorDAO, Date from, Date to) { - List fixesForCompetitor = fixes.get(competitorDAO); - int indexOfFirstShownFix = firstShownFix.get(competitorDAO) == null ? -1 : firstShownFix.get(competitorDAO); - while (indexOfFirstShownFix != -1 && tail.getVertexCount() > 0 - && fixesForCompetitor.get(indexOfFirstShownFix).timepoint.before(from)) { - tail.deleteVertex(0); - indexOfFirstShownFix++; - } - // now the polyline contains no more vertices representing fixes before "from"; - // go back in time starting at indexOfFirstShownFix while the fixes are still at or after "from" - // and insert corresponding vertices into the polyline - while (indexOfFirstShownFix > 0 && !fixesForCompetitor.get(indexOfFirstShownFix - 1).timepoint.before(from)) { - indexOfFirstShownFix--; - GPSFixDAO fix = fixesForCompetitor.get(indexOfFirstShownFix); - tail.insertVertex(0, LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg)); - } - // now adjust the polylines tail: remove excess vertices that are after "to" - int indexOfLastShownFix = lastShownFix.get(competitorDAO) == null ? -1 : lastShownFix.get(competitorDAO); - while (indexOfLastShownFix != -1 && tail.getVertexCount() > 0 - && fixesForCompetitor.get(indexOfLastShownFix).timepoint.after(to)) { - tail.deleteVertex(tail.getVertexCount() - 1); - indexOfLastShownFix--; - } - // now the polyline contains no more vertices representing fixes after "to"; - // go forward in time starting at indexOfLastShownFix while the fixes are still at or before "to" - // and insert corresponding vertices into the polyline - while (indexOfLastShownFix < fixesForCompetitor.size() - 1 - && !fixesForCompetitor.get(indexOfLastShownFix + 1).timepoint.after(to)) { - indexOfLastShownFix++; - GPSFixDAO fix = fixesForCompetitor.get(indexOfLastShownFix); - tail.insertVertex(tail.getVertexCount(), LatLng.newInstance(fix.position.latDeg, fix.position.lngDeg)); - } - firstShownFix.put(competitorDAO, indexOfFirstShownFix); - lastShownFix.put(competitorDAO, indexOfLastShownFix); - } - -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java index f695974a089..9262c9d7148 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java @@ -1,21 +1,13 @@ package com.sap.sailing.gwt.ui.adminconsole; import java.util.ArrayList; -import java.util.Collection; import java.util.Date; -import java.util.HashSet; import java.util.List; import java.util.Map; -import java.util.Set; -import com.google.gwt.event.dom.client.ChangeEvent; -import com.google.gwt.event.dom.client.ChangeHandler; -import com.google.gwt.event.dom.client.ClickEvent; -import com.google.gwt.event.dom.client.ClickHandler; import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.event.logical.shared.ValueChangeHandler; import com.google.gwt.maps.client.geom.LatLng; -import com.google.gwt.maps.client.overlay.Marker; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FormPanel; @@ -23,7 +15,6 @@ import com.google.gwt.user.client.ui.Grid; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.IntegerBox; import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.ProvidesResize; import com.google.gwt.user.client.ui.RequiresResize; import com.google.gwt.user.client.ui.VerticalPanel; @@ -39,9 +30,7 @@ import com.sap.sailing.gwt.ui.client.Timer; import com.sap.sailing.gwt.ui.leaderboard.SmallWindHistoryPanel; import com.sap.sailing.gwt.ui.shared.CompetitorDAO; import com.sap.sailing.gwt.ui.shared.EventDAO; -import com.sap.sailing.gwt.ui.shared.GPSFixDAO; import com.sap.sailing.gwt.ui.shared.ManeuverDAO; -import com.sap.sailing.gwt.ui.shared.MarkDAO; import com.sap.sailing.gwt.ui.shared.PositionDAO; import com.sap.sailing.gwt.ui.shared.QuickRankDAO; import com.sap.sailing.gwt.ui.shared.RaceDAO; @@ -53,44 +42,23 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe private final SailingServiceAsync sailingService; private final ErrorReporter errorReporter; private final Grid grid; - private final RacesListBoxPanel newRaceListBox; - private final ListBox quickRanksBox; - private final List quickRanksList; + private final RacesListBoxPanel raceListBox; private final TimePanel timePanel; - private final Set competitorsSelectedInMap; private final Timer timer; private List> checkboxAndType; private CheckBox checkBoxDouglasPeuckerPoints; - - private final CheckBox showOnlySelected; - + private final CheckBox showOnlySelectedCompetitors; private final IntegerBox tailLengthBox; - /** - * RPC calls may receive responses out of order if there are multiple calls in-flight at the same time. If the time - * slider is moved quickly it generates many requests for boat positions quickly after each other. Sometimes, - * responses for requests send later may return before the responses to all earlier requests have been received and - * processed. This counter is used to number the requests. When processing of a response for a later request has - * already begun, responses to earlier requests will be ignored. - */ - private int boatPositionRequestIDCounter; - - /** - * Corresponds to {@link #boatPositionRequestIDCounter}. As soon as the processing of a response for a request ID - * begins, this attribute is set to the ID. A response won't be processed if a later response is already being - * processed. - */ - private int startedProcessingRequestID; - private final RaceMap raceMap; - + private final QuickRanksListBoxComposite quickRanksListBox; + public RaceMapPanel(SailingServiceAsync sailingService, ErrorReporter errorReporter, final EventRefresher eventRefresher, StringConstants stringConstants) { this.sailingService = sailingService; this.errorReporter = errorReporter; this.timer = new Timer(/* delayBetweenAutoAdvancesInMilliseconds */500); - competitorsSelectedInMap = new HashSet(); checkboxAndType = new ArrayList>(); final VerticalPanel verticalCheckBoxPanel = new VerticalPanel(); verticalCheckBoxPanel.add(new Label(stringConstants.maneuverTypes())); @@ -114,9 +82,9 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe pair.getA().addValueChangeHandler(new ValueChangeHandler() { @Override public void onValueChange(ValueChangeEvent event) { - if (!timer.isPlaying() && raceMap.data.lastManeuverResult != null) { + if (!timer.isPlaying() && raceMap.lastManeuverResult != null) { raceMap.removeAllManeuverMarkers(); - showManeuvers(raceMap.data.lastManeuverResult); + showManeuvers(raceMap.lastManeuverResult); } } }); @@ -128,12 +96,12 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe @Override public void onValueChange(ValueChangeEvent event) { - if (!timer.isPlaying() && raceMap.data.lastDouglasPeuckerResult != null && event.getValue()) { - raceMap.showDouglasPeuckerPoints = true; + if (!timer.isPlaying() && raceMap.lastDouglasPeuckerResult != null && event.getValue()) { + raceMap.getSettings().setShowDouglasPeuckerPoints(true); raceMap.removeAllMarkDouglasPeuckerpoints(); - raceMap.showMarkDouglasPeuckerPoints(raceMap.data.lastDouglasPeuckerResult); + raceMap.showMarkDouglasPeuckerPoints(raceMap.lastDouglasPeuckerResult); } else if (!event.getValue()) { - raceMap.showDouglasPeuckerPoints = false; + raceMap.getSettings().setShowDouglasPeuckerPoints(false); raceMap.removeAllMarkDouglasPeuckerpoints(); } } @@ -151,61 +119,53 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe setMapDisplayOptions(); - newRaceListBox = new RacesListBoxPanel(eventRefresher, stringConstants); - newRaceListBox.addRaceSelectionChangeListener(this); - grid.setWidget(0, 0, newRaceListBox); + raceListBox = new RacesListBoxPanel(eventRefresher, stringConstants); + raceListBox.addRaceSelectionChangeListener(this); + grid.setWidget(0, 0, raceListBox); PositionDAO pos = new PositionDAO(); - if (!raceMap.data.boatMarkers.isEmpty()) { - LatLng latLng = raceMap.data.boatMarkers.values().iterator().next().getLatLng(); + if (!raceMap.boatMarkers.isEmpty()) { + LatLng latLng = raceMap.boatMarkers.values().iterator().next().getLatLng(); pos.latDeg = latLng.getLatitude(); pos.lngDeg = latLng.getLongitude(); } SmallWindHistoryPanel windHistory = new SmallWindHistoryPanel(sailingService, pos, /* number of wind displays */5, /* time interval between displays in milliseconds */5000, stringConstants, errorReporter); - newRaceListBox.addRaceSelectionChangeListener(windHistory); + raceListBox.addRaceSelectionChangeListener(windHistory); grid.setWidget(1, 0, windHistory); HorizontalPanel horizontalRanksVerticalAndCheckboxesManeuversPanel = new HorizontalPanel(); horizontalRanksVerticalAndCheckboxesManeuversPanel.setSpacing(15); - VerticalPanel ranksAndCheckboxAndTailLength = new VerticalPanel(); - HorizontalPanel labelAndTailLengthBox = new HorizontalPanel(); - labelAndTailLengthBox.add(new Label(stringConstants.tailLength())); + VerticalPanel ranksAndCheckboxAndTailLengthPanel = new VerticalPanel(); + HorizontalPanel labelAndTailLengthBoxPanel = new HorizontalPanel(); + labelAndTailLengthBoxPanel.add(new Label(stringConstants.tailLength())); tailLengthBox = new IntegerBox(); - tailLengthBox.setValue((int) (raceMap.tailLengthInMilliSeconds / 1000)); + tailLengthBox.setValue((int) (raceMap.getSettings().getTailLengthInMilliSeconds() / 1000)); tailLengthBox.addValueChangeHandler(new ValueChangeHandler() { @Override public void onValueChange(ValueChangeEvent event) { - raceMap.tailLengthInMilliSeconds = 1000l * event.getValue(); - refreshMapContents(); + raceMap.getSettings().setTailLengthInMilliSeconds(1000l * event.getValue()); + raceMap.redraw(); } }); - labelAndTailLengthBox.add(tailLengthBox); - ranksAndCheckboxAndTailLength.add(labelAndTailLengthBox); - showOnlySelected = new CheckBox(stringConstants.showOnlySelected()); - showOnlySelected.addValueChangeHandler(new ValueChangeHandler() { + labelAndTailLengthBoxPanel.add(tailLengthBox); + ranksAndCheckboxAndTailLengthPanel.add(labelAndTailLengthBoxPanel); + showOnlySelectedCompetitors = new CheckBox(stringConstants.showOnlySelected()); + showOnlySelectedCompetitors.addValueChangeHandler(new ValueChangeHandler() { @Override public void onValueChange(ValueChangeEvent event) { - refreshMapContents(); + raceMap.getSettings().setShowOnlySelectedCompetitors(event.getValue()); + raceMap.redraw(); } }); - ranksAndCheckboxAndTailLength.add(showOnlySelected); - quickRanksList = new ArrayList(); - quickRanksBox = new ListBox(/* isMultipleSelect */true); - quickRanksBox.setVisibleItemCount(20); - quickRanksBox.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - updateBoatSelection(); - } - }); - quickRanksBox.addChangeHandler(new ChangeHandler() { - @Override - public void onChange(ChangeEvent event) { - updateBoatSelection(); - } - }); - ranksAndCheckboxAndTailLength.add(quickRanksBox); - horizontalRanksVerticalAndCheckboxesManeuversPanel.add(ranksAndCheckboxAndTailLength); + ranksAndCheckboxAndTailLengthPanel.add(showOnlySelectedCompetitors); + + quickRanksListBox = new QuickRanksListBoxComposite(true); + quickRanksListBox.addCompetitorSelectionChangeListener(raceMap); + quickRanksListBox.getListBox().setVisibleItemCount(20); + + ranksAndCheckboxAndTailLengthPanel.add(quickRanksListBox); + + horizontalRanksVerticalAndCheckboxesManeuversPanel.add(ranksAndCheckboxAndTailLengthPanel); VerticalPanel verticalPanelRadioAndCheckboxes = new VerticalPanel(); verticalPanelRadioAndCheckboxes.add(verticalCheckBoxPanel); @@ -213,6 +173,7 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe grid.setWidget(2, 0, horizontalRanksVerticalAndCheckboxesManeuversPanel); timePanel = new TimePanel(stringConstants, timer); + timer.addTimeListener(raceMap); timer.addTimeListener(this); timer.addTimeListener(windHistory); grid.setWidget(1, 1, timePanel); @@ -227,51 +188,14 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe return false; } - private void updateBoatSelection() { - for (int i = 0; i < quickRanksBox.getItemCount(); i++) { - setSelectedInMap(quickRanksList.get(i), quickRanksBox.isItemSelected(i)); - } - if (showOnlySelected.getValue()) { - refreshMapContents(); - } - } - - private void setSelectedInMap(CompetitorDAO competitorDAO, boolean itemSelected) { - if (!itemSelected && competitorsSelectedInMap.contains(competitorDAO)) { - // "lowlight" currently selected competitor - Marker highlightedMarker = raceMap.data.boatMarkers.get(competitorDAO); - if (highlightedMarker != null) { - Marker lowlightedMarker = raceMap.createBoatMarker(competitorDAO, false); - raceMap.map.removeOverlay(highlightedMarker); - raceMap.map.addOverlay(lowlightedMarker); - raceMap.data.boatMarkers.put(competitorDAO, lowlightedMarker); - competitorsSelectedInMap.remove(competitorDAO); - } - } else if (itemSelected && !competitorsSelectedInMap.contains(competitorDAO)) { - Marker lowlightedMarker = raceMap.data.boatMarkers.get(competitorDAO); - if (lowlightedMarker != null) { - Marker highlightedMarker = raceMap.createBoatMarker(competitorDAO, true); - raceMap.map.removeOverlay(lowlightedMarker); - raceMap.map.addOverlay(highlightedMarker); - raceMap.data.boatMarkers.put(competitorDAO, highlightedMarker); - int selectionIndex = quickRanksList.indexOf(competitorDAO); - quickRanksBox.setItemSelected(selectionIndex, true); - } - // add the competitor even if not currently contained in map - competitorsSelectedInMap.add(competitorDAO); - } - } - @Override public void fillEvents(List result) { - newRaceListBox.fillEvents(result); - raceMap.selectedEventAndRace = newRaceListBox.getSelectedEventAndRace(); + raceListBox.fillEvents(result); } @Override public void onRaceSelectionChange(List> selectedRaces) { - raceMap.mapFirstZoomDone = false; - raceMap.mapZoomedOrPannedSinceLastRaceSelectionChange = false; + if (!selectedRaces.isEmpty() && selectedRaces.get(selectedRaces.size() - 1) != null) { RaceDAO raceDAO = selectedRaces.get(selectedRaces.size() - 1).getC(); if (raceDAO.startOfRace != null) { @@ -280,12 +204,8 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe } updateSlider(raceDAO); } - // force display of currently selected race - refreshMapContents(); - } - - private void refreshMapContents() { - timeChanged(timer.getTime()); + + raceMap.onRaceSelectionChange(selectedRaces); } private void updateSlider(RaceDAO selectedRace) { @@ -300,51 +220,11 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe @Override public void timeChanged(final Date date) { if (date != null) { - List> selection = newRaceListBox.getSelectedEventAndRace(); + List> selection = raceListBox.getSelectedEventAndRace(); if (!selection.isEmpty()) { EventDAO event = selection.get(selection.size() - 1).getA(); RaceDAO race = selection.get(selection.size() - 1).getC(); if (event != null && race != null) { - final Triple, Map, Map> fromAndToAndOverlap = raceMap.computeFromAndTo(date,this.getCompetitorsToShow()); - final int requestID = boatPositionRequestIDCounter++; - sailingService.getBoatPositions(new EventNameAndRaceName(event.name, race.name), - fromAndToAndOverlap.getA(), fromAndToAndOverlap.getB(), true, - new AsyncCallback>>() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining boat positions: " + caught.getMessage()); - } - - @Override - public void onSuccess(Map> result) { - // process response only if not received out of order - if (startedProcessingRequestID < requestID) { - startedProcessingRequestID = requestID; - Date from = new Date(date.getTime() - raceMap.tailLengthInMilliSeconds); - raceMap.updateFixes(result, fromAndToAndOverlap.getC()); - raceMap.showBoatsOnMap(from, date, getCompetitorsToShow(), competitorsSelectedInMap); - if (raceMap.data.douglasMarkers != null) { - raceMap.removeAllMarkDouglasPeuckerpoints(); - } - if (raceMap.data.maneuverMarkers != null) { - raceMap.removeAllManeuverMarkers(); - } - } - } - }); - sailingService.getMarkPositions(new EventNameAndRaceName(event.name, race.name), date, - new AsyncCallback>() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error trying to obtain mark positions: " - + caught.getMessage()); - } - - @Override - public void onSuccess(List result) { - raceMap.showMarksOnMap(result); - } - }); sailingService.getQuickRanks(new EventNameAndRaceName(event.name, race.name), date, new AsyncCallback>() { @Override @@ -363,30 +243,18 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe } private void showQuickRanks(List result) { - quickRanksBox.clear(); - quickRanksList.clear(); + quickRanksListBox.fillQuickRanks(result, true); + raceMap.fillCompetitors(quickRanksListBox.getCompetitors()); int i = 0; for (QuickRankDAO quickRank : result) { - quickRanksList.add(quickRank.competitor); - quickRanksBox.addItem("" + quickRank.rank + ". " + quickRank.competitor.name + " (" - + quickRank.competitor.threeLetterIocCountryCode + ") in leg #" + (quickRank.legNumber + 1)); // maintain previous selection, based on competitorsSelectedInMap - if (competitorsSelectedInMap.contains(quickRank.competitor)) { - quickRanksBox.setItemSelected(i, /* selected */true); + if (raceMap.getSelectedMapCompetitors().contains(quickRank.competitor)) { + quickRanksListBox.getListBox().setItemSelected(i, /* selected */true); } i++; } } - private Collection getCompetitorsToShow() { - if (showOnlySelected.getValue()) { - return competitorsSelectedInMap; - } else { - // here the quickrankslist is emtpy, because of that te map is not zoomed correctly - return quickRanksList; - } - } - /* * (non-Javadoc) * @@ -407,15 +275,15 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe private void setMapDisplayOptions() { - raceMap.showManeuverTack = getCheckboxValueManeuver("TACK"); - raceMap.showManeuverJibe = getCheckboxValueManeuver("JIBE"); - raceMap.showManeuverHeadUp = getCheckboxValueManeuver("HEAD_UP"); - raceMap.showManeuverBearAway = getCheckboxValueManeuver("BEAR_AWAY"); - raceMap.showManeuverPenaltyCircle = getCheckboxValueManeuver("PENALTY_CIRCLE"); - raceMap.showManeuverMarkPassing = getCheckboxValueManeuver("MARK_PASSING"); - raceMap.showManeuverOther = getCheckboxValueManeuver("OTHER"); - - raceMap.showDouglasPeuckerPoints = checkBoxDouglasPeuckerPoints.getValue(); + RaceMapSettings settings = raceMap.getSettings(); + settings.setShowManeuverTack(getCheckboxValueManeuver("TACK")); + settings.setShowManeuverJibe(getCheckboxValueManeuver("JIBE")); + settings.setShowManeuverHeadUp(getCheckboxValueManeuver("HEAD_UP")); + settings.setShowManeuverBearAway(getCheckboxValueManeuver("BEAR_AWAY")); + settings.setShowManeuverPenaltyCircle(getCheckboxValueManeuver("PENALTY_CIRCLE")); + settings.setShowManeuverMarkPassing(getCheckboxValueManeuver("MARK_PASSING")); + settings.setShowManeuverOther(getCheckboxValueManeuver("OTHER")); + settings.setShowDouglasPeuckerPoints(checkBoxDouglasPeuckerPoints.getValue()); } private void showManeuvers(Map> maneuvers) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapResources.java new file mode 100644 index 00000000000..0d981cc7971 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapResources.java @@ -0,0 +1,204 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.maps.client.MapWidget; +import com.google.gwt.maps.client.geom.LatLng; +import com.google.gwt.maps.client.geom.LatLngBounds; +import com.google.gwt.maps.client.geom.Point; +import com.google.gwt.maps.client.overlay.Icon; +import com.sap.sailing.domain.common.LegType; +import com.sap.sailing.domain.common.Tack; +import com.sap.sailing.gwt.ui.shared.GPSFixDAO; + +public class RaceMapResources { + + private final MapWidget map; + + /** + * Two sails on downwind leg, wind from port (sails on starboard); no highlighting + */ + protected ImageTransformator boatIconDownwindPortRotator; + + /** + * Two sails on downwind leg, wind from port (sails on starboard); with highlighting + */ + protected ImageTransformator boatIconHighlightedDownwindPortRotator; + + /** + * Two sails on downwind leg, wind from starboard (sails on port); no highlighting + */ + protected ImageTransformator boatIconDownwindStarboardRotator; + + /** + * Two sails on downwind leg, wind from starboard (sails on port); with highlighting + */ + protected ImageTransformator boatIconHighlightedDownwindStarboardRotator; + + /** + * One sail, wind from port (sails on starboard); no highlighting + */ + protected ImageTransformator boatIconPortRotator; + + /** + * One sail, wind from port (sails on starboard); with highlighting + */ + protected ImageTransformator boatIconHighlightedPortRotator; + + /** + * One sail, wind from starboard (sails on port); no highlighting + */ + protected ImageTransformator boatIconStarboardRotator; + + /** + * One sail, wind from starboard (sails on port); with highlighting + */ + protected ImageTransformator boatIconHighlightedStarboardRotator; + + protected Icon buoyIcon; + protected Icon tackToStarboardIcon; + protected Icon tackToPortIcon; + protected Icon jibeToStarboardIcon; + protected Icon jibeToPortIcon; + protected Icon markPassingToStarboardIcon; + protected Icon markPassingToPortIcon; + protected Icon headUpOnStarboardIcon; + protected Icon headUpOnPortIcon; + protected Icon bearAwayOnStarboardIcon; + protected Icon bearAwayOnPortIcon; + protected Icon unknownManeuverIcon; + protected Icon penaltyCircleToStarboardIcon; + protected Icon penaltyCircleToPortIcon; + + private static AdminConsoleResources resources = GWT.create(AdminConsoleResources.class); + + public RaceMapResources(final MapWidget map) + { + this.map = map; + + boatIconDownwindPortRotator = new ImageTransformator(resources.lowlightedBoatIconDW_Port()); + boatIconHighlightedDownwindPortRotator = new ImageTransformator(resources.highlightedBoatIconDW_Port()); + boatIconDownwindStarboardRotator = new ImageTransformator(resources.lowlightedBoatIconDW_Starboard()); + boatIconHighlightedDownwindStarboardRotator = new ImageTransformator(resources + .highlightedBoatIconDW_Starboard()); + boatIconPortRotator = new ImageTransformator(resources.lowlightedBoatIcon_Port()); + boatIconHighlightedPortRotator = new ImageTransformator(resources.highlightedBoatIcon_Port()); + boatIconStarboardRotator = new ImageTransformator(resources.lowlightedBoatIcon_Starboard()); + boatIconHighlightedStarboardRotator = new ImageTransformator(resources.highlightedBoatIcon_Starboard()); + buoyIcon = Icon.newInstance(resources.buoyIcon().getSafeUri().asString()); + buoyIcon.setIconAnchor(Point.newInstance(4, 4)); + tackToStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=T|00FF00|000000"); + tackToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + tackToPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=T|FF0000|000000"); + tackToPortIcon.setIconAnchor(Point.newInstance(10, 33)); + jibeToStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=J|00FF00|000000"); + jibeToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + jibeToPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=J|FF0000|000000"); + jibeToPortIcon.setIconAnchor(Point.newInstance(10, 33)); + headUpOnStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=H|00FF00|000000"); + headUpOnStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + headUpOnPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=H|FF0000|000000"); + headUpOnPortIcon.setIconAnchor(Point.newInstance(10, 33)); + bearAwayOnStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=B|00FF00|000000"); + bearAwayOnStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + bearAwayOnPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=B|FF0000|000000"); + bearAwayOnPortIcon.setIconAnchor(Point.newInstance(10, 33)); + markPassingToStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=M|00FF00|000000"); + markPassingToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + markPassingToPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=M|FF0000|000000"); + markPassingToPortIcon.setIconAnchor(Point.newInstance(10, 33)); + unknownManeuverIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=?|FFFFFF|000000"); + unknownManeuverIcon.setIconAnchor(Point.newInstance(10, 33)); + penaltyCircleToStarboardIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=P|00FF00|000000"); + penaltyCircleToStarboardIcon.setIconAnchor(Point.newInstance(10, 33)); + penaltyCircleToPortIcon = Icon + .newInstance("http://chart.apis.google.com/chart?chst=d_map_pin_letter&chld=P|FF0000|000000"); + penaltyCircleToPortIcon.setIconAnchor(Point.newInstance(10, 33)); + } + + public ImageTransformator getBoatImageTransformator(GPSFixDAO boatFix, boolean highlighted) { + if (boatFix.tack == Tack.PORT) { + if (LegType.DOWNWIND == boatFix.legType) { + if (highlighted) { + return boatIconHighlightedDownwindStarboardRotator; + } else { + return boatIconDownwindStarboardRotator; + } + } else { + if (highlighted) { + return boatIconHighlightedStarboardRotator; + } else { + return boatIconStarboardRotator; + } + } + } else { + if (LegType.DOWNWIND == boatFix.legType) { + if (highlighted) { + return boatIconHighlightedDownwindPortRotator; + } else { + return boatIconDownwindPortRotator; + } + } else { + if (highlighted) { + return boatIconHighlightedPortRotator; + } else { + return boatIconPortRotator; + } + } + } + } + + public String getBoatImageURL(GPSFixDAO boatFix, boolean highlighted) { + return getBoatImageURL(getBoatImageTransformator(boatFix, highlighted), boatFix); + } + + public String getBoatImageURL(ImageTransformator boatImageTransformator, GPSFixDAO boatFix) { + // the possible zoom level range is 0 to 21 (zoom level 0 would show the whole world) + int zoomLevel = map.getZoomLevel(); + double minScaleFactor = 0.33; + double realBoatSizeScaleFactor = minScaleFactor; + double boatLengthInMeter = 5.0; + + if(zoomLevel > 5) { + int boatSizeXInPixel = 41; + // int boatSizeYInPixel = 21; + + LatLngBounds bounds = map.getBounds(); + LatLng upperRight = bounds.getNorthEast(); + LatLng bottomLeft = bounds.getSouthWest(); + LatLng upperLeft = LatLng.newInstance(upperRight.getLatitude(), bottomLeft.getLongitude()); + + double distXInMeters = upperLeft.distanceFrom(upperRight); + // double distYInMeters = upperLeft.distanceFrom(bottomLeft); + + int widthInPixel = map.getSize().getWidth(); + // int heightInPixel = map.getSize().getHeight(); + + double realBoatSizeInPixel = (widthInPixel * boatLengthInMeter) / distXInMeters; + realBoatSizeScaleFactor = realBoatSizeInPixel / (double) boatSizeXInPixel; + + if(realBoatSizeScaleFactor < minScaleFactor) + realBoatSizeScaleFactor = minScaleFactor; + } + + return boatImageTransformator.getTransformedImageURL(boatFix.speedWithBearing.bearingInDegrees, realBoatSizeScaleFactor); + } + + public Icon getBoatImageIcon(GPSFixDAO boatFix, boolean highlighted) { + ImageTransformator boatImageTransformator = getBoatImageTransformator(boatFix, highlighted); + Icon icon = Icon.newInstance(getBoatImageURL(boatImageTransformator, boatFix)); + icon.setIconAnchor(boatImageTransformator.getAnchor()); + return icon; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapSettings.java new file mode 100644 index 00000000000..a3c55fa1c32 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapSettings.java @@ -0,0 +1,105 @@ +package com.sap.sailing.gwt.ui.adminconsole; + +public class RaceMapSettings { + + private boolean showDouglasPeuckerPoints = false; + + private boolean showManeuverHeadUp = false; + + private boolean showManeuverBearAway = false; + + private boolean showManeuverTack = false; + + private boolean showManeuverJibe = false; + + private boolean showManeuverPenaltyCircle = false; + + private boolean showManeuverMarkPassing = false; + + private boolean showManeuverOther = false; + + private boolean showOnlySelectedCompetitors = false; + + private long tailLengthInMilliSeconds = 30000l; + + public long getTailLengthInMilliSeconds() { + return tailLengthInMilliSeconds; + } + + public void setTailLengthInMilliSeconds(long tailLengthInMilliSeconds) { + this.tailLengthInMilliSeconds = tailLengthInMilliSeconds; + } + + public boolean isShowDouglasPeuckerPoints() { + return showDouglasPeuckerPoints; + } + + public void setShowDouglasPeuckerPoints(boolean showDouglasPeuckerPoints) { + this.showDouglasPeuckerPoints = showDouglasPeuckerPoints; + } + + public boolean isShowManeuverHeadUp() { + return showManeuverHeadUp; + } + + public void setShowManeuverHeadUp(boolean showManeuverHeadUp) { + this.showManeuverHeadUp = showManeuverHeadUp; + } + + public boolean isShowManeuverBearAway() { + return showManeuverBearAway; + } + + public void setShowManeuverBearAway(boolean showManeuverBearAway) { + this.showManeuverBearAway = showManeuverBearAway; + } + + public boolean isShowManeuverTack() { + return showManeuverTack; + } + + public void setShowManeuverTack(boolean showManeuverTack) { + this.showManeuverTack = showManeuverTack; + } + + public boolean isShowManeuverJibe() { + return showManeuverJibe; + } + + public void setShowManeuverJibe(boolean showManeuverJibe) { + this.showManeuverJibe = showManeuverJibe; + } + + public boolean isShowManeuverPenaltyCircle() { + return showManeuverPenaltyCircle; + } + + public void setShowManeuverPenaltyCircle(boolean showManeuverPenaltyCircle) { + this.showManeuverPenaltyCircle = showManeuverPenaltyCircle; + } + + public boolean isShowManeuverMarkPassing() { + return showManeuverMarkPassing; + } + + public void setShowManeuverMarkPassing(boolean showManeuverMarkPassing) { + this.showManeuverMarkPassing = showManeuverMarkPassing; + } + + public boolean isShowManeuverOther() { + return showManeuverOther; + } + + public void setShowManeuverOther(boolean showManeuverOther) { + this.showManeuverOther = showManeuverOther; + } + + public boolean isShowOnlySelectedCompetitors() { + return showOnlySelectedCompetitors; + } + + public void setShowOnlySelectedCompetitors(boolean showOnlySelectedCompetitors) { + this.showOnlySelectedCompetitors = showOnlySelectedCompetitors; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionChangeListener.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionChangeListener.java new file mode 100644 index 00000000000..488a92e5f94 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionChangeListener.java @@ -0,0 +1,19 @@ +package com.sap.sailing.gwt.ui.client; + +import java.util.List; + +import com.sap.sailing.gwt.ui.shared.CompetitorDAO; + + +/** + * Allows UI components to observe a competitor selector, such as a drop-down box showing a list of competitors + */ +public interface CompetitorSelectionChangeListener { + + /** + * The first element is the first one selected + * @param selectedCompetitors a non-null list which is empty if nothing is selected + */ + void onCompetitorSelectionChange(List selectedCompetitors); + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionProvider.java new file mode 100644 index 00000000000..c8d1d264125 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/CompetitorSelectionProvider.java @@ -0,0 +1,17 @@ +package com.sap.sailing.gwt.ui.client; + +import java.util.List; + +import com.sap.sailing.gwt.ui.shared.CompetitorDAO; + +public interface CompetitorSelectionProvider { + + /** + * @return a non-null list which may be empty + */ + List getSelectedCompetitors(); + + void addCompetitorSelectionChangeListener(CompetitorSelectionChangeListener listener); + + void removeCompetitorSelectionChangeListener(CompetitorSelectionChangeListener listener); +} From ef4f797ae34b0cfce3db9fc049d90c0aadba83c0 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 16:57:40 +0100 Subject: [PATCH 16/33] fixed comment --- .../src/com/sap/sailing/domain/confidence/HasConfidence.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java index bd51e7ff0e9..8e38e951e72 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java @@ -32,7 +32,7 @@ import com.sap.sailing.domain.base.Distance; * @author Axel Uhl (d043530) * * @param - * the type of the scalable value. See particularly the {@link #scale()} method. + * the type of the scalable value * */ public interface HasConfidence { From 7ff19d7cf506c3d19b82b88082f2b13471794006 Mon Sep 17 00:00:00 2001 From: fmittag Date: Fri, 13 Jan 2012 17:35:13 +0100 Subject: [PATCH 17/33] some bug fixes regarding keeping the selection in the QuickRanksListBox --- .../ui/adminconsole/QuickRanksListBoxComposite.java | 13 ++++++++++--- .../sap/sailing/gwt/ui/adminconsole/RaceMap.java | 3 +-- .../sailing/gwt/ui/adminconsole/RaceMapPanel.java | 8 -------- 3 files changed, 11 insertions(+), 13 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/QuickRanksListBoxComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/QuickRanksListBoxComposite.java index 1995f84ad6b..f24b63ef624 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/QuickRanksListBoxComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/QuickRanksListBoxComposite.java @@ -55,9 +55,12 @@ public class QuickRanksListBoxComposite extends Composite implements CompetitorS } public void fillQuickRanks(List quickRanks, boolean isSorted) { + List oldSelection = getSelectedCompetitors(); + quickRankList.clear(); competitorList.clear(); quickRankListBox.clear(); + for (QuickRankDAO quickRank: quickRanks) { quickRankList.add(quickRank); competitorList.add(quickRank.competitor); @@ -73,8 +76,12 @@ public class QuickRanksListBoxComposite extends Composite implements CompetitorS }); } - for (QuickRankDAO c : quickRankList) { - quickRankListBox.addItem(toString(c)); + int index = 0; + for (QuickRankDAO quickRank : quickRankList) { + quickRankListBox.addItem(toString(quickRank)); + if(oldSelection.contains(quickRank.competitor)) + quickRankListBox.setItemSelected(index, true); + index++; } fireCompetitorSelectionChanged(getSelectedCompetitors()); } @@ -121,7 +128,7 @@ public class QuickRanksListBoxComposite extends Composite implements CompetitorS } } - private String toString(QuickRankDAO quickRank) { + protected String toString(QuickRankDAO quickRank) { return quickRank.rank + ". " + quickRank.competitor.name + " (" + quickRank.competitor.threeLetterIocCountryCode + ") in leg #" + (quickRank.legNumber + 1); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java index d9d5adb5123..60429714b69 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java @@ -235,10 +235,9 @@ public class RaceMap implements TimeListener, CompetitorSelectionChangeListener, @Override public void onCompetitorSelectionChange(List newSelectedCompetitors) { - for (CompetitorDAO competitorDAO : newSelectedCompetitors) { - if (selectedMapCompetitors.contains(competitorDAO)) { + if (!selectedMapCompetitors.contains(competitorDAO)) { // "lowlight" currently selected competitor Marker highlightedMarker = boatMarkers.get(competitorDAO); if (highlightedMarker != null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java index 9262c9d7148..e107071bc36 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java @@ -245,14 +245,6 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe private void showQuickRanks(List result) { quickRanksListBox.fillQuickRanks(result, true); raceMap.fillCompetitors(quickRanksListBox.getCompetitors()); - int i = 0; - for (QuickRankDAO quickRank : result) { - // maintain previous selection, based on competitorsSelectedInMap - if (raceMap.getSelectedMapCompetitors().contains(quickRank.competitor)) { - quickRanksListBox.getListBox().setItemSelected(i, /* selected */true); - } - i++; - } } /* From dc0932410da44f15ab9e946e4c4200ca866f1736 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 21:01:35 +0100 Subject: [PATCH 18/33] started to add interfaces for UI components / "widgets" including their setting dialog contributions --- .../adminconsole/LeaderboardCreateDialog.java | 2 +- .../adminconsole/LeaderboardEditDialog.java | 2 +- .../gwt/ui/client/DataEntryDialog.java | 19 +++++++++--- .../gwt/ui/shared/components/Component.java | 30 +++++++++++++++++++ .../shared/components/ComponentHierarchy.java | 5 ++++ .../components/SettingsDialogComponent.java | 29 ++++++++++++++++++ 6 files changed, 81 insertions(+), 6 deletions(-) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentHierarchy.java create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialogComponent.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardCreateDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardCreateDialog.java index c7af9c65bcb..9c7e0987329 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardCreateDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardCreateDialog.java @@ -18,7 +18,7 @@ public class LeaderboardCreateDialog extends LeaderboardDialog{ discardThresholdBoxes = new LongBox[MAX_NUMBER_OF_DISCARDED_RESULTS]; for (int i = 0; i < discardThresholdBoxes.length; i++) { - discardThresholdBoxes[i] = createIntegerBoxWithOptionalValue(null, 2); + discardThresholdBoxes[i] = createLongBoxWithOptionalValue(null, 2); discardThresholdBoxes[i].setVisibleLength(2); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardEditDialog.java index c6e9f8a5351..7804a9a3673 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardEditDialog.java @@ -22,7 +22,7 @@ public class LeaderboardEditDialog extends LeaderboardDialog { if (i < leaderboard.discardThresholds.length) { discardThresholdBoxes[i] = createLongBox(leaderboard.discardThresholds[i], 2); } else { - discardThresholdBoxes[i] = createIntegerBoxWithOptionalValue(null, 2); + discardThresholdBoxes[i] = createLongBoxWithOptionalValue(null, 2); } discardThresholdBoxes[i].setVisibleLength(2); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DataEntryDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DataEntryDialog.java index 2f7b17ec318..f977f1e7c09 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DataEntryDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DataEntryDialog.java @@ -20,6 +20,8 @@ import com.google.gwt.user.client.ui.Widget; /** * An abstract data entry dialog class, capturing data of type T, with generic OK/Cancel buttons, title and * message. Subclasses may override the {@link #show()} method to set the focus on their favorable initial entry field. + * Subclasses can specify a widget to show in the dialog to capture properties specific to the result type T by + * overriding the {@link #getAdditionalWidget()} method. * * @author Axel Uhl (d043530) */ @@ -38,6 +40,15 @@ public abstract class DataEntryDialog { String getErrorMessage(T valueToValidate); } + /** + * @param validator + * an optional validator; if null, no validation of data entered is performed; otherwise, + * data validation is triggered upon any noticeable change in any of the elements constructed by + * {@link #createCheckbox(String)}, {@link #createTextBox(String)}, etc. + * @param callback + * will be called when the dialog if {@link AsyncCallback#onFailure(Throwable) cancelled} or + * {@link AsyncCallback#onSuccess(Object) confirmed} + */ public DataEntryDialog(String title, String message, String okButtonName, String cancelButtonName, Validator validator, final AsyncCallback callback) { dateEntryDialog = new DialogBox(); @@ -102,7 +113,7 @@ public abstract class DataEntryDialog { * * @param initialValue initial value to show in text box; null is permissible */ - protected TextBox createTextBox(String initialValue) { + public TextBox createTextBox(String initialValue) { TextBox textBox = new TextBox(); textBox.setText(initialValue == null ? "" : initialValue); AbstractEntryPoint.addFocusUponKeyUpToggler(textBox); @@ -123,7 +134,7 @@ public abstract class DataEntryDialog { * * @param initialValue initial value to show in text box; null is permissible */ - protected LongBox createLongBox(long initialValue, int visibleLength) { + public LongBox createLongBox(long initialValue, int visibleLength) { LongBox longBox = new LongBox(); longBox.setVisibleLength(visibleLength); longBox.setValue(initialValue); @@ -139,7 +150,7 @@ public abstract class DataEntryDialog { return longBox; } - protected LongBox createIntegerBoxWithOptionalValue(Long initialValue, int visibleLength) { + public LongBox createLongBoxWithOptionalValue(Long initialValue, int visibleLength) { LongBox longBox = new LongBox(); longBox.setVisibleLength(visibleLength); longBox.setValue(initialValue); @@ -155,7 +166,7 @@ public abstract class DataEntryDialog { return longBox; } - protected CheckBox createCheckbox(String checkboxLabel) { + public CheckBox createCheckbox(String checkboxLabel) { CheckBox result = new CheckBox(checkboxLabel); result.addValueChangeHandler(new ValueChangeHandler() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java new file mode 100755 index 00000000000..6218e148c50 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java @@ -0,0 +1,30 @@ +package com.sap.sailing.gwt.ui.shared.components; + + +/** + * + * @author Axel Uhl (d043530) + * + * @param + * the type that describes this component's settings (if any). Use {@link Object} in case your components + * doesn't support any settings. + */ +public interface Component { + /** + * @return the name to display to a user for quick navigation to this component + */ + String getLocalizedShortName(); + + /** + * @return whether this component has settings that a user may change; if so, + */ + boolean hasSettings(); + + /** + * If this component {@link #hasSettings has settings}, this method may return a component for the settings dialog. + * It will be used to obtain a widget shown in the settings dialog, a validator for the component-specific settings, + * as well as to produce a result from the widget's state when the settings dialog wants to validate or return the + * settings. + */ + SettingsDialogComponent getSettingsDialogComponent(); +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentHierarchy.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentHierarchy.java new file mode 100755 index 00000000000..9dcff35b70e --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentHierarchy.java @@ -0,0 +1,5 @@ +package com.sap.sailing.gwt.ui.shared.components; + +public interface ComponentHierarchy { + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialogComponent.java new file mode 100755 index 00000000000..73e345e026e --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialogComponent.java @@ -0,0 +1,29 @@ +package com.sap.sailing.gwt.ui.shared.components; + +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.ui.client.DataEntryDialog; +import com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator; + +public interface SettingsDialogComponent { + /** + * @param dialog + * can be used to {@link DataEntryDialog#createCheckbox(String) produce a checkbox}, a + * {@link DataEntryDialog#createTextBox(String)} or other data entry elements that participate in key + * handling for validation triggering and for canceling and confirming the dialog + * @return null in case this component doesn't make any special contribution to the settings dialog, or + * a valid widget that will be displayed in the settings dialog + */ + Widget getAdditionalWidget(DataEntryDialog dialog); + + /** + * Obtains the result from this settings dialog component; usually, the result object is contructed by reading data + * from the elements of the widget constructed by {@link #getAdditionalWidget(DataEntryDialog)}. + */ + SettingsType getResult(); + + /** + * Obtains an optional validator that is used to validate the {@link #getAdditionalWidget(DataEntryDialog)} contents upon + * changes. If null is returned, no validation will be performed. + */ + Validator getValidator(); +} From 37b33bea6eee1a0e68cef9a20caa3b4811591f5e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 21:20:08 +0100 Subject: [PATCH 19/33] added first pieces of "micro-framework" for settings dialog handling for UI components / widgets --- .../sailing/gwt/ui/client/StringMessages.java | 7 ++++ .../gwt/ui/client/StringMessages.properties | 1 + .../ui/client/StringMessages_de.properties | 1 + .../gwt/ui/shared/components/Component.java | 6 ++++ .../ui/shared/components/SettingsDialog.java | 33 +++++++++++++++++++ 5 files changed, 48 insertions(+) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java 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 new file mode 100755 index 00000000000..d11ca89a699 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -0,0 +1,7 @@ +package com.sap.sailing.gwt.ui.client; + +import com.google.gwt.i18n.client.Messages; + +public interface StringMessages extends Messages { + String settingsForComponent(String localizedComponentName); +} 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 new file mode 100755 index 00000000000..4675edd4746 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties @@ -0,0 +1 @@ +settingsForComponent=Settings for {1} \ No newline at end of file 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 new file mode 100755 index 00000000000..bd84196e60e --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties @@ -0,0 +1 @@ +settingsForComponent=Einstellungen für {1} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java index 6218e148c50..2c18707bc34 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java @@ -27,4 +27,10 @@ public interface Component { * settings. */ SettingsDialogComponent getSettingsDialogComponent(); + + /** + * Updates the settings of this component. Expected to be called when a settings dialog using this component's + * {@link #getSettingsDialogComponent()} has been confirmed. + */ + void updateSettings(SettingsType newSettings); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java new file mode 100755 index 00000000000..5a157736e81 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java @@ -0,0 +1,33 @@ +package com.sap.sailing.gwt.ui.shared.components; + +import com.google.gwt.core.client.GWT; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.sap.sailing.gwt.ui.client.DataEntryDialog; +import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; + +public class SettingsDialog extends DataEntryDialog { + private static StringMessages stringMessages = GWT.create(StringMessages.class); + private final SettingsDialogComponent settingsDialogComponent; + + public SettingsDialog(final Component component, StringConstants stringConstants) { + super(stringMessages.settingsForComponent(component.getLocalizedShortName()), + stringMessages.settingsForComponent(component.getLocalizedShortName()), + stringConstants.ok(), stringConstants.cancel(), component.getSettingsDialogComponent().getValidator(), + new AsyncCallback() { + @Override + public void onFailure(Throwable t) {} + @Override + public void onSuccess(SettingsType newSettings) { + component.updateSettings(newSettings); + } + }); + this.settingsDialogComponent = component.getSettingsDialogComponent(); + } + + @Override + protected SettingsType getResult() { + return settingsDialogComponent.getResult(); + } + +} From af0c6002aa42243546090c8d441858b656ec2f2a Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 21:28:17 +0100 Subject: [PATCH 20/33] added a bit of logic for a component "forest" (multi-rooted tree) --- .../sailing/gwt/ui/shared/components/Component.java | 7 +------ .../gwt/ui/shared/components/ComponentForest.java | 12 ++++++++++++ .../ui/shared/components/ComponentForestEntry.java | 8 ++++++++ .../gwt/ui/shared/components/ComponentHierarchy.java | 5 ----- .../gwt/ui/shared/components/SettingsDialog.java | 10 ++++++++++ .../shared/components/SettingsDialogComponent.java | 8 ++++++++ 6 files changed, 39 insertions(+), 11 deletions(-) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentForest.java create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentForestEntry.java delete mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentHierarchy.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java index 2c18707bc34..b72b8eaff09 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/Component.java @@ -9,12 +9,7 @@ package com.sap.sailing.gwt.ui.shared.components; * the type that describes this component's settings (if any). Use {@link Object} in case your components * doesn't support any settings. */ -public interface Component { - /** - * @return the name to display to a user for quick navigation to this component - */ - String getLocalizedShortName(); - +public interface Component extends ComponentForestEntry { /** * @return whether this component has settings that a user may change; if so, */ diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentForest.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentForest.java new file mode 100755 index 00000000000..965d94bffdd --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentForest.java @@ -0,0 +1,12 @@ +package com.sap.sailing.gwt.ui.shared.components; + +/** + * {@link Component}s are arranged in an "ordered forest" structure (multi-rooted tree with the roots ordered in a sequence). + * Each hierarchy level can optionally provide a localized name. Each hierarchy level consists of one or more + * + * @author Axel Uhl (d043530) + * + */ +public interface ComponentForest extends ComponentForestEntry { + Iterable getEntries(); +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentForestEntry.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentForestEntry.java new file mode 100755 index 00000000000..50cec5ce4ae --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentForestEntry.java @@ -0,0 +1,8 @@ +package com.sap.sailing.gwt.ui.shared.components; + +public interface ComponentForestEntry { + /** + * @return the name to display to a user for quick navigation to this component + */ + String getLocalizedShortName(); +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentHierarchy.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentHierarchy.java deleted file mode 100755 index 9dcff35b70e..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/ComponentHierarchy.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.sap.sailing.gwt.ui.shared.components; - -public interface ComponentHierarchy { - -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java index 5a157736e81..df926fe1d45 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java @@ -2,6 +2,7 @@ package com.sap.sailing.gwt.ui.shared.components; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.FocusWidget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.StringConstants; import com.sap.sailing.gwt.ui.client.StringMessages; @@ -30,4 +31,13 @@ public class SettingsDialog extends DataEntryDialog return settingsDialogComponent.getResult(); } + @Override + public void show() { + super.show(); + FocusWidget focusWidget = settingsDialogComponent.getFocusWidget(); + if (focusWidget != null) { + focusWidget.setFocus(true); + } + } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialogComponent.java index 73e345e026e..9add54e3ed1 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialogComponent.java @@ -1,5 +1,6 @@ package com.sap.sailing.gwt.ui.shared.components; +import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator; @@ -26,4 +27,11 @@ public interface SettingsDialogComponent { * changes. If null is returned, no validation will be performed. */ Validator getValidator(); + + /** + * @return null if no initial focus on any widget is desired, or a widget from the + * {@link #getAdditionalWidget(DataEntryDialog)} result that will then receive the focus when the settings + * dialog is shown + */ + FocusWidget getFocusWidget(); } From 7a1f3d165982fee6597d2060c6a4a2aeee1ce55a Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 21:33:40 +0100 Subject: [PATCH 21/33] started to move leaderboard settings dialog logic into micro-framework for settings / component / widget handling --- .../ui/leaderboard/LeaderboardSettings.java | 5 +++ .../LeaderboardSettingsDialogComponent.java | 35 +++++++++++++++++++ 2 files changed, 40 insertions(+) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettings.java create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettings.java new file mode 100755 index 00000000000..51b40334d56 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettings.java @@ -0,0 +1,5 @@ +package com.sap.sailing.gwt.ui.leaderboard; + +public class LeaderboardSettings { + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java new file mode 100755 index 00000000000..892a96e8a65 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java @@ -0,0 +1,35 @@ +package com.sap.sailing.gwt.ui.leaderboard; + +import com.google.gwt.user.client.ui.FocusWidget; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.ui.client.DataEntryDialog; +import com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator; +import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent; + +public class LeaderboardSettingsDialogComponent implements SettingsDialogComponent { + + @Override + public Widget getAdditionalWidget(DataEntryDialog dialog) { + // TODO Auto-generated method stub + return null; + } + + @Override + public LeaderboardSettings getResult() { + // TODO Auto-generated method stub + return null; + } + + @Override + public Validator getValidator() { + // TODO Auto-generated method stub + return null; + } + + @Override + public FocusWidget getFocusWidget() { + // TODO Auto-generated method stub + return null; + } + +} From 3125ed9a8618bea9ef591e67e86ad7284ddb3264 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 21:55:10 +0100 Subject: [PATCH 22/33] working towards replacing LeaderboardSettingsPanel by new SettingsDialogComponent logic --- .../gwt/ui/client/StringConstants.java | 1 - .../gwt/ui/client/StringConstants.properties | 1 - .../ui/client/StringConstants_de.properties | 1 - .../gwt/ui/leaderboard/LeaderboardPanel.java | 116 +++++++------- .../ui/leaderboard/LeaderboardSettings.java | 46 +++++- .../LeaderboardSettingsDialogComponent.java | 148 +++++++++++++++++- .../leaderboard/LeaderboardSettingsPanel.java | 13 +- 7 files changed, 252 insertions(+), 74 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java index fb0b97f9115..8e8ef1b298a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java @@ -72,7 +72,6 @@ public interface StringConstants extends Constants { String leaderboard(); String leaderboards(); String leaderboardSettings(); - String selectLegDetails(); String settings(); String selectAtLeastOneLegDetail(); String currentSpeedOverGroundInKnots(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties index 1c6e4b4c54e..500baf62d4c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties @@ -67,7 +67,6 @@ sapSailingAnalytics=Sailing Analytics leaderboard=LEADERBOARD leaderboards=Leaderboards leaderboardSettings=Leaderboard Settings -selectLegDetails=Select Timing and Leg Details settings=Settings (e.g., leg detail selection) selectAtLeastOneLegDetail=Select at least one leg detail currentSpeedOverGroundInKnots=SOG diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties index dc562b1511a..5632fd6a93f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties @@ -67,7 +67,6 @@ sapSailingAnalytics=Sailing Analytics leaderboard=ERGEBNISSE leaderboards=Ergebnislisten leaderboardSettings=Einstellungen Ergebnisliste -selectLegDetails=Detaileinstellungen settings=Einstellungen (z.B. Abschnittdetails auswählen) selectAtLeastOneLegDetail=Mindestens ein Detail auswählen currentSpeedOverGroundInKnots=FüG diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index 4fcece85135..a770758922e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -36,7 +36,6 @@ import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.MultiSelectionModel; import com.sap.sailing.gwt.ui.client.Collator; -import com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.PlayStateListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; @@ -50,6 +49,9 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDAO; import com.sap.sailing.gwt.ui.shared.LeaderboardRowDAO; import com.sap.sailing.gwt.ui.shared.LegEntryDAO; import com.sap.sailing.gwt.ui.shared.RaceInLeaderboardDAO; +import com.sap.sailing.gwt.ui.shared.components.Component; +import com.sap.sailing.gwt.ui.shared.components.SettingsDialog; +import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent; import com.sap.sailing.server.api.DetailType; import com.sap.sailing.server.api.LeaderboardNameAndRaceColumnName; import com.sap.sailing.server.api.RaceIdentifier; @@ -60,7 +62,7 @@ import com.sap.sailing.server.api.RaceIdentifier; * @author Axel Uhl (D043530) * */ -public class LeaderboardPanel extends FormPanel implements TimeListener, PlayStateListener { +public class LeaderboardPanel extends FormPanel implements TimeListener, PlayStateListener, Component { private static final int RANK_COLUMN_INDEX = 0; private static final int SAIL_ID_COLUMN_INDEX = 1; @@ -147,63 +149,44 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta @Override public void onClick(ClickEvent event) { - new LeaderboardSettingsPanel(Collections.unmodifiableList(selectedManeuverDetails), Collections.unmodifiableList(selectedLegDetails), - Collections.unmodifiableList(selectedRaceDetails), /* All races to select */ - leaderboard.getRaceColumnNameList(), selectedRaceColumns, timer.getDelayBetweenAutoAdvancesInMilliseconds(), - stringConstants.leaderboardSettings(), stringConstants.selectLegDetails(), stringConstants.ok(), - stringConstants.cancel(), new Validator() { - @Override - public String getErrorMessage(LeaderboardSettingsPanel.Result valueToValidate) { - if (valueToValidate.getLegDetailsToShow().isEmpty()) { - return stringConstants.selectAtLeastOneLegDetail(); - } else if (valueToValidate.getDelayBetweenAutoAdvancesInMilliseconds() < 1000) { - return stringConstants.chooseUpdateIntervalOfAtLeastOneSecond(); - } else { - return null; - } - } - }, new AsyncCallback() { - @Override - public void onSuccess(LeaderboardSettingsPanel.Result result) { - List> columnsToExpandAgain = new ArrayList>(); - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - Column c = getLeaderboardTable().getColumn(i); - if (c instanceof ExpandableSortableColumn) { - ExpandableSortableColumn expandableSortableColumn = (ExpandableSortableColumn) c; - if (expandableSortableColumn.isExpanded()) { - // now toggle expansion back and forth, - // enforcing a re-build of the visible - // child columns - expandableSortableColumn.toggleExpansion(); - columnsToExpandAgain.add(expandableSortableColumn); - } - } - } - selectedManeuverDetails.clear(); - selectedManeuverDetails.addAll(result.getManeuverDetailsToShow()); - selectedLegDetails.clear(); - selectedLegDetails.addAll(result.getLegDetailsToShow()); - selectedRaceDetails.clear(); - selectedRaceDetails.addAll(result.getRaceDetailsToShow()); - selectedRaceColumns.clear(); - selectedRaceColumns.addAll(result.getRaceColumnsToShow()); - // update leaderboard after settings panel column selection change - updateLeaderboard(leaderboard); - - timer.setDelayBetweenAutoAdvancesInMilliseconds(result - .getDelayBetweenAutoAdvancesInMilliseconds()); - setDelayInMilliseconds(result.getDelayInMilliseconds()); - for (ExpandableSortableColumn expandableSortableColumn : columnsToExpandAgain) { - expandableSortableColumn.toggleExpansion(); - } - } - - @Override - public void onFailure(Throwable caught) { - } - }, stringConstants, getDelayInMilliseconds()).show(); + new SettingsDialog(LeaderboardPanel.this, stringConstants).show(); } } + + @Override + public void updateSettings(LeaderboardSettings result) { + List> columnsToExpandAgain = new ArrayList>(); + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + Column c = getLeaderboardTable().getColumn(i); + if (c instanceof ExpandableSortableColumn) { + ExpandableSortableColumn expandableSortableColumn = (ExpandableSortableColumn) c; + if (expandableSortableColumn.isExpanded()) { + // now toggle expansion back and forth, + // enforcing a re-build of the visible + // child columns + expandableSortableColumn.toggleExpansion(); + columnsToExpandAgain.add(expandableSortableColumn); + } + } + } + selectedManeuverDetails.clear(); + selectedManeuverDetails.addAll(result.getManeuverDetailsToShow()); + selectedLegDetails.clear(); + selectedLegDetails.addAll(result.getLegDetailsToShow()); + selectedRaceDetails.clear(); + selectedRaceDetails.addAll(result.getRaceDetailsToShow()); + selectedRaceColumns.clear(); + selectedRaceColumns.addAll(result.getRaceColumnsToShow()); + // update leaderboard after settings panel column selection change + updateLeaderboard(leaderboard); + + timer.setDelayBetweenAutoAdvancesInMilliseconds(result + .getDelayBetweenAutoAdvancesInMilliseconds()); + setDelayInMilliseconds(result.getDelayInMilliseconds()); + for (ExpandableSortableColumn expandableSortableColumn : columnsToExpandAgain) { + expandableSortableColumn.toggleExpansion(); + } + } protected class CompetitorColumn extends SortableColumn { @@ -1314,4 +1297,23 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta CompareCompetitorsChartDialog chartDialog = new CompareCompetitorsChartDialog(sailingService, competitors, races.toArray(new LeaderboardNameAndRaceColumnName[0]), stringConstants, errorReporter); chartDialog.show(); } + + @Override + public boolean hasSettings() { + return true; + } + + @Override + public SettingsDialogComponent getSettingsDialogComponent() { + return new LeaderboardSettingsDialogComponent(Collections.unmodifiableList(selectedManeuverDetails), + Collections.unmodifiableList(selectedLegDetails), + Collections.unmodifiableList(selectedRaceDetails), /* All races to select */ + leaderboard.getRaceColumnNameList(), selectedRaceColumns, timer.getDelayBetweenAutoAdvancesInMilliseconds(), + delayInMilliseconds, stringConstants); + } + + @Override + public String getLocalizedShortName() { + return stringConstants.leaderboard(); + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettings.java index 51b40334d56..d459e58f06c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettings.java @@ -1,5 +1,49 @@ package com.sap.sailing.gwt.ui.leaderboard; -public class LeaderboardSettings { +import java.util.List; +import com.sap.sailing.server.api.DetailType; + +public class LeaderboardSettings { + private final List raceColumnsToShow; + private final List maneuverDetailsToShow; + private final List legDetailsToShow; + private final List raceDetailsToShow; + private final long delayBetweenAutoAdvancesInMilliseconds; + private final long delayInMilliseconds; + + public LeaderboardSettings(List meneuverDetailsToShow, List legDetailsToShow, + List raceDetailsToShow, List raceColumnsToShow, + long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds) { + this.legDetailsToShow = legDetailsToShow; + this.raceDetailsToShow = raceDetailsToShow; + this.raceColumnsToShow = raceColumnsToShow; + this.delayBetweenAutoAdvancesInMilliseconds = delayBetweenAutoAdvancesInMilliseconds; + this.delayInMilliseconds = delayInMilliseconds; + this.maneuverDetailsToShow = meneuverDetailsToShow; + } + + public List getManeuverDetailsToShow() { + return maneuverDetailsToShow; + } + + public List getLegDetailsToShow() { + return legDetailsToShow; + } + + public List getRaceDetailsToShow() { + return raceDetailsToShow; + } + + public List getRaceColumnsToShow(){ + return raceColumnsToShow; + } + + public long getDelayBetweenAutoAdvancesInMilliseconds() { + return delayBetweenAutoAdvancesInMilliseconds; + } + + public long getDelayInMilliseconds() { + return delayInMilliseconds; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java index 892a96e8a65..a5c5dbae956 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java @@ -1,35 +1,167 @@ package com.sap.sailing.gwt.ui.leaderboard; +import java.util.ArrayList; +import java.util.LinkedHashMap; +import java.util.List; +import java.util.Map; + +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FocusWidget; +import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.LongBox; +import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator; +import com.sap.sailing.gwt.ui.client.DetailTypeFormatter; +import com.sap.sailing.gwt.ui.client.StringConstants; import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent; +import com.sap.sailing.server.api.DetailType; public class LeaderboardSettingsDialogComponent implements SettingsDialogComponent { + private final List raceColumnSelection; + private final List raceAllRaceColumns; + private final List maneuverDetailSelection; + private final List legDetailSelection; + private final List raceDetailSelection; + private final Map raceColumnCheckboxes; + private final Map maneuverDetailCheckboxes; + private final Map legDetailCheckboxes; + private final Map raceDetailCheckboxes; + private final StringConstants stringConstants; + private LongBox delayBetweenAutoAdvancesInSecondsBox; + private LongBox delayInSecondsBox; + private final long delayBetweenAutoAdvancesInMilliseconds; + private final long delayInMilliseconds; + public LeaderboardSettingsDialogComponent(List maneuverDetailSelection, + List legDetailSelection, List raceDetailSelection, List raceAllRaceColumns, + List raceColumnSelection, long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds, + StringConstants stringConstants) { + this.maneuverDetailSelection = maneuverDetailSelection; + this.raceColumnSelection = raceColumnSelection; + this.raceAllRaceColumns = raceAllRaceColumns; + this.legDetailSelection = legDetailSelection; + this.raceDetailSelection = raceDetailSelection; + this.stringConstants = stringConstants; + raceColumnCheckboxes = new LinkedHashMap(); + maneuverDetailCheckboxes = new LinkedHashMap(); + legDetailCheckboxes = new LinkedHashMap(); + raceDetailCheckboxes = new LinkedHashMap(); + this.delayBetweenAutoAdvancesInMilliseconds = delayBetweenAutoAdvancesInMilliseconds; + this.delayInMilliseconds = delayInMilliseconds; + } + @Override public Widget getAdditionalWidget(DataEntryDialog dialog) { - // TODO Auto-generated method stub - return null; + delayBetweenAutoAdvancesInSecondsBox = dialog.createLongBox(delayBetweenAutoAdvancesInMilliseconds/1000l, 4); + delayInSecondsBox = dialog.createLongBox(delayInMilliseconds/1000l, 4); + HorizontalPanel hp = new HorizontalPanel(); + VerticalPanel vpMeneuvers = new VerticalPanel(); + vpMeneuvers.setSpacing(5); + vpMeneuvers.add(new Label(stringConstants.maneuverTypes())); + List currentMeneuverDetailSelection = maneuverDetailSelection; + for (DetailType detailType : ManeuverCountRaceColumn.getAvailableManeuverDetailColumnTypes()) { + CheckBox checkbox = dialog.createCheckbox(DetailTypeFormatter.format(detailType, stringConstants)); + checkbox.setValue(currentMeneuverDetailSelection.contains(detailType)); + maneuverDetailCheckboxes.put(detailType, checkbox); + vpMeneuvers.add(checkbox); + } + hp.add(vpMeneuvers); + VerticalPanel vpLeft = new VerticalPanel(); + vpLeft.setSpacing(5); + VerticalPanel vpRight = new VerticalPanel(); + vpRight.setSpacing(5); + vpLeft.add(new Label(stringConstants.timing())); + Label delayLabel = new Label(stringConstants.delayInSeconds()); + vpLeft.add(delayLabel); + vpLeft.add(delayInSecondsBox); + Label delayBetweenAutoAdvancesLabel = new Label(stringConstants.delayBetweenAutoAdvances()); + vpLeft.add(delayBetweenAutoAdvancesLabel); + vpLeft.add(delayBetweenAutoAdvancesInSecondsBox); + vpLeft.add(new Label(stringConstants.raceDetailsToShow())); + List currentRaceDetailSelection = raceDetailSelection; + for (DetailType type : LeaderboardPanel.getAvailableRaceDetailColumnTypes()) { + CheckBox checkbox = dialog.createCheckbox(DetailTypeFormatter.format(type, stringConstants)); + checkbox.setValue(currentRaceDetailSelection.contains(type)); + raceDetailCheckboxes.put(type, checkbox); + vpLeft.add(checkbox); + } + vpLeft.add(new Label(stringConstants.legDetailsToShow())); + List currentLegDetailSelection = legDetailSelection; + for (DetailType type : LegColumn.getAvailableLegDetailColumnTypes()) { + CheckBox checkbox = dialog.createCheckbox(DetailTypeFormatter.format(type, stringConstants)); + checkbox.setValue(currentLegDetailSelection.contains(type)); + legDetailCheckboxes.put(type, checkbox); + vpLeft.add(checkbox); + } + hp.add(vpLeft); + + vpRight.add(new Label(stringConstants.selectedRaces())); + List allColumns = raceAllRaceColumns; + for (String expandableSortableColumn : allColumns) { + CheckBox checkbox = dialog.createCheckbox(expandableSortableColumn); + checkbox.setValue(raceColumnSelection.contains(expandableSortableColumn)); + raceColumnCheckboxes.put(expandableSortableColumn, checkbox); + vpRight.add(checkbox); + } + hp.add(vpRight); + return hp; } @Override public LeaderboardSettings getResult() { - // TODO Auto-generated method stub - return null; + List maneuverDetailsToShow = new ArrayList(); + for (Map.Entry entry : maneuverDetailCheckboxes.entrySet()) { + if (entry.getValue().getValue()) { + maneuverDetailsToShow.add(entry.getKey()); + } + } + List raceDetailsToShow = new ArrayList(); + for (Map.Entry entry : raceDetailCheckboxes.entrySet()) { + if (entry.getValue().getValue()) { + raceDetailsToShow.add(entry.getKey()); + } + } + List legDetailsToShow = new ArrayList(); + for (Map.Entry entry : legDetailCheckboxes.entrySet()) { + if (entry.getValue().getValue()) { + legDetailsToShow.add(entry.getKey()); + } + } + List raceColumnsToShow = new ArrayList(); + for (Map.Entry entry : raceColumnCheckboxes.entrySet()) { + if(entry.getValue().getValue()){ + raceColumnsToShow.add(entry.getKey()); + } + } + Long delayBetweenAutoAdvancesValue = delayBetweenAutoAdvancesInSecondsBox.getValue(); + Long delayInSecondsValue = delayInSecondsBox.getValue(); + return new LeaderboardSettings(maneuverDetailsToShow, legDetailsToShow, raceDetailsToShow, raceColumnsToShow, + 1000l * (delayBetweenAutoAdvancesValue == null ? 0l : delayBetweenAutoAdvancesValue.longValue()), + 1000 * (delayInSecondsValue==null?0:delayInSecondsValue.longValue())); } @Override public Validator getValidator() { - // TODO Auto-generated method stub - return null; + return new Validator() { + @Override + public String getErrorMessage(LeaderboardSettings valueToValidate) { + if (valueToValidate.getLegDetailsToShow().isEmpty()) { + return stringConstants.selectAtLeastOneLegDetail(); + } else if (valueToValidate.getDelayBetweenAutoAdvancesInMilliseconds() < 1000) { + return stringConstants.chooseUpdateIntervalOfAtLeastOneSecond(); + } else { + return null; + } + } + }; } @Override public FocusWidget getFocusWidget() { - // TODO Auto-generated method stub - return null; + return delayInSecondsBox; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsPanel.java index f55cc78b690..0f72c6031c4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsPanel.java @@ -40,7 +40,9 @@ public class LeaderboardSettingsPanel extends DataEntryDialog { private final long delayBetweenAutoAdvancesInMilliseconds; private final long delayInMilliseconds; - public Result(List meneuverDetailsToShow, List legDetailsToShow, List raceDetailsToShow, List raceColumnsToShow, long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds) { + public Result(List meneuverDetailsToShow, List legDetailsToShow, + List raceDetailsToShow, List raceColumnsToShow, + long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds) { this.legDetailsToShow = legDetailsToShow; this.raceDetailsToShow = raceDetailsToShow; this.raceColumnsToShow = raceColumnsToShow; @@ -74,10 +76,11 @@ public class LeaderboardSettingsPanel extends DataEntryDialog { } } - public LeaderboardSettingsPanel(List maneuverDetailSelection, List legDetailSelection, List raceDetailSelection, List raceAllRaceColumns, List raceColumnSelection, - long delayBetweenAutoAdvancesInMilliseconds, String title, String message, - String okButtonName, - String cancelButtonName, com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator validator, AsyncCallback callback, StringConstants stringConstants, long delayInMilliseconds) { + public LeaderboardSettingsPanel(List maneuverDetailSelection, List legDetailSelection, + List raceDetailSelection, List raceAllRaceColumns, List raceColumnSelection, + long delayBetweenAutoAdvancesInMilliseconds, String title, String message, String okButtonName, + String cancelButtonName, com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator validator, + AsyncCallback callback, StringConstants stringConstants, long delayInMilliseconds) { super(title, message, okButtonName, cancelButtonName, validator, callback); this.maneuverDetailSelection = maneuverDetailSelection; this.raceColumnSelection = raceColumnSelection; From 1377bcfdc715c0c2a1f7f3e7639b587047f58db2 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 22:09:46 +0100 Subject: [PATCH 23/33] fixed UTF-8 issues in german property files; first working version of refactored leaderboard settings panel --- .../.settings/org.eclipse.core.resources.prefs | 3 ++- .../sap/sailing/gwt/ui/adminconsole/AdminConsole.java | 4 +++- .../sailing/gwt/ui/client/StringConstants_de.properties | 2 +- .../sap/sailing/gwt/ui/client/StringMessages.properties | 2 +- .../sailing/gwt/ui/client/StringMessages_de.properties | 2 +- .../sailing/gwt/ui/shared/components/SettingsDialog.java | 6 ++++++ .../sap/sailing/server/impl/RacingEventServiceImpl.java | 9 ++++----- 7 files changed, 18 insertions(+), 10 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs index 7faf173b37f..41e85ddbb89 100755 --- a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs +++ b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs @@ -1,3 +1,4 @@ -#Sat Oct 01 15:18:53 CEST 2011 +#Fri Jan 13 22:08:39 CET 2012 eclipse.preferences.version=1 encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties=UTF-8 +encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties=UTF-8 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsole.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsole.java index d40951c4110..413198e63e5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsole.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AdminConsole.java @@ -12,6 +12,7 @@ import com.google.gwt.user.client.ui.TabPanel; import com.sap.sailing.gwt.ui.client.AbstractEntryPoint; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; import com.sap.sailing.gwt.ui.shared.EventDAO; +import com.sap.sailing.server.api.DefaultLeaderboardName; public class AdminConsole extends AbstractEntryPoint implements EventRefresher { private Set eventDisplayers; @@ -47,7 +48,8 @@ public class AdminConsole extends AbstractEntryPoint implements EventRefresher { eventDisplayers.add(raceMapPanel); raceMapPanel.setSize("90%", "90%"); tabPanel.add(raceMapPanel, stringConstants.map(), /* asHTML */ false); - LeaderboardPanel defaultLeaderboardPanel = new LeaderboardPanel(sailingService, stringConstants.defaultLeaderboard(), this, stringConstants); + LeaderboardPanel defaultLeaderboardPanel = new LeaderboardPanel(sailingService, + DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME, this, stringConstants); defaultLeaderboardPanel.setSize("90%", "90%"); tabPanel.add(defaultLeaderboardPanel, stringConstants.defaultLeaderboard(), /* asHTML */ false); LeaderboardConfigPanel leaderboardConfigPanel = new LeaderboardConfigPanel(sailingService, this, this, stringConstants); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties index 5632fd6a93f..cda5dcded8c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties @@ -138,7 +138,7 @@ tacks=Wenden jibes=Halsen penaltyCircles=Strafkringel medalRaceIsNull=Medialienwert nicht erlaubt -maneuverTypes=Man�ver +maneuverTypes=Manöver raceNameEmpty=Der Name des Rennens ist leer configuration=Konfiguration chooseChart=Diagramm wählen 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 4675edd4746..905c853e643 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 @@ -1 +1 @@ -settingsForComponent=Settings for {1} \ No newline at end of file +settingsForComponent=Settings for {0} \ No newline at end of file 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 bd84196e60e..29598078d66 100755 --- 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 @@ -1 +1 @@ -settingsForComponent=Einstellungen für {1} \ No newline at end of file +settingsForComponent=Einstellungen für {0} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java index df926fe1d45..a094915a483 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.shared.components; import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.FocusWidget; +import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.StringConstants; import com.sap.sailing.gwt.ui.client.StringMessages; @@ -26,6 +27,11 @@ public class SettingsDialog extends DataEntryDialog this.settingsDialogComponent = component.getSettingsDialogComponent(); } + @Override + protected Widget getAdditionalWidget() { + return settingsDialogComponent.getAdditionalWidget(this); + } + @Override protected SettingsType getResult() { return settingsDialogComponent.getResult(); diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index ef6b0c3cce1..71108cf2cd6 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -59,6 +59,7 @@ import com.sap.sailing.domain.tractracadapter.RaceRecord; import com.sap.sailing.domain.tractracadapter.Receiver; import com.sap.sailing.expeditionconnector.ExpeditionWindTrackerFactory; import com.sap.sailing.server.RacingEventService; +import com.sap.sailing.server.api.DefaultLeaderboardName; import com.sap.sailing.server.api.EventFetcher; import com.sap.sailing.server.api.EventIdentifier; import com.sap.sailing.server.api.EventNameAndRaceName; @@ -101,8 +102,6 @@ public class RacingEventServiceImpl implements RacingEventService, EventFetcher, */ private final Map leaderboardsByName; - private static final String DEFAULT_LEADERBOARD_NAME = "Default Leaderboard"; - private Set eventsObservedForDefaultLeaderboard = new HashSet(); private final MongoObjectFactory mongoObjectFactory; @@ -131,7 +130,7 @@ public class RacingEventServiceImpl implements RacingEventService, EventFetcher, leaderboardsByName = new HashMap(); // Add one default leaderboard that aggregates all races currently tracked by this service. // This is more for debugging purposes than for anything else. - addLeaderboard(DEFAULT_LEADERBOARD_NAME, new int[] { 5, 8 }); + addLeaderboard(DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME, new int[] { 5, 8 }); loadStoredLeaderboards(); } @@ -353,7 +352,7 @@ public class RacingEventServiceImpl implements RacingEventService, EventFetcher, @Override public void raceAdded(TrackedRace trackedRace) { - leaderboardsByName.get(DEFAULT_LEADERBOARD_NAME).addRace(trackedRace, + leaderboardsByName.get(DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME).addRace(trackedRace, trackedRace.getRace().getName(), /* medalRace */ false); } }); @@ -383,7 +382,7 @@ public class RacingEventServiceImpl implements RacingEventService, EventFetcher, for (RaceDefinition race : event.getAllRaces()) { stopTrackingWind(event, race); // remove from default leaderboard - Leaderboard defaultLeaderboard = getLeaderboardByName(DEFAULT_LEADERBOARD_NAME); + Leaderboard defaultLeaderboard = getLeaderboardByName(DefaultLeaderboardName.DEFAULT_LEADERBOARD_NAME); defaultLeaderboard.removeRaceColumn(race.getName()); } } From a15713f047ed7c3635f595db84c967b5d9f6fa44 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 22:11:06 +0100 Subject: [PATCH 24/33] extracted default leaderboard name constant in shared interface --- .../com/sap/sailing/server/api/DefaultLeaderboardName.java | 5 +++++ 1 file changed, 5 insertions(+) create mode 100755 java/com.sap.sailing.server.api/src/com/sap/sailing/server/api/DefaultLeaderboardName.java diff --git a/java/com.sap.sailing.server.api/src/com/sap/sailing/server/api/DefaultLeaderboardName.java b/java/com.sap.sailing.server.api/src/com/sap/sailing/server/api/DefaultLeaderboardName.java new file mode 100755 index 00000000000..b97ca914a58 --- /dev/null +++ b/java/com.sap.sailing.server.api/src/com/sap/sailing/server/api/DefaultLeaderboardName.java @@ -0,0 +1,5 @@ +package com.sap.sailing.server.api; + +public interface DefaultLeaderboardName { + static final String DEFAULT_LEADERBOARD_NAME = "Default Leaderboard"; +} From 49f366ba842d3888d386790614f8484e49b2db96 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 22:13:03 +0100 Subject: [PATCH 25/33] removed no longer needed leaderboard settings panel; it all moved to LeaderboardSettingsDialogComponent --- .../leaderboard/LeaderboardSettingsPanel.java | 192 ------------------ 1 file changed, 192 deletions(-) delete mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsPanel.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsPanel.java deleted file mode 100755 index 0f72c6031c4..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsPanel.java +++ /dev/null @@ -1,192 +0,0 @@ -package com.sap.sailing.gwt.ui.leaderboard; - -import java.util.ArrayList; -import java.util.LinkedHashMap; -import java.util.List; -import java.util.Map; - -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.CheckBox; -import com.google.gwt.user.client.ui.HorizontalPanel; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.LongBox; -import com.google.gwt.user.client.ui.VerticalPanel; -import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.gwt.ui.client.DataEntryDialog; -import com.sap.sailing.gwt.ui.client.DetailTypeFormatter; -import com.sap.sailing.gwt.ui.client.StringConstants; -import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettingsPanel.Result; -import com.sap.sailing.server.api.DetailType; - -public class LeaderboardSettingsPanel extends DataEntryDialog { - private final List raceColumnSelection; - private final List raceAllRaceColumns; - private final List maneuverDetailSelection; - private final List legDetailSelection; - private final List raceDetailSelection; - private final Map raceColumnCheckboxes; - private final Map maneuverDetailCheckboxes; - private final Map legDetailCheckboxes; - private final Map raceDetailCheckboxes; - private final StringConstants stringConstants; - private final LongBox delayBetweenAutoAdvancesInSecondsBox; - private final LongBox delayInSecondsBox; - - public static class Result { - private final List raceColumnsToShow; - private final List maneuverDetailsToShow; - private final List legDetailsToShow; - private final List raceDetailsToShow; - private final long delayBetweenAutoAdvancesInMilliseconds; - private final long delayInMilliseconds; - - public Result(List meneuverDetailsToShow, List legDetailsToShow, - List raceDetailsToShow, List raceColumnsToShow, - long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds) { - this.legDetailsToShow = legDetailsToShow; - this.raceDetailsToShow = raceDetailsToShow; - this.raceColumnsToShow = raceColumnsToShow; - this.delayBetweenAutoAdvancesInMilliseconds = delayBetweenAutoAdvancesInMilliseconds; - this.delayInMilliseconds = delayInMilliseconds; - this.maneuverDetailsToShow = meneuverDetailsToShow; - } - - public List getManeuverDetailsToShow() { - return maneuverDetailsToShow; - } - - public List getLegDetailsToShow() { - return legDetailsToShow; - } - - public List getRaceDetailsToShow() { - return raceDetailsToShow; - } - - public List getRaceColumnsToShow(){ - return raceColumnsToShow; - } - - public long getDelayBetweenAutoAdvancesInMilliseconds() { - return delayBetweenAutoAdvancesInMilliseconds; - } - - public long getDelayInMilliseconds() { - return delayInMilliseconds; - } - } - - public LeaderboardSettingsPanel(List maneuverDetailSelection, List legDetailSelection, - List raceDetailSelection, List raceAllRaceColumns, List raceColumnSelection, - long delayBetweenAutoAdvancesInMilliseconds, String title, String message, String okButtonName, - String cancelButtonName, com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator validator, - AsyncCallback callback, StringConstants stringConstants, long delayInMilliseconds) { - super(title, message, okButtonName, cancelButtonName, validator, callback); - this.maneuverDetailSelection = maneuverDetailSelection; - this.raceColumnSelection = raceColumnSelection; - this.raceAllRaceColumns = raceAllRaceColumns; - this.legDetailSelection = legDetailSelection; - this.raceDetailSelection = raceDetailSelection; - this.stringConstants = stringConstants; - raceColumnCheckboxes = new LinkedHashMap(); - maneuverDetailCheckboxes = new LinkedHashMap(); - legDetailCheckboxes = new LinkedHashMap(); - raceDetailCheckboxes = new LinkedHashMap(); - delayBetweenAutoAdvancesInSecondsBox = createLongBox(delayBetweenAutoAdvancesInMilliseconds/1000l, 4); - delayInSecondsBox = createLongBox(delayInMilliseconds/1000l, 4); - } - - @Override - protected Widget getAdditionalWidget() { - HorizontalPanel hp = new HorizontalPanel(); - VerticalPanel vpMeneuvers = new VerticalPanel(); - vpMeneuvers.setSpacing(5); - vpMeneuvers.add(new Label(stringConstants.maneuverTypes())); - List currentMeneuverDetailSelection = maneuverDetailSelection; - for (DetailType detailType : ManeuverCountRaceColumn.getAvailableManeuverDetailColumnTypes()) { - CheckBox checkbox = createCheckbox(DetailTypeFormatter.format(detailType, stringConstants)); - checkbox.setValue(currentMeneuverDetailSelection.contains(detailType)); - maneuverDetailCheckboxes.put(detailType, checkbox); - vpMeneuvers.add(checkbox); - } - hp.add(vpMeneuvers); - VerticalPanel vpLeft = new VerticalPanel(); - vpLeft.setSpacing(5); - VerticalPanel vpRight = new VerticalPanel(); - vpRight.setSpacing(5); - vpLeft.add(new Label(stringConstants.timing())); - Label delayLabel = new Label(stringConstants.delayInSeconds()); - vpLeft.add(delayLabel); - vpLeft.add(delayInSecondsBox); - Label delayBetweenAutoAdvancesLabel = new Label(stringConstants.delayBetweenAutoAdvances()); - vpLeft.add(delayBetweenAutoAdvancesLabel); - vpLeft.add(delayBetweenAutoAdvancesInSecondsBox); - vpLeft.add(new Label(stringConstants.raceDetailsToShow())); - List currentRaceDetailSelection = raceDetailSelection; - for (DetailType type : LeaderboardPanel.getAvailableRaceDetailColumnTypes()) { - CheckBox checkbox = createCheckbox(DetailTypeFormatter.format(type, stringConstants)); - checkbox.setValue(currentRaceDetailSelection.contains(type)); - raceDetailCheckboxes.put(type, checkbox); - vpLeft.add(checkbox); - } - vpLeft.add(new Label(stringConstants.legDetailsToShow())); - List currentLegDetailSelection = legDetailSelection; - for (DetailType type : LegColumn.getAvailableLegDetailColumnTypes()) { - CheckBox checkbox = createCheckbox(DetailTypeFormatter.format(type, stringConstants)); - checkbox.setValue(currentLegDetailSelection.contains(type)); - legDetailCheckboxes.put(type, checkbox); - vpLeft.add(checkbox); - } - hp.add(vpLeft); - - vpRight.add(new Label(stringConstants.selectedRaces())); - List allColumns = raceAllRaceColumns; - for (String expandableSortableColumn : allColumns) { - CheckBox checkbox = createCheckbox(expandableSortableColumn); - checkbox.setValue(raceColumnSelection.contains(expandableSortableColumn)); - raceColumnCheckboxes.put(expandableSortableColumn, checkbox); - vpRight.add(checkbox); - } - hp.add(vpRight); - return hp; - } - - @Override - protected Result getResult() { - List maneuverDetailsToShow = new ArrayList(); - for (Map.Entry entry : maneuverDetailCheckboxes.entrySet()) { - if (entry.getValue().getValue()) { - maneuverDetailsToShow.add(entry.getKey()); - } - } - List raceDetailsToShow = new ArrayList(); - for (Map.Entry entry : raceDetailCheckboxes.entrySet()) { - if (entry.getValue().getValue()) { - raceDetailsToShow.add(entry.getKey()); - } - } - List legDetailsToShow = new ArrayList(); - for (Map.Entry entry : legDetailCheckboxes.entrySet()) { - if (entry.getValue().getValue()) { - legDetailsToShow.add(entry.getKey()); - } - } - List raceColumnsToShow = new ArrayList(); - for (Map.Entry entry : raceColumnCheckboxes.entrySet()) { - if(entry.getValue().getValue()){ - raceColumnsToShow.add(entry.getKey()); - } - } - Long delayBetweenAutoAdvancesValue = delayBetweenAutoAdvancesInSecondsBox.getValue(); - Long delayInSecondsValue = delayInSecondsBox.getValue(); - return new Result(maneuverDetailsToShow, legDetailsToShow, raceDetailsToShow, raceColumnsToShow, 1000l*(delayBetweenAutoAdvancesValue==null?0l:delayBetweenAutoAdvancesValue.longValue()), - 1000*(delayInSecondsValue==null?0:delayInSecondsValue.longValue())); - } - - @Override - public void show() { - super.show(); - delayInSecondsBox.setFocus(true); - } - -} From a5524b7598a6d082fb2fab83a84462744770fad1 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 22:15:30 +0100 Subject: [PATCH 26/33] re-formatted method --- .../gwt/ui/leaderboard/LeaderboardPanel.java | 57 +++++++++---------- 1 file changed, 28 insertions(+), 29 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index a770758922e..b35a7826426 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -155,37 +155,36 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta @Override public void updateSettings(LeaderboardSettings result) { - List> columnsToExpandAgain = new ArrayList>(); - for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { - Column c = getLeaderboardTable().getColumn(i); - if (c instanceof ExpandableSortableColumn) { - ExpandableSortableColumn expandableSortableColumn = (ExpandableSortableColumn) c; - if (expandableSortableColumn.isExpanded()) { - // now toggle expansion back and forth, - // enforcing a re-build of the visible - // child columns - expandableSortableColumn.toggleExpansion(); - columnsToExpandAgain.add(expandableSortableColumn); - } - } - } - selectedManeuverDetails.clear(); - selectedManeuverDetails.addAll(result.getManeuverDetailsToShow()); - selectedLegDetails.clear(); - selectedLegDetails.addAll(result.getLegDetailsToShow()); - selectedRaceDetails.clear(); - selectedRaceDetails.addAll(result.getRaceDetailsToShow()); - selectedRaceColumns.clear(); - selectedRaceColumns.addAll(result.getRaceColumnsToShow()); - // update leaderboard after settings panel column selection change - updateLeaderboard(leaderboard); - - timer.setDelayBetweenAutoAdvancesInMilliseconds(result - .getDelayBetweenAutoAdvancesInMilliseconds()); - setDelayInMilliseconds(result.getDelayInMilliseconds()); - for (ExpandableSortableColumn expandableSortableColumn : columnsToExpandAgain) { + List> columnsToExpandAgain = new ArrayList>(); + for (int i = 0; i < getLeaderboardTable().getColumnCount(); i++) { + Column c = getLeaderboardTable().getColumn(i); + if (c instanceof ExpandableSortableColumn) { + ExpandableSortableColumn expandableSortableColumn = (ExpandableSortableColumn) c; + if (expandableSortableColumn.isExpanded()) { + // now toggle expansion back and forth, + // enforcing a re-build of the visible + // child columns expandableSortableColumn.toggleExpansion(); + columnsToExpandAgain.add(expandableSortableColumn); } + } + } + selectedManeuverDetails.clear(); + selectedManeuverDetails.addAll(result.getManeuverDetailsToShow()); + selectedLegDetails.clear(); + selectedLegDetails.addAll(result.getLegDetailsToShow()); + selectedRaceDetails.clear(); + selectedRaceDetails.addAll(result.getRaceDetailsToShow()); + selectedRaceColumns.clear(); + selectedRaceColumns.addAll(result.getRaceColumnsToShow()); + // update leaderboard after settings panel column selection change + updateLeaderboard(leaderboard); + + timer.setDelayBetweenAutoAdvancesInMilliseconds(result.getDelayBetweenAutoAdvancesInMilliseconds()); + setDelayInMilliseconds(result.getDelayInMilliseconds()); + for (ExpandableSortableColumn expandableSortableColumn : columnsToExpandAgain) { + expandableSortableColumn.toggleExpansion(); + } } protected class CompetitorColumn extends SortableColumn { From 2ccdeab2c3a384cdaabd532907aa75ccd75d844b Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 22:24:51 +0100 Subject: [PATCH 27/33] fixed property texts; made StringConstants a Messages interface --- .../.settings/org.eclipse.core.resources.prefs | 3 +-- .../com/sap/sailing/gwt/ui/client/StringConstants.java | 5 +++-- .../sap/sailing/gwt/ui/client/StringConstants.properties | 5 +++-- .../sailing/gwt/ui/client/StringConstants_de.properties | 5 +++-- .../java/com/sap/sailing/gwt/ui/client/StringMessages.java | 7 ------- .../sap/sailing/gwt/ui/client/StringMessages.properties | 1 - .../sap/sailing/gwt/ui/client/StringMessages_de.properties | 1 - .../sailing/gwt/ui/shared/components/SettingsDialog.java | 7 ++----- 8 files changed, 12 insertions(+), 22 deletions(-) delete mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java delete mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties delete mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties diff --git a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs index 41e85ddbb89..b425b7953a9 100755 --- a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs +++ b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs @@ -1,4 +1,3 @@ -#Fri Jan 13 22:08:39 CET 2012 +#Fri Jan 13 22:19:08 CET 2012 eclipse.preferences.version=1 encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties=UTF-8 -encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties=UTF-8 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java index 8e8ef1b298a..75eb6cc15b1 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java @@ -1,8 +1,8 @@ package com.sap.sailing.gwt.ui.client; -import com.google.gwt.i18n.client.Constants; +import com.google.gwt.i18n.client.Messages; -public interface StringConstants extends Constants { +public interface StringConstants extends Messages { String helloWorld(); String kielWeel2011(); String stgAccount(); @@ -193,4 +193,5 @@ public interface StringConstants extends Constants { String hoverOverAPoint(); String noSelection(); String raceIsKnownToStartUpwind(); + String settingsForComponent(String localizedComponentName); } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties index 500baf62d4c..b791679f276 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties @@ -100,7 +100,7 @@ connectAndReadRaces=Connect and read races ports=Ports liveData=Live data storedData=Stored data -uris=URI's +uris=URIs jsonUrl=JSON URL livePort=Port Live Data storedPort=Port Stored Data @@ -187,4 +187,5 @@ yes=Yes no=No hoverOverAPoint=Hover over a point for details noSelection=No selection -raceIsKnownToStartUpwind=Race starts with upwind leg \ No newline at end of file +raceIsKnownToStartUpwind=Race starts with upwind leg +settingsForComponent=Settings for {0} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties index cda5dcded8c..0a2fcb02548 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties @@ -101,7 +101,7 @@ trackSettings=Track settings ports=Ports liveData=Live data storedData=Stored data -uris=URI's +uris=URIs jsonUrl=JSON URL livePort=Port Live Data storedPort=Port Stored Data @@ -188,4 +188,5 @@ yes=Ja no=Nein hoverOverAPoint=Für Details Maus über dem Graph positionieren noSelection=Keine Selektion -raceIsKnownToStartUpwind=Rennen beginnt mit Kreuz \ No newline at end of file +raceIsKnownToStartUpwind=Rennen beginnt mit Kreuz +settingsForComponent=Einstellungen für {0} \ No newline at end of file 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 deleted file mode 100755 index d11ca89a699..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ /dev/null @@ -1,7 +0,0 @@ -package com.sap.sailing.gwt.ui.client; - -import com.google.gwt.i18n.client.Messages; - -public interface StringMessages extends Messages { - String settingsForComponent(String localizedComponentName); -} 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 deleted file mode 100755 index 905c853e643..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties +++ /dev/null @@ -1 +0,0 @@ -settingsForComponent=Settings for {0} \ No newline at end of file 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 deleted file mode 100755 index 29598078d66..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties +++ /dev/null @@ -1 +0,0 @@ -settingsForComponent=Einstellungen für {0} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java index a094915a483..0b7868ef193 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java @@ -1,20 +1,17 @@ package com.sap.sailing.gwt.ui.shared.components; -import com.google.gwt.core.client.GWT; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.StringConstants; -import com.sap.sailing.gwt.ui.client.StringMessages; public class SettingsDialog extends DataEntryDialog { - private static StringMessages stringMessages = GWT.create(StringMessages.class); private final SettingsDialogComponent settingsDialogComponent; public SettingsDialog(final Component component, StringConstants stringConstants) { - super(stringMessages.settingsForComponent(component.getLocalizedShortName()), - stringMessages.settingsForComponent(component.getLocalizedShortName()), + super(stringConstants.settingsForComponent(component.getLocalizedShortName()), + stringConstants.settingsForComponent(component.getLocalizedShortName()), stringConstants.ok(), stringConstants.cancel(), component.getSettingsDialogComponent().getValidator(), new AsyncCallback() { @Override From b630be9418ed128a34241c63bf3922fb003c0996 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 22:25:45 +0100 Subject: [PATCH 28/33] renamed StringConstants to StringMessages because it now extends Messages, no longer Constants --- .../gwt/ui/test/GwtTestCaseColumnToggling.java | 6 +++--- .../sailing/gwt/ui/test/LeaderboardPanelMock.java | 4 ++-- .../.settings/org.eclipse.core.resources.prefs | 4 ++-- .../adminconsole/AbstractEventManagementPanel.java | 6 +++--- .../ui/adminconsole/CreateSwissTimingRacePanel.java | 4 ++-- .../adminconsole/LeaderboardConfigImagesBarCell.java | 8 ++++---- .../gwt/ui/adminconsole/LeaderboardConfigPanel.java | 6 +++--- .../gwt/ui/adminconsole/LeaderboardCreateDialog.java | 4 ++-- .../gwt/ui/adminconsole/LeaderboardDialog.java | 10 +++++----- .../gwt/ui/adminconsole/LeaderboardEditDialog.java | 4 ++-- .../LeaderboardRaceConfigImagesBarCell.java | 8 ++++---- .../gwt/ui/adminconsole/RaceInLeaderboardDialog.java | 8 ++++---- .../sailing/gwt/ui/adminconsole/RaceMapPanel.java | 4 ++-- .../gwt/ui/adminconsole/RacesListBoxPanel.java | 6 +++--- .../SwissTimingEventManagementPanel.java | 4 ++-- .../sap/sailing/gwt/ui/adminconsole/TimePanel.java | 4 ++-- .../adminconsole/TracTracEventManagementPanel.java | 4 ++-- .../gwt/ui/adminconsole/TrackedEventsComposite.java | 4 ++-- .../sap/sailing/gwt/ui/adminconsole/WindPanel.java | 6 +++--- .../sailing/gwt/ui/client/AbstractEntryPoint.java | 4 ++-- .../sailing/gwt/ui/client/DetailTypeFormatter.java | 2 +- .../sap/sailing/gwt/ui/client/LogoAndTitlePanel.java | 2 +- .../{StringConstants.java => StringMessages.java} | 2 +- ...onstants.properties => StringMessages.properties} | 0 ...ts_de.properties => StringMessages_de.properties} | 0 .../sap/sailing/gwt/ui/leaderboard/ChartsPanel.java | 6 +++--- .../leaderboard/CompareCompetitorsChartDialog.java | 4 ++-- .../gwt/ui/leaderboard/ExpandableSortableColumn.java | 6 +++--- .../sailing/gwt/ui/leaderboard/LeaderboardPanel.java | 12 ++++++------ .../LeaderboardSettingsDialogComponent.java | 6 +++--- .../sap/sailing/gwt/ui/leaderboard/LegColumn.java | 8 ++++---- .../gwt/ui/leaderboard/ManeuverCountRaceColumn.java | 8 ++++---- .../gwt/ui/leaderboard/SmallWindHistoryPanel.java | 6 +++--- .../leaderboard/SortableExpandableColumnHeader.java | 6 +++--- .../ui/leaderboardedit/EditableLeaderboardPanel.java | 4 ++-- .../gwt/ui/shared/components/SettingsDialog.java | 4 ++-- 36 files changed, 92 insertions(+), 92 deletions(-) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/{StringConstants.java => StringMessages.java} (95%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/{StringConstants.properties => StringMessages.properties} (100%) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/{StringConstants_de.properties => StringMessages_de.properties} (100%) diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/GwtTestCaseColumnToggling.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/GwtTestCaseColumnToggling.java index 3f10c04fb9d..095fa6e130d 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/GwtTestCaseColumnToggling.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/GwtTestCaseColumnToggling.java @@ -9,7 +9,7 @@ import com.google.gwt.junit.client.GWTTestCase; import com.google.gwt.user.cellview.client.Column; import com.google.gwt.user.client.rpc.AsyncCallback; import com.sap.sailing.domain.common.Util.Pair; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.ExpandableSortableColumn; import com.sap.sailing.gwt.ui.shared.LeaderboardDAO; import com.sap.sailing.gwt.ui.shared.LeaderboardRowDAO; @@ -21,7 +21,7 @@ public class GwtTestCaseColumnToggling extends GWTTestCase { //These objects should be created by calling GWT.create(Class c); private LeaderboardPanelMock leaderboardPanel; private MyTestSailingServiceAsync service; - private StringConstants sc; + private StringMessages sc; //Test data. private final String LEADERBOARD_NAME = "test"; @@ -43,7 +43,7 @@ public class GwtTestCaseColumnToggling extends GWTTestCase { protected void gwtSetUp() throws Exception { super.gwtSetUp(); service = GWT.create(MyTestSailingService.class); - sc = GWT.create(StringConstants.class); + sc = GWT.create(StringMessages.class); } public void testColumnDeleting() { diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/LeaderboardPanelMock.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/LeaderboardPanelMock.java index aaf4ba7b4ec..e5a68d13d72 100644 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/LeaderboardPanelMock.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/LeaderboardPanelMock.java @@ -3,7 +3,7 @@ package com.sap.sailing.gwt.ui.test; import com.google.gwt.user.cellview.client.CellTable; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; import com.sap.sailing.gwt.ui.leaderboard.SortableColumn; import com.sap.sailing.gwt.ui.shared.LeaderboardDAO; @@ -13,7 +13,7 @@ public class LeaderboardPanelMock extends LeaderboardPanel { public LeaderboardPanelMock(SailingServiceAsync sailingService, String leaderboardName, ErrorReporter errorReporter, - StringConstants stringConstants) { + StringMessages stringConstants) { super(sailingService, leaderboardName, errorReporter, stringConstants); // TODO Auto-generated constructor stub } diff --git a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs index b425b7953a9..3d1e6b08933 100755 --- a/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs +++ b/java/com.sap.sailing.gwt.ui/.settings/org.eclipse.core.resources.prefs @@ -1,3 +1,3 @@ -#Fri Jan 13 22:19:08 CET 2012 +#Fri Jan 13 22:25:34 CET 2012 eclipse.preferences.version=1 -encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties=UTF-8 +encoding//src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties=UTF-8 diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractEventManagementPanel.java index 607c8e892b2..fd0697d88d2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/AbstractEventManagementPanel.java @@ -8,7 +8,7 @@ import com.google.gwt.text.client.DateTimeFormatRenderer; import com.google.gwt.user.client.ui.FormPanel; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.EventDAO; public abstract class AbstractEventManagementPanel extends FormPanel implements EventDisplayer{ @@ -18,10 +18,10 @@ public abstract class AbstractEventManagementPanel extends FormPanel implements protected final TrackedEventsComposite trackedEventsComposite; protected final EventRefresher eventRefresher; protected ErrorReporter errorReporter; - protected StringConstants stringConstants; + protected StringMessages stringConstants; public AbstractEventManagementPanel(SailingServiceAsync sailingService, - EventRefresher eventRefresher, ErrorReporter errorReporter, StringConstants stringConstants) { + EventRefresher eventRefresher, ErrorReporter errorReporter, StringMessages stringConstants) { super(); this.sailingService = sailingService; this.eventRefresher = eventRefresher; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateSwissTimingRacePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateSwissTimingRacePanel.java index b2a88070068..9c273d19fdf 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateSwissTimingRacePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/CreateSwissTimingRacePanel.java @@ -24,7 +24,7 @@ import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; public class CreateSwissTimingRacePanel extends FormPanel { private TextBox txtbRaceName; @@ -40,7 +40,7 @@ public class CreateSwissTimingRacePanel extends FormPanel { private final SailingServiceAsync service; private ErrorReporter errorReporter; - public CreateSwissTimingRacePanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter, final StringConstants stringConstants){ + public CreateSwissTimingRacePanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter, final StringMessages stringConstants){ this.service = sailingService; this.errorReporter = errorReporter; competitors = new ArrayList(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigImagesBarCell.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigImagesBarCell.java index 0e3a7a7bece..c7e8c1fe9a6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigImagesBarCell.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigImagesBarCell.java @@ -7,7 +7,7 @@ import com.google.gwt.safehtml.client.SafeHtmlTemplates; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.text.shared.SafeHtmlRenderer; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; public class LeaderboardConfigImagesBarCell extends ImagesBarCell { @@ -16,12 +16,12 @@ public class LeaderboardConfigImagesBarCell extends ImagesBarCell { SafeHtml cell(String name, SafeStyles styles, String title, SafeHtml value); } - public LeaderboardConfigImagesBarCell(StringConstants stringConstants) { + public LeaderboardConfigImagesBarCell(StringMessages stringConstants) { super(); this.stringConstants = stringConstants; } - public LeaderboardConfigImagesBarCell(SafeHtmlRenderer renderer, StringConstants stringConstants) { + public LeaderboardConfigImagesBarCell(SafeHtmlRenderer renderer, StringMessages stringConstants) { super(); this.stringConstants = stringConstants; } @@ -36,7 +36,7 @@ public class LeaderboardConfigImagesBarCell extends ImagesBarCell { private static final SafeHtml ICON_EDIT_SCORES = makeImage(resources.scoresIcon()); - private StringConstants stringConstants; + private StringMessages stringConstants; @Override protected void render(com.google.gwt.cell.client.Cell.Context context, SafeHtml data, SafeHtmlBuilder sb) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java index 06da6a9579f..57b3463eb52 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardConfigPanel.java @@ -46,7 +46,7 @@ import com.sap.sailing.domain.common.Util.Triple; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.EventDAO; import com.sap.sailing.gwt.ui.shared.LeaderboardDAO; import com.sap.sailing.gwt.ui.shared.RaceDAO; @@ -59,7 +59,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, private final TrackedEventsComposite trackedEventsComposite; - private final StringConstants stringConstants; + private final StringMessages stringConstants; private final SailingServiceAsync sailingService; @@ -110,7 +110,7 @@ public class LeaderboardConfigPanel extends FormPanel implements EventDisplayer, private static AnchorTemplates ANCHORTEMPLATE = GWT.create(AnchorTemplates.class); public LeaderboardConfigPanel(SailingServiceAsync sailingService, AdminConsole adminConsole, - final ErrorReporter errorReporter, StringConstants theStringConstants) { + final ErrorReporter errorReporter, StringMessages theStringConstants) { this.stringConstants = theStringConstants; this.sailingService = sailingService; leaderboardList = new ListDataProvider(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardCreateDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardCreateDialog.java index 9c7e0987329..8bddad47834 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardCreateDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardCreateDialog.java @@ -5,12 +5,12 @@ import java.util.Collection; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.LongBox; import com.sap.sailing.gwt.ui.client.ErrorReporter; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.LeaderboardDAO; public class LeaderboardCreateDialog extends LeaderboardDialog{ - public LeaderboardCreateDialog(Collection existingLeaderboards, StringConstants stringConstants, + public LeaderboardCreateDialog(Collection existingLeaderboards, StringMessages stringConstants, ErrorReporter errorReporter, AsyncCallback callback) { super(new LeaderboardDAO(), stringConstants, errorReporter, new LeaderboardDialog.LeaderboardParameterValidator(stringConstants, existingLeaderboards), callback); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDialog.java index b612c7b2e4d..b0617024c24 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardDialog.java @@ -13,12 +13,12 @@ import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.ErrorReporter; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.LeaderboardDAO; public abstract class LeaderboardDialog extends DataEntryDialog { - protected final StringConstants stringConstants; + protected final StringMessages stringConstants; protected TextBox entryField; protected LeaderboardDAO leaderboard; @@ -26,10 +26,10 @@ public abstract class LeaderboardDialog extends DataEntryDialog protected static final int MAX_NUMBER_OF_DISCARDED_RESULTS = 4; protected static class LeaderboardParameterValidator implements Validator { - protected final StringConstants stringConstants; + protected final StringMessages stringConstants; protected final Collection existingLeaderboards; - public LeaderboardParameterValidator(StringConstants stringConstants, Collection existingLeaderboards){ + public LeaderboardParameterValidator(StringMessages stringConstants, Collection existingLeaderboards){ super(); this.stringConstants = stringConstants; this.existingLeaderboards = existingLeaderboards; @@ -69,7 +69,7 @@ public abstract class LeaderboardDialog extends DataEntryDialog } } - public LeaderboardDialog(LeaderboardDAO leaderboardDAO, StringConstants stringConstants, + public LeaderboardDialog(LeaderboardDAO leaderboardDAO, StringMessages stringConstants, ErrorReporter errorReporter, LeaderboardParameterValidator validator, AsyncCallback callback) { super(stringConstants.leaderboardName(), stringConstants.leaderboardName(), stringConstants.ok(), stringConstants.cancel(), validator, callback); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardEditDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardEditDialog.java index 7804a9a3673..f443d302930 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardEditDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardEditDialog.java @@ -5,12 +5,12 @@ import java.util.Collection; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.LongBox; import com.sap.sailing.gwt.ui.client.ErrorReporter; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.LeaderboardDAO; public class LeaderboardEditDialog extends LeaderboardDialog { - public LeaderboardEditDialog(Collection otherExistingLeaderboards, LeaderboardDAO leaderboard, StringConstants stringConstants, + public LeaderboardEditDialog(Collection otherExistingLeaderboards, LeaderboardDAO leaderboard, StringMessages stringConstants, ErrorReporter errorReporter, AsyncCallback callback) { super(leaderboard, stringConstants, errorReporter, new LeaderboardDialog.LeaderboardParameterValidator( stringConstants, otherExistingLeaderboards), callback); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardRaceConfigImagesBarCell.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardRaceConfigImagesBarCell.java index d2d50bd74f7..8ef7ed015a5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardRaceConfigImagesBarCell.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/LeaderboardRaceConfigImagesBarCell.java @@ -7,23 +7,23 @@ import com.google.gwt.safehtml.client.SafeHtmlTemplates; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.text.shared.SafeHtmlRenderer; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; public class LeaderboardRaceConfigImagesBarCell extends ImagesBarCell { - private StringConstants stringConstants; + private StringMessages stringConstants; interface ImagesBarTemplates extends SafeHtmlTemplates { @SafeHtmlTemplates.Template("

{3}
") SafeHtml cell(String name, SafeStyles styles, String title, SafeHtml value); } - public LeaderboardRaceConfigImagesBarCell(StringConstants stringConstants) { + public LeaderboardRaceConfigImagesBarCell(StringMessages stringConstants) { super(); this.stringConstants = stringConstants; } - public LeaderboardRaceConfigImagesBarCell(SafeHtmlRenderer renderer, StringConstants stringConstants) { + public LeaderboardRaceConfigImagesBarCell(SafeHtmlRenderer renderer, StringMessages stringConstants) { super(); this.stringConstants = stringConstants; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceInLeaderboardDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceInLeaderboardDialog.java index d5b2862c75b..37f2beef9b9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceInLeaderboardDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceInLeaderboardDialog.java @@ -8,7 +8,7 @@ import com.google.gwt.user.client.ui.TextBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.RaceInLeaderboardDAO; public class RaceInLeaderboardDialog extends DataEntryDialog { @@ -20,10 +20,10 @@ public class RaceInLeaderboardDialog extends DataEntryDialog { - private StringConstants stringConstants; + private StringMessages stringConstants; private Collection existingRaces; - public RaceDialogValidator(StringConstants stringConstants, Collection existingRaces) { + public RaceDialogValidator(StringMessages stringConstants, Collection existingRaces) { this.stringConstants = stringConstants; this.existingRaces = existingRaces; } @@ -58,7 +58,7 @@ public class RaceInLeaderboardDialog extends DataEntryDialog existingRaces, - RaceInLeaderboardDAO raceInLeaderboard, StringConstants stringConstants, + RaceInLeaderboardDAO raceInLeaderboard, StringMessages stringConstants, AsyncCallback callback) { super(stringConstants.name(), stringConstants.name(), stringConstants.ok(), stringConstants.cancel(), new RaceDialogValidator(stringConstants, existingRaces), callback); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java index e107071bc36..7735eab48d5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapPanel.java @@ -24,7 +24,7 @@ import com.sap.sailing.domain.common.Util.Triple; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.TimeListener; import com.sap.sailing.gwt.ui.client.Timer; import com.sap.sailing.gwt.ui.leaderboard.SmallWindHistoryPanel; @@ -55,7 +55,7 @@ public class RaceMapPanel extends FormPanel implements EventDisplayer, TimeListe private final QuickRanksListBoxComposite quickRanksListBox; public RaceMapPanel(SailingServiceAsync sailingService, ErrorReporter errorReporter, - final EventRefresher eventRefresher, StringConstants stringConstants) { + final EventRefresher eventRefresher, StringMessages stringConstants) { this.sailingService = sailingService; this.errorReporter = errorReporter; this.timer = new Timer(/* delayBetweenAutoAdvancesInMilliseconds */500); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RacesListBoxPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RacesListBoxPanel.java index ff8e117ee99..958c488ef15 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RacesListBoxPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RacesListBoxPanel.java @@ -19,7 +19,7 @@ import com.google.gwt.user.client.ui.ListBox; import com.google.gwt.user.client.ui.VerticalPanel; import com.sap.sailing.domain.common.Util.Triple; import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.EventDAO; import com.sap.sailing.gwt.ui.shared.RaceDAO; import com.sap.sailing.gwt.ui.shared.RegattaDAO; @@ -29,9 +29,9 @@ public class RacesListBoxPanel extends FormPanel implements RaceSelectionProvide private final List> raceList; private final ListBox raceListBox; private final EventRefresher eventRefresher; - private final StringConstants stringConstants; + private final StringMessages stringConstants; - public RacesListBoxPanel(EventRefresher eventRefresher, StringConstants stringConstants) { + public RacesListBoxPanel(EventRefresher eventRefresher, StringMessages stringConstants) { this.eventRefresher = eventRefresher; this.stringConstants = stringConstants; this.raceSelectionChangeListeners = new HashSet(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java index ecca0214125..2e067790c22 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/SwissTimingEventManagementPanel.java @@ -36,7 +36,7 @@ import com.google.gwt.view.client.ListDataProvider; import com.google.gwt.view.client.MultiSelectionModel; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.EventDAO; import com.sap.sailing.gwt.ui.shared.SwissTimingConfigurationDAO; import com.sap.sailing.gwt.ui.shared.SwissTimingRaceRecordDAO; @@ -66,7 +66,7 @@ public class SwissTimingEventManagementPanel extends AbstractEventManagementPane private final List availableSwissTimingRaces = new ArrayList(); public SwissTimingEventManagementPanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter, - EventRefresher eventRefresher, StringConstants stringConstants) { + EventRefresher eventRefresher, StringMessages stringConstants) { super(sailingService, eventRefresher, errorReporter, stringConstants); this.errorReporter = errorReporter; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimePanel.java index 26b15f81def..f8043bf2caf 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TimePanel.java @@ -15,7 +15,7 @@ import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.VerticalPanel; import com.sap.sailing.gwt.ui.client.PlayStateListener; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.TimeListener; import com.sap.sailing.gwt.ui.client.Timer; import com.sap.ui.commons.client.SliderWidget; @@ -31,7 +31,7 @@ public class TimePanel extends FormPanel implements TimeListener, PlayStateListe private boolean delayBoxHasFocus; private final Label timeLabel; - public TimePanel(StringConstants stringConstants, Timer timer) { + public TimePanel(StringMessages stringConstants, Timer timer) { this.timer = timer; timer.addTimeListener(this); timer.addPlayStateListener(this); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java index 7f21d416d3e..a911af16577 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TracTracEventManagementPanel.java @@ -37,7 +37,7 @@ import com.google.gwt.view.client.MultiSelectionModel; import com.sap.sailing.domain.common.Util.Pair; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.EventDAO; import com.sap.sailing.gwt.ui.shared.TracTracConfigurationDAO; import com.sap.sailing.gwt.ui.shared.TracTracRaceRecordDAO; @@ -73,7 +73,7 @@ public class TracTracEventManagementPanel extends AbstractEventManagementPanel { private final List availableTracTracRaces; public TracTracEventManagementPanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter, - EventRefresher eventRefresher, StringConstants stringConstants) { + EventRefresher eventRefresher, StringMessages stringConstants) { super(sailingService, eventRefresher, errorReporter, stringConstants); this.errorReporter = errorReporter; availableTracTracRaces = new ArrayList(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedEventsComposite.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedEventsComposite.java index 9a97a0f1fa8..2b5d8d7bbef 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedEventsComposite.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/TrackedEventsComposite.java @@ -38,7 +38,7 @@ import com.sap.sailing.domain.common.Util.Triple; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.EventDAO; import com.sap.sailing.gwt.ui.shared.RaceDAO; import com.sap.sailing.gwt.ui.shared.RegattaDAO; @@ -86,7 +86,7 @@ public class TrackedEventsComposite extends FormPanel implements EventDisplayer, private Triple lastSelectedTriple; public TrackedEventsComposite(final SailingServiceAsync sailingService, final ErrorReporter errorReporter, - final EventRefresher eventRefresher, StringConstants stringConstants, boolean hasMultiSelection) { + final EventRefresher eventRefresher, StringMessages stringConstants, boolean hasMultiSelection) { if (eventRefresher == null) { throw new IllegalArgumentException("eventRefresher must not be null"); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java index aa8eb3e2ae5..700b3ec784d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/WindPanel.java @@ -59,7 +59,7 @@ import com.sap.sailing.gwt.ui.client.ColorMap; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.EventDAO; import com.sap.sailing.gwt.ui.shared.RaceDAO; import com.sap.sailing.gwt.ui.shared.RegattaDAO; @@ -72,7 +72,7 @@ public class WindPanel extends FormPanel implements EventDisplayer, WindShower, private final SailingServiceAsync sailingService; private final ErrorReporter errorReporter; private final Grid grid; - private final StringConstants stringConstants; + private final StringMessages stringConstants; private final WindSettingPanel windSettingPanel; private ColumnSortList columnSortList; private final IdentityColumn removeColumn; @@ -91,7 +91,7 @@ public class WindPanel extends FormPanel implements EventDisplayer, WindShower, private final Map stripChartSeries; public WindPanel(final SailingServiceAsync sailingService, ErrorReporter errorReporter, - EventRefresher eventRefresher, StringConstants stringConstants) { + EventRefresher eventRefresher, StringMessages stringConstants) { this.sailingService = sailingService; this.errorReporter = errorReporter; this.stringConstants = stringConstants; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractEntryPoint.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractEntryPoint.java index 80d88ba65a7..58f1cae2d84 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractEntryPoint.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/AbstractEntryPoint.java @@ -22,7 +22,7 @@ public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter { private DialogBox errorDialogBox; private HTML serverResponseLabel; private Button dialogCloseButton; - protected StringConstants stringConstants; + protected StringMessages stringConstants; /** * Create a remote service proxy to talk to the server-side sailing service. @@ -38,7 +38,7 @@ public abstract class AbstractEntryPoint implements EntryPoint, ErrorReporter { @Override public void onModuleLoad() { - stringConstants = GWT.create(StringConstants.class); + stringConstants = GWT.create(StringMessages.class); errorDialogBox = createErrorDialog(); ServiceDefTarget serviceDef = (ServiceDefTarget) sailingService; String moduleBaseURL = GWT.getModuleBaseURL(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java index b1386ea54e2..f400a77521c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java @@ -3,7 +3,7 @@ package com.sap.sailing.gwt.ui.client; import com.sap.sailing.server.api.DetailType; public class DetailTypeFormatter { - public static String format(DetailType detailType, StringConstants stringConstants) { + public static String format(DetailType detailType, StringMessages stringConstants) { switch (detailType) { case DISTANCE_TRAVELED: return stringConstants.distanceInMeters(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LogoAndTitlePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LogoAndTitlePanel.java index dd4a9d3aec1..3037daa3691 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LogoAndTitlePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/LogoAndTitlePanel.java @@ -7,7 +7,7 @@ import com.google.gwt.user.client.ui.Label; public class LogoAndTitlePanel extends FlowPanel { - public LogoAndTitlePanel(StringConstants stringConstants) { + public LogoAndTitlePanel(StringMessages stringConstants) { Anchor sapLogo = new Anchor(new SafeHtmlBuilder().appendHtmlConstant( "").toSafeHtml()); sapLogo.setHref("http://www.sap.com"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java similarity index 95% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 75eb6cc15b1..f8a6b1b6c15 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2,7 +2,7 @@ package com.sap.sailing.gwt.ui.client; import com.google.gwt.i18n.client.Messages; -public interface StringConstants extends Messages { +public interface StringMessages extends Messages { String helloWorld(); String kielWeel2011(); String stgAccount(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties similarity index 100% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants.properties rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties similarity index 100% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringConstants_de.properties rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_de.properties diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ChartsPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ChartsPanel.java index d4f5dc3d050..8c48df3d96c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ChartsPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ChartsPanel.java @@ -60,7 +60,7 @@ import com.sap.sailing.gwt.ui.client.ColorMap; import com.sap.sailing.gwt.ui.client.DetailTypeFormatter; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.CompetitorDAO; import com.sap.sailing.gwt.ui.shared.CompetitorInRaceDAO; import com.sap.sailing.gwt.ui.shared.CompetitorsAndTimePointsDAO; @@ -92,7 +92,7 @@ public class ChartsPanel extends FormPanel { private final ColorMap colorMap; private int selectedRace = 0; private int stepsToLoad = 100; - private final StringConstants stringConstants; + private final StringMessages stringConstants; private final Set competitorVisible; private VerticalPanel selectCompetitors; private PlotWithOverview plot; @@ -108,7 +108,7 @@ public class ChartsPanel extends FormPanel { private AbsolutePanel loadingPanel; public ChartsPanel(SailingServiceAsync sailingService, final List competitors, - RaceIdentifier[] races, StringConstants stringConstants, int chartWidth, int chartHeight, ErrorReporter errorReporter) { + RaceIdentifier[] races, StringMessages stringConstants, int chartWidth, int chartHeight, ErrorReporter errorReporter) { width = chartWidth; height = chartHeight; this.errorReporter = errorReporter; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/CompareCompetitorsChartDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/CompareCompetitorsChartDialog.java index 1b0b68d9236..673a9b36425 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/CompareCompetitorsChartDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/CompareCompetitorsChartDialog.java @@ -16,7 +16,7 @@ import com.google.gwt.user.client.ui.HTML; import com.google.gwt.user.client.ui.HasHorizontalAlignment; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.ChartsPanel.DataLoadedEvent; import com.sap.sailing.gwt.ui.leaderboard.ChartsPanel.DataLoadedHandler; import com.sap.sailing.gwt.ui.shared.CompetitorDAO; @@ -25,7 +25,7 @@ import com.sap.sailing.server.api.RaceIdentifier; public class CompareCompetitorsChartDialog extends DialogBox { private Anchor closeAnchor; - public CompareCompetitorsChartDialog(SailingServiceAsync sailingService, List competitors, RaceIdentifier[] races, StringConstants stringConstants, ErrorReporter errorReporter){ + public CompareCompetitorsChartDialog(SailingServiceAsync sailingService, List competitors, RaceIdentifier[] races, StringMessages stringConstants, ErrorReporter errorReporter){ super(false); final ChartsPanel ccp = new ChartsPanel(sailingService, competitors, races, stringConstants, (int) (Window.getClientWidth()-350), (int) (Window.getClientHeight()-170),errorReporter); ccp.addDataLoadedHandler(new DataLoadedHandler() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ExpandableSortableColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ExpandableSortableColumn.java index 71794b69d7f..88761fd9951 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ExpandableSortableColumn.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ExpandableSortableColumn.java @@ -12,7 +12,7 @@ import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Header; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.LeaderboardRowDAO; import com.sap.sailing.server.api.DetailType; @@ -45,7 +45,7 @@ public abstract class ExpandableSortableColumn extends SortableColumn cell, - StringConstants stringConstants, String detailHeaderStyle, String detailColumnStyle, + StringMessages stringConstants, String detailHeaderStyle, String detailColumnStyle, List detailSelection) { super(cell); this.enableExpansion = enableExpansion; @@ -59,7 +59,7 @@ public abstract class ExpandableSortableColumn extends SortableColumn> getDetailColumnMap( - LeaderboardPanel leaderboardPanel, StringConstants stringConstants, String detailHeaderStyle, + LeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, String detailColumnStyle) { return Collections.emptyMap(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index b35a7826426..fb7b2e9476e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -39,7 +39,7 @@ import com.sap.sailing.gwt.ui.client.Collator; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.PlayStateListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.TimeListener; import com.sap.sailing.gwt.ui.client.Timer; import com.sap.sailing.gwt.ui.leaderboard.LegDetailColumn.LegDetailField; @@ -80,7 +80,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta private final ErrorReporter errorReporter; - private final StringConstants stringConstants; + private final StringMessages stringConstants; private final CellTable leaderboardTable; @@ -141,9 +141,9 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta */ private final Anchor playPause; private class SettingsClickHandler implements ClickHandler { - private final StringConstants stringConstants; + private final StringMessages stringConstants; - private SettingsClickHandler(StringConstants stringConstants) { + private SettingsClickHandler(StringMessages stringConstants) { this.stringConstants = stringConstants; } @@ -428,7 +428,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta @Override protected Map> getDetailColumnMap( - LeaderboardPanel leaderboardPanel, StringConstants stringConstants, String detailHeaderStyle, + LeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, String detailColumnStyle) { Map> result = new HashMap>(); result.put(DetailType.RACE_DISTANCE_TRAVELED, @@ -678,7 +678,7 @@ public class LeaderboardPanel extends FormPanel implements TimeListener, PlaySta } public LeaderboardPanel(SailingServiceAsync sailingService, String leaderboardName, ErrorReporter errorReporter, - final StringConstants stringConstants) { + final StringMessages stringConstants) { this.sailingService = sailingService; this.setLeaderboardName(leaderboardName); this.errorReporter = errorReporter; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java index a5c5dbae956..1218efe4d97 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsDialogComponent.java @@ -15,7 +15,7 @@ import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; import com.sap.sailing.gwt.ui.client.DataEntryDialog.Validator; import com.sap.sailing.gwt.ui.client.DetailTypeFormatter; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.shared.components.SettingsDialogComponent; import com.sap.sailing.server.api.DetailType; @@ -29,7 +29,7 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone private final Map maneuverDetailCheckboxes; private final Map legDetailCheckboxes; private final Map raceDetailCheckboxes; - private final StringConstants stringConstants; + private final StringMessages stringConstants; private LongBox delayBetweenAutoAdvancesInSecondsBox; private LongBox delayInSecondsBox; private final long delayBetweenAutoAdvancesInMilliseconds; @@ -38,7 +38,7 @@ public class LeaderboardSettingsDialogComponent implements SettingsDialogCompone public LeaderboardSettingsDialogComponent(List maneuverDetailSelection, List legDetailSelection, List raceDetailSelection, List raceAllRaceColumns, List raceColumnSelection, long delayBetweenAutoAdvancesInMilliseconds, long delayInMilliseconds, - StringConstants stringConstants) { + StringMessages stringConstants) { this.maneuverDetailSelection = maneuverDetailSelection; this.raceColumnSelection = raceColumnSelection; this.raceAllRaceColumns = raceAllRaceColumns; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java index e4672bc0571..f3a592ba68b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LegColumn.java @@ -9,7 +9,7 @@ import com.google.gwt.cell.client.TextCell; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Header; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.LegDetailColumn.LegDetailField; import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDAO; import com.sap.sailing.gwt.ui.shared.LeaderboardRowDAO; @@ -26,7 +26,7 @@ import com.sap.sailing.server.api.DetailType; public class LegColumn extends ExpandableSortableColumn { private final String raceName; private final int legIndex; - private final StringConstants stringConstants; + private final StringMessages stringConstants; private final String headerStyle; private final String columnStyle; @@ -188,7 +188,7 @@ public class LegColumn extends ExpandableSortableColumn { } } - public LegColumn(LeaderboardPanel leaderboardPanel, String raceName, int legIndex, StringConstants stringConstants, + public LegColumn(LeaderboardPanel leaderboardPanel, String raceName, int legIndex, StringMessages stringConstants, List legDetailSelection, String headerStyle, String columnStyle, String detailHeaderStyle, String detailColumnStyle) { super(leaderboardPanel, /* expandable */true /* all legs have details */, new TextCell(), stringConstants, @@ -213,7 +213,7 @@ public class LegColumn extends ExpandableSortableColumn { @Override protected Map> getDetailColumnMap( - LeaderboardPanel leaderboardPanel, StringConstants stringConstants, String detailHeaderStyle, + LeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, String detailColumnStyle) { Map> result = new HashMap>(); result.put(DetailType.DISTANCE_TRAVELED, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ManeuverCountRaceColumn.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ManeuverCountRaceColumn.java index c9e3e8fc029..5967ae8441c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ManeuverCountRaceColumn.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/ManeuverCountRaceColumn.java @@ -10,7 +10,7 @@ import com.google.gwt.cell.client.TextCell; import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlBuilder; import com.google.gwt.user.cellview.client.Header; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.LegDetailColumn.LegDetailField; import com.sap.sailing.gwt.ui.shared.LeaderboardDAO; import com.sap.sailing.gwt.ui.shared.LeaderboardEntryDAO; @@ -21,7 +21,7 @@ import com.sap.sailing.domain.common.Util.Triple; public class ManeuverCountRaceColumn extends ExpandableSortableColumn implements HasStringAndDoubleValue { - private final StringConstants stringConstants; + private final StringMessages stringConstants; private final RaceNameProvider raceNameProvider; private final String headerStyle; @@ -68,7 +68,7 @@ public class ManeuverCountRaceColumn extends ExpandableSortableColumn im } public ManeuverCountRaceColumn(LeaderboardPanel leaderboardPanel, RaceNameProvider raceNameProvider, - StringConstants stringConstants, List maneuverDetailSelection, String headerStyle, + StringMessages stringConstants, List maneuverDetailSelection, String headerStyle, String columnStylee, String detailHeaderStyle, String detailColumnStyle) { super(leaderboardPanel, /* expandable */true /* all legs have details */, new TextCell(), stringConstants, detailHeaderStyle, detailColumnStyle, maneuverDetailSelection); @@ -254,7 +254,7 @@ public class ManeuverCountRaceColumn extends ExpandableSortableColumn im @Override protected Map> getDetailColumnMap( - LeaderboardPanel leaderboardPanel, StringConstants stringConstants, String detailHeaderStyle, + LeaderboardPanel leaderboardPanel, StringMessages stringConstants, String detailHeaderStyle, String detailColumnStyle) { Map> result = new HashMap>(); result.put(DetailType.TACK, new FormattedDoubleLegDetailColumn(stringConstants.tack(), "", new NumberOfTacks(), diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SmallWindHistoryPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SmallWindHistoryPanel.java index 9f46ea9f0a0..16d12c35ab5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SmallWindHistoryPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SmallWindHistoryPanel.java @@ -14,7 +14,7 @@ import com.sap.sailing.gwt.ui.adminconsole.WindIndicator; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.RaceSelectionChangeListener; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.TimeListener; import com.sap.sailing.gwt.ui.shared.EventDAO; import com.sap.sailing.gwt.ui.shared.PositionDAO; @@ -40,7 +40,7 @@ public class SmallWindHistoryPanel extends FormPanel implements TimeListener, Ra private final SailingServiceAsync sailingService; private final long millisecondStepsPerLabel; private final ErrorReporter errorReporter; - private final StringConstants stringConstants; + private final StringMessages stringConstants; /** * @param position @@ -52,7 +52,7 @@ public class SmallWindHistoryPanel extends FormPanel implements TimeListener, Ra * the wind track to visualize using this panel */ public SmallWindHistoryPanel(SailingServiceAsync sailingService, PositionDAO position, - int numberOfTimepoints, long millisecondStepsPerLabel, StringConstants stringConstants, ErrorReporter errorReporter) { + int numberOfTimepoints, long millisecondStepsPerLabel, StringMessages stringConstants, ErrorReporter errorReporter) { this.sailingService = sailingService; this.position = position; this.stringConstants = stringConstants; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SortableExpandableColumnHeader.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SortableExpandableColumnHeader.java index be09ac8633a..24e1fee6f3b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SortableExpandableColumnHeader.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/SortableExpandableColumnHeader.java @@ -22,7 +22,7 @@ import com.google.gwt.safehtml.shared.SafeHtml; import com.google.gwt.safehtml.shared.SafeHtmlUtils; import com.google.gwt.user.cellview.client.CellTable; import com.google.gwt.user.cellview.client.Header; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; /** * A {@link CellTable} {@link Header} implementation that uses a {@link CompositeCell} containing a @@ -51,12 +51,12 @@ public class SortableExpandableColumnHeader extends Header { private static RaceCellTemplates template = GWT.create(RaceCellTemplates.class); public SortableExpandableColumnHeader(String title, String iconURL, LeaderboardPanel leaderboardPanel, - ExpandableSortableColumn column, StringConstants stringConstants) { + ExpandableSortableColumn column, StringMessages stringConstants) { super(constructCell(title, iconURL, column.isExpansionEnabled(), leaderboardPanel, column, stringConstants)); } private static Cell constructCell(final String title, final String iconURL, boolean isExpansionEnabled, - final LeaderboardPanel leaderboardPanel, final ExpandableSortableColumn column, final StringConstants stringConstants) { + final LeaderboardPanel leaderboardPanel, final ExpandableSortableColumn column, final StringMessages stringConstants) { final List> cells = new ArrayList>(3); // if it's a medal race, add the cell rendering the medal image // add the cell rendering the expand/collapse button: diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java index 890cbd95abc..4dfbdbb72dc 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/EditableLeaderboardPanel.java @@ -16,7 +16,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.sap.sailing.domain.common.Util.Pair; import com.sap.sailing.gwt.ui.client.ErrorReporter; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.leaderboard.LeaderboardPanel; import com.sap.sailing.gwt.ui.shared.CompetitorDAO; import com.sap.sailing.gwt.ui.shared.LeaderboardDAO; @@ -247,7 +247,7 @@ public class EditableLeaderboardPanel extends LeaderboardPanel { } public EditableLeaderboardPanel(SailingServiceAsync sailingService, String leaderboardName, - ErrorReporter errorReporter, StringConstants stringConstants) { + ErrorReporter errorReporter, StringMessages stringConstants) { super(sailingService, leaderboardName, errorReporter, stringConstants); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java index 0b7868ef193..41b22435ad2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/components/SettingsDialog.java @@ -4,12 +4,12 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.FocusWidget; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.ui.client.DataEntryDialog; -import com.sap.sailing.gwt.ui.client.StringConstants; +import com.sap.sailing.gwt.ui.client.StringMessages; public class SettingsDialog extends DataEntryDialog { private final SettingsDialogComponent settingsDialogComponent; - public SettingsDialog(final Component component, StringConstants stringConstants) { + public SettingsDialog(final Component component, StringMessages stringConstants) { super(stringConstants.settingsForComponent(component.getLocalizedShortName()), stringConstants.settingsForComponent(component.getLocalizedShortName()), stringConstants.ok(), stringConstants.cancel(), component.getSettingsDialogComponent().getValidator(), From 1f799dd70e40176119a1f93cc7db9d369d539001 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 13 Jan 2012 23:56:29 +0100 Subject: [PATCH 29/33] boats now zoom when map is zoomed, also when paused --- .../sailing/gwt/ui/adminconsole/RaceMap.java | 73 ++++++++++++------- 1 file changed, 46 insertions(+), 27 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java index 60429714b69..60ecda19553 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java @@ -203,6 +203,16 @@ public class RaceMap implements TimeListener, CompetitorSelectionChangeListener, @Override public void onZoomEnd(MapZoomEndEvent event) { mapZoomedOrPannedSinceLastRaceSelectionChange = true; + Set competitorDAOsOfUnusedMarkers = new HashSet(boatMarkers.keySet()); + for (CompetitorDAO competitorDAO : getCompetitorsToShow()) { + boolean usedExistingMarker = updateMarkerForCompetitor(competitorDAO); + if (usedExistingMarker) { + competitorDAOsOfUnusedMarkers.remove(competitorDAO); + } + } + for (CompetitorDAO unusedMarkerCompetitorDAO : competitorDAOsOfUnusedMarkers) { + map.removeOverlay(boatMarkers.remove(unusedMarkerCompetitorDAO)); + } } }); map.addMapDragEndHandler(new MapDragEndHandler() { @@ -498,33 +508,9 @@ public class RaceMap implements TimeListener, CompetitorSelectionChangeListener, newMapBounds.extend(bounds.getNorthEast()); newMapBounds.extend(bounds.getSouthWest()); } - if (lastShownFix.containsKey(competitorDAO) && lastShownFix.get(competitorDAO) != -1) { - GPSFixDAO lastPos = getBoatFix(competitorDAO); - Marker boatMarker = boatMarkers.get(competitorDAO); - if (boatMarker == null) { - boatMarker = createBoatMarker(competitorDAO, false); - map.addOverlay(boatMarker); - boatMarkers.put(competitorDAO, boatMarker); - } else { - competitorDAOsOfUnusedMarkers.remove(competitorDAO); - // check if anchors match; re-use marker with setImage only if anchors match - Point newAnchor = imageResources.getBoatImageTransformator(lastPos, - selectedMapCompetitors.contains(competitorDAO)).getAnchor(); - Point oldAnchor = boatMarker.getIcon().getIconAnchor(); - if (oldAnchor.getX() == newAnchor.getX() && oldAnchor.getY() == newAnchor.getY()) { - boatMarker.setLatLng(LatLng.newInstance(lastPos.position.latDeg, - lastPos.position.lngDeg)); - boatMarker.setImage(imageResources.getBoatImageURL(lastPos, - selectedMapCompetitors.contains(competitorDAO))); - } else { - // anchors don't match; replace marker - map.removeOverlay(boatMarker); - boatMarker = createBoatMarker(competitorDAO, - selectedMapCompetitors.contains(competitorDAO)); - map.addOverlay(boatMarker); - boatMarkers.put(competitorDAO, boatMarker); - } - } + boolean usedExistingMarker = updateMarkerForCompetitor(competitorDAO); + if (usedExistingMarker) { + competitorDAOsOfUnusedMarkers.remove(competitorDAO); } } } @@ -541,6 +527,39 @@ public class RaceMap implements TimeListener, CompetitorSelectionChangeListener, } } } + + private boolean updateMarkerForCompetitor(CompetitorDAO competitorDAO) { + boolean usedExistingMarker = false; + if (lastShownFix.containsKey(competitorDAO) && lastShownFix.get(competitorDAO) != -1) { + GPSFixDAO lastPos = getBoatFix(competitorDAO); + Marker boatMarker = boatMarkers.get(competitorDAO); + if (boatMarker == null) { + boatMarker = createBoatMarker(competitorDAO, false); + map.addOverlay(boatMarker); + boatMarkers.put(competitorDAO, boatMarker); + } else { + usedExistingMarker = true; + // check if anchors match; re-use marker with setImage only if anchors match + Point newAnchor = imageResources.getBoatImageTransformator(lastPos, + selectedMapCompetitors.contains(competitorDAO)).getAnchor(); + Point oldAnchor = boatMarker.getIcon().getIconAnchor(); + if (oldAnchor.getX() == newAnchor.getX() && oldAnchor.getY() == newAnchor.getY()) { + boatMarker.setLatLng(LatLng.newInstance(lastPos.position.latDeg, + lastPos.position.lngDeg)); + boatMarker.setImage(imageResources.getBoatImageURL(lastPos, + selectedMapCompetitors.contains(competitorDAO))); + } else { + // anchors don't match; replace marker + map.removeOverlay(boatMarker); + boatMarker = createBoatMarker(competitorDAO, + selectedMapCompetitors.contains(competitorDAO)); + map.addOverlay(boatMarker); + boatMarkers.put(competitorDAO, boatMarker); + } + } + } + return usedExistingMarker; + } protected Marker createBuoyMarker(final MarkDAO markDAO) { MarkerOptions options = MarkerOptions.newInstance(); From 6a5921717b62098a3a1df88d5359d3894135b14c Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 14 Jan 2012 00:45:08 +0100 Subject: [PATCH 30/33] pass scale to each and every operation of ImageTransformer to avoid confusion about its internal state --- ...ansformator.java => ImageTransformer.java} | 33 ++++++---- .../sailing/gwt/ui/adminconsole/RaceMap.java | 5 +- .../gwt/ui/adminconsole/RaceMapResources.java | 64 +++++++++---------- 3 files changed, 52 insertions(+), 50 deletions(-) rename java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/{ImageTransformator.java => ImageTransformer.java} (75%) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageTransformator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageTransformer.java similarity index 75% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageTransformator.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageTransformer.java index 71483654f59..0a1df8a1b85 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageTransformator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/ImageTransformer.java @@ -16,7 +16,7 @@ import com.google.gwt.user.client.ui.Image; * @author Axel Uhl (d043530) * */ -public class ImageTransformator { +public class ImageTransformer { private final String untransformedImageURL; private final Canvas canvas; private final int imageWidth; @@ -25,16 +25,18 @@ public class ImageTransformator { private int canvasWidth; private int canvasHeight; + private double currentScale; + private ImageElement imageElement; private Context2d context; - public ImageTransformator(ImageResource untransformedImage) { + public ImageTransformer(ImageResource untransformedImage) { this.untransformedImageURL = untransformedImage.getSafeUri().asString(); canvas = Canvas.createIfSupported(); imageWidth = untransformedImage.getWidth(); imageHeight = untransformedImage.getHeight(); - calculateCanvasSize(1.0); + scale(1.0); final Image image = new Image(untransformedImageURL.toString()); imageElement = (ImageElement) image.getElement().cast(); if (imageElement == null) { @@ -47,18 +49,21 @@ public class ImageTransformator { } } - private void calculateCanvasSize(double scaleFactor) - { - canvasWidth = (int) Math.round(imageWidth * scaleFactor); - canvasHeight = (int) Math.round(imageHeight * scaleFactor); - canvasRadius = (int) Math.sqrt(canvasWidth*canvasWidth/4 + canvasHeight*canvasHeight/4); - canvas.setSize(""+2*canvasRadius+"px", ""+2*canvasRadius+"px"); - canvas.setCoordinateSpaceWidth(2*canvasRadius); - canvas.setCoordinateSpaceHeight(2*canvasRadius); - context = canvas.getContext2d(); + private void scale(double scaleFactor) { + if (scaleFactor != currentScale) { + canvasWidth = (int) Math.round(imageWidth * scaleFactor); + canvasHeight = (int) Math.round(imageHeight * scaleFactor); + canvasRadius = (int) Math.sqrt(canvasWidth * canvasWidth / 4 + canvasHeight * canvasHeight / 4); + canvas.setSize("" + 2 * canvasRadius + "px", "" + 2 * canvasRadius + "px"); + canvas.setCoordinateSpaceWidth(2 * canvasRadius); + canvas.setCoordinateSpaceHeight(2 * canvasRadius); + context = canvas.getContext2d(); + currentScale = scaleFactor; + } } - public Point getAnchor() { + public Point getAnchor(double scaleFactor) { + scale(scaleFactor); return Point.newInstance(canvasRadius, canvasRadius); } @@ -74,8 +79,8 @@ public class ImageTransformator { String result = getUntransformedImageURL(); if (canvas != null) { if (imageElement != null) { + scale(scaleFactor); double angleInRadians = angleInDegrees/180.*Math.PI; - calculateCanvasSize(scaleFactor); context.clearRect(0, 0, 2*canvasRadius, 2*canvasRadius); context.save(); context.translate(canvasRadius, canvasRadius); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java index 60ecda19553..4abeb7983a4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java @@ -540,8 +540,9 @@ public class RaceMap implements TimeListener, CompetitorSelectionChangeListener, } else { usedExistingMarker = true; // check if anchors match; re-use marker with setImage only if anchors match - Point newAnchor = imageResources.getBoatImageTransformator(lastPos, - selectedMapCompetitors.contains(competitorDAO)).getAnchor(); + ImageTransformer transformer = imageResources.getBoatImageTransformer(lastPos, + selectedMapCompetitors.contains(competitorDAO)); + Point newAnchor = transformer.getAnchor(imageResources.getRealBoatSizeScaleFactor()); Point oldAnchor = boatMarker.getIcon().getIconAnchor(); if (oldAnchor.getX() == newAnchor.getX() && oldAnchor.getY() == newAnchor.getY()) { boatMarker.setLatLng(LatLng.newInstance(lastPos.position.latDeg, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapResources.java index 0d981cc7971..e24f3b2a67a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMapResources.java @@ -17,42 +17,42 @@ public class RaceMapResources { /** * Two sails on downwind leg, wind from port (sails on starboard); no highlighting */ - protected ImageTransformator boatIconDownwindPortRotator; + protected ImageTransformer boatIconDownwindPortRotator; /** * Two sails on downwind leg, wind from port (sails on starboard); with highlighting */ - protected ImageTransformator boatIconHighlightedDownwindPortRotator; + protected ImageTransformer boatIconHighlightedDownwindPortRotator; /** * Two sails on downwind leg, wind from starboard (sails on port); no highlighting */ - protected ImageTransformator boatIconDownwindStarboardRotator; + protected ImageTransformer boatIconDownwindStarboardRotator; /** * Two sails on downwind leg, wind from starboard (sails on port); with highlighting */ - protected ImageTransformator boatIconHighlightedDownwindStarboardRotator; + protected ImageTransformer boatIconHighlightedDownwindStarboardRotator; /** * One sail, wind from port (sails on starboard); no highlighting */ - protected ImageTransformator boatIconPortRotator; + protected ImageTransformer boatIconPortRotator; /** * One sail, wind from port (sails on starboard); with highlighting */ - protected ImageTransformator boatIconHighlightedPortRotator; + protected ImageTransformer boatIconHighlightedPortRotator; /** * One sail, wind from starboard (sails on port); no highlighting */ - protected ImageTransformator boatIconStarboardRotator; + protected ImageTransformer boatIconStarboardRotator; /** * One sail, wind from starboard (sails on port); with highlighting */ - protected ImageTransformator boatIconHighlightedStarboardRotator; + protected ImageTransformer boatIconHighlightedStarboardRotator; protected Icon buoyIcon; protected Icon tackToStarboardIcon; @@ -75,15 +75,15 @@ public class RaceMapResources { { this.map = map; - boatIconDownwindPortRotator = new ImageTransformator(resources.lowlightedBoatIconDW_Port()); - boatIconHighlightedDownwindPortRotator = new ImageTransformator(resources.highlightedBoatIconDW_Port()); - boatIconDownwindStarboardRotator = new ImageTransformator(resources.lowlightedBoatIconDW_Starboard()); - boatIconHighlightedDownwindStarboardRotator = new ImageTransformator(resources + boatIconDownwindPortRotator = new ImageTransformer(resources.lowlightedBoatIconDW_Port()); + boatIconHighlightedDownwindPortRotator = new ImageTransformer(resources.highlightedBoatIconDW_Port()); + boatIconDownwindStarboardRotator = new ImageTransformer(resources.lowlightedBoatIconDW_Starboard()); + boatIconHighlightedDownwindStarboardRotator = new ImageTransformer(resources .highlightedBoatIconDW_Starboard()); - boatIconPortRotator = new ImageTransformator(resources.lowlightedBoatIcon_Port()); - boatIconHighlightedPortRotator = new ImageTransformator(resources.highlightedBoatIcon_Port()); - boatIconStarboardRotator = new ImageTransformator(resources.lowlightedBoatIcon_Starboard()); - boatIconHighlightedStarboardRotator = new ImageTransformator(resources.highlightedBoatIcon_Starboard()); + boatIconPortRotator = new ImageTransformer(resources.lowlightedBoatIcon_Port()); + boatIconHighlightedPortRotator = new ImageTransformer(resources.highlightedBoatIcon_Port()); + boatIconStarboardRotator = new ImageTransformer(resources.lowlightedBoatIcon_Starboard()); + boatIconHighlightedStarboardRotator = new ImageTransformer(resources.highlightedBoatIcon_Starboard()); buoyIcon = Icon.newInstance(resources.buoyIcon().getSafeUri().asString()); buoyIcon.setIconAnchor(Point.newInstance(4, 4)); tackToStarboardIcon = Icon @@ -127,7 +127,7 @@ public class RaceMapResources { penaltyCircleToPortIcon.setIconAnchor(Point.newInstance(10, 33)); } - public ImageTransformator getBoatImageTransformator(GPSFixDAO boatFix, boolean highlighted) { + public ImageTransformer getBoatImageTransformer(GPSFixDAO boatFix, boolean highlighted) { if (boatFix.tack == Tack.PORT) { if (LegType.DOWNWIND == boatFix.legType) { if (highlighted) { @@ -160,45 +160,41 @@ public class RaceMapResources { } public String getBoatImageURL(GPSFixDAO boatFix, boolean highlighted) { - return getBoatImageURL(getBoatImageTransformator(boatFix, highlighted), boatFix); + return getBoatImageURL(getBoatImageTransformer(boatFix, highlighted), boatFix); } - public String getBoatImageURL(ImageTransformator boatImageTransformator, GPSFixDAO boatFix) { + public String getBoatImageURL(ImageTransformer boatImageTransformer, GPSFixDAO boatFix) { + double realBoatSizeScaleFactor = getRealBoatSizeScaleFactor(); + return boatImageTransformer.getTransformedImageURL(boatFix.speedWithBearing.bearingInDegrees, realBoatSizeScaleFactor); + } + + public double getRealBoatSizeScaleFactor() { // the possible zoom level range is 0 to 21 (zoom level 0 would show the whole world) int zoomLevel = map.getZoomLevel(); double minScaleFactor = 0.33; double realBoatSizeScaleFactor = minScaleFactor; double boatLengthInMeter = 5.0; - if(zoomLevel > 5) { int boatSizeXInPixel = 41; - // int boatSizeYInPixel = 21; - LatLngBounds bounds = map.getBounds(); LatLng upperRight = bounds.getNorthEast(); LatLng bottomLeft = bounds.getSouthWest(); LatLng upperLeft = LatLng.newInstance(upperRight.getLatitude(), bottomLeft.getLongitude()); - double distXInMeters = upperLeft.distanceFrom(upperRight); - // double distYInMeters = upperLeft.distanceFrom(bottomLeft); - int widthInPixel = map.getSize().getWidth(); - // int heightInPixel = map.getSize().getHeight(); - double realBoatSizeInPixel = (widthInPixel * boatLengthInMeter) / distXInMeters; realBoatSizeScaleFactor = realBoatSizeInPixel / (double) boatSizeXInPixel; - - if(realBoatSizeScaleFactor < minScaleFactor) + if(realBoatSizeScaleFactor < minScaleFactor) { realBoatSizeScaleFactor = minScaleFactor; + } } - - return boatImageTransformator.getTransformedImageURL(boatFix.speedWithBearing.bearingInDegrees, realBoatSizeScaleFactor); + return realBoatSizeScaleFactor; } public Icon getBoatImageIcon(GPSFixDAO boatFix, boolean highlighted) { - ImageTransformator boatImageTransformator = getBoatImageTransformator(boatFix, highlighted); - Icon icon = Icon.newInstance(getBoatImageURL(boatImageTransformator, boatFix)); - icon.setIconAnchor(boatImageTransformator.getAnchor()); + ImageTransformer boatImageTransformer = getBoatImageTransformer(boatFix, highlighted); + Icon icon = Icon.newInstance(getBoatImageURL(boatImageTransformer, boatFix)); + icon.setIconAnchor(boatImageTransformer.getAnchor(getRealBoatSizeScaleFactor())); return icon; } } From 4d9ec8e0508638a3fd64aa14e31114bc41665e19 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 14 Jan 2012 02:13:15 +0100 Subject: [PATCH 31/33] avoid unnecessary logging-related computations using Logger.isLoggable --- .../sailing/domain/tracking/impl/TrackedRaceImpl.java | 9 +++++++-- 1 file changed, 7 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index ef32ecdc3ac..5b3def75a27 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -9,6 +9,7 @@ import java.util.List; import java.util.Map; import java.util.NavigableSet; import java.util.concurrent.ConcurrentSkipListSet; +import java.util.logging.Level; import java.util.logging.Logger; import com.sap.sailing.domain.base.Bearing; @@ -876,8 +877,12 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener { break; default: maneuverType = Type.UNKNOWN; - logger.fine("Unknown maneuver for " + competitor + " at " + maneuverTimePoint - + (legBeforeManeuver != null ? " on reaching leg " + legBeforeManeuver.getLeg() : " before start")); + if (logger.isLoggable(Level.FINE)) { + logger.fine("Unknown maneuver for " + + competitor + " at " + maneuverTimePoint + + (legBeforeManeuver != null ? " on reaching leg " + legBeforeManeuver.getLeg() + : " before start")); + } break; } } else { From 0498463db2797aa11f808c29ffaf7b9b6f0c1db3 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sun, 15 Jan 2012 00:31:03 +0100 Subject: [PATCH 32/33] reduced tail width to 1 upon request from Marcus Baur --- .../main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java index 4abeb7983a4..ffde8824428 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/adminconsole/RaceMap.java @@ -744,7 +744,7 @@ public class RaceMap implements TimeListener, CompetitorSelectionChangeListener, } PolylineOptions options = PolylineOptions.newInstance( /* clickable */true, /* geodesic */true); - Polyline result = new Polyline(points.toArray(new LatLng[0]), getColorString(competitorDAO), /* width */3, + Polyline result = new Polyline(points.toArray(new LatLng[0]), getColorString(competitorDAO), /* width */ 1, /* opacity */0.5, options); result.addPolylineClickHandler(new PolylineClickHandler() { @Override From e73be8199acd4a50d712ffacdd4f19ff72b799ba Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 16 Jan 2012 11:10:26 +0100 Subject: [PATCH 33/33] refactored confidence stuff such that confidence is now externally attached to existing objects --- .../sailing/domain/test/ConfidenceTest.java | 2 +- .../WindEstimationOnStoredTracksTest.java | 33 ++++---- .../domain/base/BearingWithConfidence.java | 4 +- .../base/SpeedWithBearingWithConfidence.java | 5 +- .../domain/base/SpeedWithConfidence.java | 3 +- .../domain/base/impl/AbstractBearing.java | 61 +------------- .../impl/AbstractSpeedWithBearingImpl.java | 66 +-------------- .../impl/AbstractSpeedWithConfidence.java | 43 ---------- .../base/impl/BearingWithConfidenceImpl.java | 77 ++++++++++++++++++ .../domain/base/impl/HasConfidenceImpl.java | 16 ++++ .../base/impl/KilometersPerHourSpeedImpl.java | 23 +----- .../domain/base/impl/KnotSpeedImpl.java | 22 +---- .../base/impl/KnotSpeedWithBearingImpl.java | 12 --- .../SpeedWithBearingWithConfidenceImpl.java | 80 +++++++++++++++++++ .../domain/confidence/HasConfidence.java | 2 +- .../domain/confidence/ScalableValue.java | 2 +- .../impl/ConfidenceBasedAveragerImpl.java | 4 +- .../confidence/impl/ScalableDouble.java | 2 +- .../domain/tracking/impl/BearingCluster.java | 5 ++ .../impl/BearingWithConfidenceCluster.java | 10 ++- .../impl/CompactGPSFixMovingImpl.java | 5 -- .../tracking/impl/GenericBearingCluster.java | 25 +++--- 22 files changed, 236 insertions(+), 266 deletions(-) delete mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithConfidence.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/BearingWithConfidenceImpl.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/HasConfidenceImpl.java create mode 100755 java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SpeedWithBearingWithConfidenceImpl.java diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java index efd019c925a..a11df3753a5 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ConfidenceTest.java @@ -49,7 +49,7 @@ public class ConfidenceTest { } @Override - public Bearing divide(double divisor) { + public Bearing divide(double divisor, double confidence) { double angle; if (cos == 0) { angle = sin >= 0 ? Math.PI / 2 : -Math.PI / 2; diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnStoredTracksTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnStoredTracksTest.java index 10ae5a3f70e..6be3b64d785 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnStoredTracksTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnStoredTracksTest.java @@ -14,6 +14,7 @@ import com.sap.sailing.domain.base.Bearing; import com.sap.sailing.domain.base.BearingWithConfidence; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.TimePoint; +import com.sap.sailing.domain.base.impl.BearingWithConfidenceImpl; import com.sap.sailing.domain.base.impl.DegreeBearingImpl; import com.sap.sailing.domain.base.impl.MillisecondsTimePoint; import com.sap.sailing.domain.common.NoWindException; @@ -39,7 +40,7 @@ public class WindEstimationOnStoredTracksTest extends StoredTrackBasedTestWithTr 27.2860810183163, 319.47157698009613, 325.1617832132204, 31.678409742672212, 35.00547108150359, 23.934778873669256, 29.76599976685808, 33.19487072661667, 19.0, 33.29318052266396, 32.7371445230587, 38.26627143611533 }) { - cluster.add(new DegreeBearingImpl(bearingInDegrees)); + cluster.add(new BearingWithConfidenceImpl(new DegreeBearingImpl(bearingInDegrees), /* confidence */ 0.9)); } BearingWithConfidenceCluster[] splitResult = cluster.splitInTwo(/* minimumDegreeDifferenceBetweenTacks */ 15); assertEquals(2, splitResult.length); @@ -54,55 +55,55 @@ public class WindEstimationOnStoredTracksTest extends StoredTrackBasedTestWithTr @Test public void testBearingWithConfidenceClusterAverageAcrossZero() { BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); - BearingWithConfidence b1 = new DegreeBearingImpl(355); - BearingWithConfidence b2 = new DegreeBearingImpl(5); + BearingWithConfidence b1 = new BearingWithConfidenceImpl(new DegreeBearingImpl(355), /* confidence */ 0.9); + BearingWithConfidence b2 = new BearingWithConfidenceImpl(new DegreeBearingImpl(5), /* confidence */ 0.9); cluster.add(b1); cluster.add(b2); - BearingWithConfidence average = cluster.getAverage(); + Bearing average = cluster.getAverage(); assertEquals(0, average.getDegrees(), 0.00000001); } @Test public void testBearingWithConfidenceClusterAverageWithZeroSinus() { BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); - BearingWithConfidence b1 = new DegreeBearingImpl(0); - BearingWithConfidence b2 = new DegreeBearingImpl(0); + BearingWithConfidence b1 = new BearingWithConfidenceImpl(new DegreeBearingImpl(0), /* confidence */ 0.9); + BearingWithConfidence b2 = new BearingWithConfidenceImpl(new DegreeBearingImpl(0), /* confidence */ 0.9); cluster.add(b1); cluster.add(b2); - BearingWithConfidence average = cluster.getAverage(); + Bearing average = cluster.getAverage(); assertEquals(0, average.getDegrees(), 0.00000001); } @Test public void testBearingWithConfidenceClusterAverageWithZeroSinusAndNegativeCosinus() { BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); - BearingWithConfidence b1 = new DegreeBearingImpl(180); - BearingWithConfidence b2 = new DegreeBearingImpl(180); + BearingWithConfidence b1 = new BearingWithConfidenceImpl(new DegreeBearingImpl(180), /* confidence */ 0.9); + BearingWithConfidence b2 = new BearingWithConfidenceImpl(new DegreeBearingImpl(180), /* confidence */ 0.9); cluster.add(b1); cluster.add(b2); - BearingWithConfidence average = cluster.getAverage(); + Bearing average = cluster.getAverage(); assertEquals(180, average.getDegrees(), 0.00000001); } @Test public void testBearingWithConfidenceClusterAverageWithZeroCosinus() { BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); - BearingWithConfidence b1 = new DegreeBearingImpl(90); - BearingWithConfidence b2 = new DegreeBearingImpl(90); + BearingWithConfidence b1 = new BearingWithConfidenceImpl(new DegreeBearingImpl(90), /* confidence */ 0.9); + BearingWithConfidence b2 = new BearingWithConfidenceImpl(new DegreeBearingImpl(90), /* confidence */ 0.9); cluster.add(b1); cluster.add(b2); - BearingWithConfidence average = cluster.getAverage(); + Bearing average = cluster.getAverage(); assertEquals(90, average.getDegrees(), 0.00000001); } @Test public void testBearingWithConfidenceClusterAverageWithZeroCosinusAndNegativeSinus() { BearingWithConfidenceCluster cluster = new BearingWithConfidenceCluster(); - BearingWithConfidence b1 = new DegreeBearingImpl(270); - BearingWithConfidence b2 = new DegreeBearingImpl(270); + BearingWithConfidence b1 = new BearingWithConfidenceImpl(new DegreeBearingImpl(270), /* confidence */ 0.9); + BearingWithConfidence b2 = new BearingWithConfidenceImpl(new DegreeBearingImpl(270), /* confidence */ 0.9); cluster.add(b1); cluster.add(b2); - BearingWithConfidence average = cluster.getAverage(); + Bearing average = cluster.getAverage(); assertEquals(270, average.getDegrees(), 0.00000001); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java index fd5b1be920f..e82e9f40335 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/BearingWithConfidence.java @@ -3,5 +3,7 @@ package com.sap.sailing.domain.base; import com.sap.sailing.domain.common.Util.Pair; import com.sap.sailing.domain.confidence.HasConfidence; -public interface BearingWithConfidence extends Bearing, HasConfidence, BearingWithConfidence> { + +public interface BearingWithConfidence extends HasConfidence, BearingWithConfidence> { + Bearing getBearing(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithBearingWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithBearingWithConfidence.java index 1750e4c7180..5c21bf58936 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithBearingWithConfidence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithBearingWithConfidence.java @@ -3,6 +3,7 @@ package com.sap.sailing.domain.base; import com.sap.sailing.domain.common.Util.Triple; import com.sap.sailing.domain.confidence.HasConfidence; -public interface SpeedWithBearingWithConfidence extends SpeedWithBearing, - HasConfidence, SpeedWithBearingWithConfidence> { +public interface SpeedWithBearingWithConfidence extends + HasConfidence, SpeedWithBearingWithConfidence> { + SpeedWithBearing getSpeedWithBearing(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithConfidence.java index c4cd41ba4b8..1712c5e96ea 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithConfidence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/SpeedWithConfidence.java @@ -2,5 +2,6 @@ package com.sap.sailing.domain.base; import com.sap.sailing.domain.confidence.HasConfidence; -public interface SpeedWithConfidence extends Speed, HasConfidence { +public interface SpeedWithConfidence extends HasConfidence { + Speed getSpeed(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java index 2b6d8d55d9e..d789da05086 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractBearing.java @@ -1,13 +1,8 @@ package com.sap.sailing.domain.base.impl; import com.sap.sailing.domain.base.Bearing; -import com.sap.sailing.domain.base.BearingWithConfidence; -import com.sap.sailing.domain.common.Util.Pair; -import com.sap.sailing.domain.confidence.ScalableValue; - -public abstract class AbstractBearing implements BearingWithConfidence { - protected static final double DEFAULT_BEARING_CONFIDENCE = 0.9; +public abstract class AbstractBearing implements Bearing { @Override public Bearing reverse() { if (getDegrees() >= 180) { @@ -64,58 +59,4 @@ public abstract class AbstractBearing implements BearingWithConfidence { } - @Override - public double getConfidence() { - return DEFAULT_BEARING_CONFIDENCE; - } - - @Override - public ScalableValue, BearingWithConfidence> getScalableValue() { - return new ScalableBearing(this); - } - - private static class ScalableBearing implements ScalableValue, BearingWithConfidence> { - private final double sin; - private final double cos; - - public ScalableBearing(Bearing bearing) { - this.sin = Math.sin(bearing.getRadians()); - this.cos = Math.cos(bearing.getRadians()); - } - - private ScalableBearing(double sin, double cos) { - this.sin = sin; - this.cos = cos; - } - - @Override - public ScalableValue, BearingWithConfidence> multiply(double factor) { - Pair pair = getValue(); - return new ScalableBearing(factor*pair.getA(), factor*pair.getB()); - } - - @Override - public ScalableValue, BearingWithConfidence> add(ScalableValue, BearingWithConfidence> t) { - Pair value = getValue(); - Pair tValue = t.getValue(); - return new ScalableBearing(value.getA()+tValue.getA(), value.getB()+tValue.getB()); - } - - @Override - public BearingWithConfidence divide(double divisor) { - double angle; - if (cos == 0) { - angle = sin >= 0 ? Math.PI / 2 : -Math.PI / 2; - } else { - angle = Math.atan2(sin, cos); - } - BearingWithConfidence result = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); - return result; - } - - @Override - public Pair getValue() { - return new Pair(sin, cos); - } - } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithBearingImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithBearingImpl.java index 733a843e035..f58376ae6e9 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithBearingImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithBearingImpl.java @@ -1,17 +1,12 @@ package com.sap.sailing.domain.base.impl; import com.sap.sailing.domain.base.Bearing; -import com.sap.sailing.domain.base.BearingWithConfidence; import com.sap.sailing.domain.base.CourseChange; import com.sap.sailing.domain.base.Position; import com.sap.sailing.domain.base.SpeedWithBearing; -import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; -import com.sap.sailing.domain.base.SpeedWithConfidence; import com.sap.sailing.domain.base.TimePoint; -import com.sap.sailing.domain.common.Util.Triple; -import com.sap.sailing.domain.confidence.ScalableValue; -public abstract class AbstractSpeedWithBearingImpl extends AbstractSpeedImpl implements SpeedWithBearingWithConfidence { +public abstract class AbstractSpeedWithBearingImpl extends AbstractSpeedImpl implements SpeedWithBearing { private final Bearing bearing; protected AbstractSpeedWithBearingImpl(Bearing bearing) { @@ -77,63 +72,4 @@ public abstract class AbstractSpeedWithBearingImpl extends AbstractSpeedImpl imp return new KnotSpeedWithBearingImpl(newSpeedInKnots, newBearing); } - @Override - public double getConfidence() { - return AbstractSpeedWithConfidence.DEFAULT_SPEED_CONFIDENCE; - } - - /** - * The scalable value used for averaging confidence-based objects of this type is a triple whose first component - * holds the speed with a confidence while the second and third element are the sine and cosine values of the bearing's - * angle. - */ - @Override - public ScalableValue, SpeedWithBearingWithConfidence> getScalableValue() { - return new ScalableSpeedWithBearing(new KnotSpeedImpl(getKnots()), Math.sin(getBearing().getRadians()), Math.cos(getBearing().getRadians())); - } - - private static class ScalableSpeedWithBearing implements ScalableValue, SpeedWithBearingWithConfidence> { - private final SpeedWithConfidence speedWithConfidence; - private final double sin; - private final double cos; - - public ScalableSpeedWithBearing(SpeedWithConfidence speedWithConfidence, double sin, double cos) { - this.speedWithConfidence = speedWithConfidence; - this.sin = sin; - this.cos = cos; - } - - @Override - public ScalableValue, SpeedWithBearingWithConfidence> multiply( - double factor) { - SpeedWithConfidence newSpeedWithConfidence = speedWithConfidence.getScalableValue().multiply(factor).getValue(); - return new ScalableSpeedWithBearing(newSpeedWithConfidence, factor*sin, factor*cos); - } - - @Override - public ScalableValue, SpeedWithBearingWithConfidence> add( - ScalableValue, SpeedWithBearingWithConfidence> t) { - SpeedWithConfidence newSpeedWithConfidence = speedWithConfidence.getScalableValue().add(t.getValue().getA().getScalableValue()).getValue(); - return new ScalableSpeedWithBearing(newSpeedWithConfidence, sin+t.getValue().getB(), cos+t.getValue().getC()); - } - - @Override - public SpeedWithBearingWithConfidence divide(double divisor) { - SpeedWithConfidence newSpeedWithConfidence = speedWithConfidence.getScalableValue().divide(divisor); - double angle; - if (cos == 0) { - angle = sin >= 0 ? Math.PI / 2 : -Math.PI / 2; - } else { - angle = Math.atan2(sin, cos); - } - BearingWithConfidence bearing = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); - return new KnotSpeedWithBearingImpl(newSpeedWithConfidence.getKnots(), bearing); - } - - @Override - public Triple getValue() { - return new Triple(speedWithConfidence, sin, cos); - } - - } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithConfidence.java deleted file mode 100755 index 39f55220d08..00000000000 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/AbstractSpeedWithConfidence.java +++ /dev/null @@ -1,43 +0,0 @@ -package com.sap.sailing.domain.base.impl; - -import com.sap.sailing.domain.base.SpeedWithConfidence; -import com.sap.sailing.domain.confidence.ScalableValue; - -public abstract class AbstractSpeedWithConfidence extends AbstractSpeedImpl implements SpeedWithConfidence, - ScalableValue { - protected static final double DEFAULT_SPEED_CONFIDENCE = 0.9; - - protected abstract AbstractSpeedWithConfidence createInstanceOfSameType(double metersPerSecond, double confidence); - - @Override - public double getConfidence() { - return DEFAULT_SPEED_CONFIDENCE; - } - - @Override - public ScalableValue multiply(double factor) { - return createInstanceOfSameType(factor*getMetersPerSecond(), getConfidence()); - } - - @Override - public AbstractSpeedWithConfidence add(ScalableValue t) { - return createInstanceOfSameType(getMetersPerSecond() + t.getValue().getMetersPerSecond(), (getConfidence() + t - .getValue().getConfidence()) / 2); - } - - @Override - public SpeedWithConfidence divide(double divisor) { - return createInstanceOfSameType(getMetersPerSecond()/divisor, getConfidence()); - } - - @Override - public SpeedWithConfidence getValue() { - return this; - } - - @Override - public ScalableValue getScalableValue() { - return this; - } - -} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/BearingWithConfidenceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/BearingWithConfidenceImpl.java new file mode 100755 index 00000000000..89eb328ffe4 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/BearingWithConfidenceImpl.java @@ -0,0 +1,77 @@ +package com.sap.sailing.domain.base.impl; + +import com.sap.sailing.domain.base.Bearing; +import com.sap.sailing.domain.base.BearingWithConfidence; +import com.sap.sailing.domain.common.Util.Pair; +import com.sap.sailing.domain.confidence.ScalableValue; + +public class BearingWithConfidenceImpl implements BearingWithConfidence { + private final Bearing bearing; + + private final double confidence; + + public BearingWithConfidenceImpl(Bearing bearing, double confidence) { + this.bearing = bearing; + this.confidence = confidence; + } + + @Override + public Bearing getBearing() { + return bearing; + } + + @Override + public double getConfidence() { + return confidence; + } + + @Override + public ScalableValue, BearingWithConfidence> getScalableValue() { + return new ScalableBearing(getBearing()); + } + + private static class ScalableBearing implements ScalableValue, BearingWithConfidence> { + private final double sin; + private final double cos; + + public ScalableBearing(Bearing bearing) { + this.sin = Math.sin(bearing.getRadians()); + this.cos = Math.cos(bearing.getRadians()); + } + + private ScalableBearing(double sin, double cos) { + this.sin = sin; + this.cos = cos; + } + + @Override + public ScalableValue, BearingWithConfidence> multiply(double factor) { + Pair pair = getValue(); + return new ScalableBearing(factor*pair.getA(), factor*pair.getB()); + } + + @Override + public ScalableValue, BearingWithConfidence> add(ScalableValue, BearingWithConfidence> t) { + Pair value = getValue(); + Pair tValue = t.getValue(); + return new ScalableBearing(value.getA()+tValue.getA(), value.getB()+tValue.getB()); + } + + @Override + public BearingWithConfidence divide(double divisor, double confidence) { + double angle; + if (cos == 0) { + angle = sin >= 0 ? Math.PI / 2 : -Math.PI / 2; + } else { + angle = Math.atan2(sin, cos); + } + BearingWithConfidence result = new BearingWithConfidenceImpl(new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle), confidence); + return result; + } + + @Override + public Pair getValue() { + return new Pair(sin, cos); + } + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/HasConfidenceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/HasConfidenceImpl.java new file mode 100755 index 00000000000..be93f9f85a2 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/HasConfidenceImpl.java @@ -0,0 +1,16 @@ +package com.sap.sailing.domain.base.impl; + +import com.sap.sailing.domain.confidence.HasConfidence; + +public abstract class HasConfidenceImpl implements HasConfidence { + private final double confidence; + + public HasConfidenceImpl(double confidence) { + this.confidence = confidence; + } + + @Override + public double getConfidence() { + return confidence; + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KilometersPerHourSpeedImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KilometersPerHourSpeedImpl.java index a10541b2d1a..a1d047562be 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KilometersPerHourSpeedImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KilometersPerHourSpeedImpl.java @@ -1,28 +1,15 @@ package com.sap.sailing.domain.base.impl; import com.sap.sailing.domain.base.Mile; -import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.base.Speed; -public class KilometersPerHourSpeedImpl extends AbstractSpeedWithConfidence implements SpeedWithConfidence { +public class KilometersPerHourSpeedImpl extends AbstractSpeedImpl implements Speed { private final double speedInKilometersPerHour; - private final double confidence; - public KilometersPerHourSpeedImpl(double speedInKilometersPerHour) { this.speedInKilometersPerHour = speedInKilometersPerHour; - this.confidence = DEFAULT_SPEED_CONFIDENCE; } - private KilometersPerHourSpeedImpl(double speedInKilometersPerHour, double confidence) { - this.speedInKilometersPerHour = speedInKilometersPerHour; - this.confidence = confidence; - } - - @Override - public double getConfidence() { - return confidence; - } - @Override public double getKnots() { return getKilometersPerHour() * 1000. / Mile.METERS_PER_NAUTICAL_MILE; @@ -37,10 +24,4 @@ public class KilometersPerHourSpeedImpl extends AbstractSpeedWithConfidence impl public double getKilometersPerHour() { return speedInKilometersPerHour; } - - @Override - protected KilometersPerHourSpeedImpl createInstanceOfSameType(double metersPerSecond, double confidence) { - return new KilometersPerHourSpeedImpl(metersPerSecond/3.6, confidence); - } - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedImpl.java index ace4ef1b439..b6520baeb50 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedImpl.java @@ -1,34 +1,16 @@ package com.sap.sailing.domain.base.impl; -import com.sap.sailing.domain.base.Mile; -import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.base.Speed; -public class KnotSpeedImpl extends AbstractSpeedWithConfidence implements SpeedWithConfidence { +public class KnotSpeedImpl extends AbstractSpeedImpl implements Speed { private final double knots; - private final double confidence; - public KnotSpeedImpl(double knots) { - this(knots, DEFAULT_SPEED_CONFIDENCE); - } - - private KnotSpeedImpl(double knots, double confidence) { this.knots = knots; - this.confidence = confidence; - } - - @Override - public double getConfidence() { - return confidence; } @Override public double getKnots() { return knots; } - - @Override - protected KnotSpeedImpl createInstanceOfSameType(double metersPerSecond, double confidence) { - return new KnotSpeedImpl(getKnots() * Mile.METERS_PER_SEA_MILE / 3600, confidence); - } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedWithBearingImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedWithBearingImpl.java index 52fbdc86c60..63dc162d337 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedWithBearingImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/KnotSpeedWithBearingImpl.java @@ -5,23 +5,11 @@ import com.sap.sailing.domain.base.Bearing; public class KnotSpeedWithBearingImpl extends AbstractSpeedWithBearingImpl { private final double speedInKnots; - private final double confidence; - public KnotSpeedWithBearingImpl(double speedInKnots, Bearing bearing) { - this(speedInKnots, bearing, AbstractSpeedWithConfidence.DEFAULT_SPEED_CONFIDENCE); - } - - public KnotSpeedWithBearingImpl(double speedInKnots, Bearing bearing, double confidence) { super(bearing); this.speedInKnots = speedInKnots; - this.confidence = confidence; } - @Override - public double getConfidence() { - return confidence; - } - @Override public double getKnots() { return speedInKnots; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SpeedWithBearingWithConfidenceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SpeedWithBearingWithConfidenceImpl.java new file mode 100755 index 00000000000..c3b5fc2c7b2 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/SpeedWithBearingWithConfidenceImpl.java @@ -0,0 +1,80 @@ +package com.sap.sailing.domain.base.impl; + +import com.sap.sailing.domain.base.Bearing; +import com.sap.sailing.domain.base.Speed; +import com.sap.sailing.domain.base.SpeedWithBearing; +import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; +import com.sap.sailing.domain.common.Util.Triple; +import com.sap.sailing.domain.confidence.ScalableValue; + +public class SpeedWithBearingWithConfidenceImpl extends + HasConfidenceImpl, SpeedWithBearingWithConfidence> implements + SpeedWithBearingWithConfidence { + private final SpeedWithBearing speedWithBearing; + + public SpeedWithBearingWithConfidenceImpl(SpeedWithBearing speedWithBearing, double confidence) { + super(confidence); + this.speedWithBearing = speedWithBearing; + } + + @Override + public SpeedWithBearing getSpeedWithBearing() { + return speedWithBearing; + } + + /** + * The scalable value used for averaging confidence-based objects of this type is a triple whose first component + * holds the speed with a confidence while the second and third element are the sine and cosine values of the bearing's + * angle. + */ + @Override + public ScalableValue, SpeedWithBearingWithConfidence> getScalableValue() { + return new ScalableSpeedWithBearing(new KnotSpeedImpl(getSpeedWithBearing().getKnots()), + Math.sin(getSpeedWithBearing().getBearing().getRadians()), Math.cos(getSpeedWithBearing().getBearing().getRadians())); + } + + private static class ScalableSpeedWithBearing implements ScalableValue, SpeedWithBearingWithConfidence> { + private final Speed speed; + private final double sin; + private final double cos; + + public ScalableSpeedWithBearing(Speed speed, double sin, double cos) { + this.speed = speed; + this.sin = sin; + this.cos = cos; + } + + @Override + public ScalableValue, SpeedWithBearingWithConfidence> multiply( + double factor) { + Speed newSpeed = new KnotSpeedImpl(factor*speed.getKnots()); + return new ScalableSpeedWithBearing(newSpeed, factor*sin, factor*cos); + } + + @Override + public ScalableValue, SpeedWithBearingWithConfidence> add( + ScalableValue, SpeedWithBearingWithConfidence> t) { + Speed newSpeed = new KnotSpeedImpl(speed.getKnots() + t.getValue().getA().getKnots()); + return new ScalableSpeedWithBearing(newSpeed, sin+t.getValue().getB(), cos+t.getValue().getC()); + } + + @Override + public SpeedWithBearingWithConfidence divide(double divisor, double confidence) { + Speed newSpeed = new KnotSpeedImpl(speed.getKnots() / divisor); + double angle; + if (cos == 0) { + angle = sin >= 0 ? Math.PI / 2 : -Math.PI / 2; + } else { + angle = Math.atan2(sin, cos); + } + Bearing bearing = new RadianBearingImpl(angle < 0 ? angle + 2 * Math.PI : angle); + return new SpeedWithBearingWithConfidenceImpl(new KnotSpeedWithBearingImpl(newSpeed.getKnots(), bearing), confidence); + } + + @Override + public Triple getValue() { + return new Triple(speed, sin, cos); + } + + } +} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java index 8e38e951e72..69293418007 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/HasConfidence.java @@ -25,7 +25,7 @@ import com.sap.sailing.domain.base.Distance; * {@link ScalableValue} which is then used for computing a weighed sum. The values' weight is their * {@link #getConfidence() confidence}. The sum (which is still a {@link ScalableValue} because * {@link ScalableValue#add(ScalableValue)} returns again a {@link ScalableValue}) is then - * {@link ScalableValue#divide(double) divided} by the sum of the confidences. This "division" is expected to + * {@link ScalableValue#divide(double, double) divided} by the sum of the confidences. This "division" is expected to * produce an object of type AveragesTo. Usually, AveragesTo would be the same as * the class implementing this interface. * diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ScalableValue.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ScalableValue.java index a65e5696786..436b86ed76f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ScalableValue.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/ScalableValue.java @@ -5,7 +5,7 @@ public interface ScalableValue { ScalableValue add(ScalableValue t); - AveragesTo divide(double divisor); + AveragesTo divide(double divisor, double confidence); ValueType getValue(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java index 80186aa61ed..6c8120be2e4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ConfidenceBasedAveragerImpl.java @@ -45,7 +45,9 @@ public class ConfidenceBasedAveragerImpl implements Confi numerator = numerator.add(next.getScalableValue().multiply(next.getConfidence())); confidenceSum += next.getConfidence(); } - AveragesTo result = numerator.divide(confidenceSum); + // TODO determine new confidence, e.g., based on the variance of estimates averaged + double newConfidence = confidenceSum / Util.size(values); + AveragesTo result = numerator.divide(confidenceSum, newConfidence); return result; } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDouble.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDouble.java index 3727713e958..60c24dbed45 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDouble.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/confidence/impl/ScalableDouble.java @@ -21,7 +21,7 @@ public class ScalableDouble implements AbstractScalarValue { } @Override - public Double divide(double divisor) { + public Double divide(double divisor, double confidence) { return getValue()/divisor; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java index 1132d9d6723..f0ab9f6a85d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingCluster.java @@ -29,4 +29,9 @@ public class BearingCluster extends GenericBearingCluster { public BearingCluster[] splitInTwo(double minimumDegreeDifferenceBetweenTacks) { return (BearingCluster[]) super.splitInTwo(minimumDegreeDifferenceBetweenTacks); } + + @Override + protected Bearing getBearing(Bearing b) { + return b; + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java index 5b94e601152..41fa1df0851 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/BearingWithConfidenceCluster.java @@ -25,9 +25,10 @@ public class BearingWithConfidenceCluster extends GenericBearingCluster, BearingWithConfidence> averager = ConfidenceBasedAveragerFactory.INSTANCE.createAverager(); - return averager.getAverage(getBearings()); + BearingWithConfidence average = averager.getAverage(getBearings()); + return average == null ? null : average.getBearing(); } @Override @@ -44,4 +45,9 @@ public class BearingWithConfidenceCluster extends GenericBearingCluster * - * It is assumed that bearings added to this cluster are no further than 180 degrees apart. Violating this rule will - * lead to unpredictable results. - * * @author Axel Uhl (d043530) * * @param the class used for the bearing objects */ -public class GenericBearingCluster { +public abstract class GenericBearingCluster { private final List bearings; private double sumSin; private double sumCos; @@ -34,6 +31,8 @@ public class GenericBearingCluster { sumCos = 0.0; } + abstract protected Bearing getBearing(BearingType b); + /** * Finds the two bearings in the cluster that are farthest apart (at least minimumDegreeDifferenceBetweenTacks). * Then, the remaining bearings in this cluster are associated with the one of the two extreme bearings to which they are @@ -63,7 +62,9 @@ public class GenericBearingCluster { } for (BearingType bearing : bearings) { if (extremeBearings == null || (bearing != extremeBearings.getA() && bearing != extremeBearings.getB())) { - if (extremeBearings == null || result[0].getDifferenceFromAverage(bearing) <= result[1].getDifferenceFromAverage(bearing)) { + if (extremeBearings == null + || result[0].getDifferenceFromAverage(getBearing(bearing)) <= result[1] + .getDifferenceFromAverage(getBearing(bearing))) { result[0].add(bearing); } else { result[1].add(bearing); @@ -77,9 +78,7 @@ public class GenericBearingCluster { return result; } - protected GenericBearingCluster createEmptyCluster() { - return new GenericBearingCluster(); - } + abstract protected GenericBearingCluster createEmptyCluster(); @SuppressWarnings("unchecked") protected GenericBearingCluster[] createBearingClusterArraySizeTwo() { @@ -92,10 +91,10 @@ public class GenericBearingCluster { Pair result = null; for (int i=0; i= maxAbsDegDiff) { + if (Math.abs(getBearing(bearings.get(i)).getDifferenceTo(getBearing(bearings.get(j))).getDegrees()) >= maxAbsDegDiff) { result = new Pair(bearings.get(i), bearings.get(j)); - maxAbsDegDiff = Math.abs(bearings.get(i).getDifferenceTo(bearings.get(j)).getDegrees()); - assert Math.abs(result.getA().getDegrees()-result.getB().getDegrees()) <= 180.; + maxAbsDegDiff = Math.abs(getBearing(bearings.get(i)).getDifferenceTo(getBearing(bearings.get(j))).getDegrees()); + assert Math.abs(getBearing(result.getA()).getDegrees()-getBearing(result.getB()).getDegrees()) <= 180.; } } } @@ -112,8 +111,8 @@ public class GenericBearingCluster { public void add(BearingType bearing) { bearings.add(bearing); - sumSin += Math.sin(bearing.getRadians()); - sumCos += Math.cos(bearing.getRadians()); + sumSin += Math.sin(getBearing(bearing).getRadians()); + sumCos += Math.cos(getBearing(bearing).getRadians()); } /**