From 4ee67a37428dafeadae7786ed97b51069b8c2ae7 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 16 May 2018 18:51:21 +0200 Subject: [PATCH 01/53] starting to create a ManeuverLossDTO to visualize the ManeuverLoss on the RaceMap --- .../maneuverdetection/impl/ManeuverLoss.java | 3 +- .../gwt/ui/client/shared/racemap/RaceMap.java | 34 +++++++++++++++++++ .../gwt/ui/server/SailingServiceImpl.java | 4 +-- .../sailing/gwt/ui/shared/ManeuverDTO.java | 2 +- 4 files changed, 38 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java index 0050dec02a2..d005d866490 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java @@ -1,7 +1,6 @@ package com.sap.sailing.domain.maneuverdetection.impl; import com.sap.sailing.domain.common.Distance; -import com.sap.sailing.domain.common.impl.MeterDistance; /** * @@ -27,7 +26,7 @@ public class ManeuverLoss { } public Distance getDistanceLost() { - return new MeterDistance(distanceSailedIfNotManeuvering.getMeters() - distanceSailed.getMeters()); + return distanceSailedIfNotManeuvering.add(distanceSailed.scale(-1)); } /** diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index c1cbd09b235..c5d2d3f0815 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2433,10 +2433,44 @@ public class RaceMap extends AbstractCompositeComponent impleme LatLng where = getCoordinateSystem().toLatLng(maneuver.position); Widget content = getInfoWindowContent(maneuver); managedInfoWindow.openAtPosition(content, where); +// visualizeManeuver(maneuver, competitor, true); }); maneuverMarkers.add(marker); marker.setMap(map); } + +// private final StringBuilder middleManeuverAngleLineText = new StringBuilder(); +// final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { +// @Override +// public String getLineInfo() { +// return stringMessages.finishLine()+middleManeuverAngleLineText; +// } +// }; +// private final StringBuilder bearingBeforeManeuverLineText = new StringBuilder(); +// final LineInfoProvider bearingBeforeManeuverLineInfoProvider = new LineInfoProvider() { +// @Override +// public String getLineInfo() { +// return stringMessages.finishLine()+bearingBeforeManeuverLineText; +// } +// }; +// +// private void visualizeManeuver(ManeuverDTO maneuver, CompetitorDTO competitor, boolean showManeuverVisualization) { +// +// Bearing bearingBefore = new DegreeBearingImpl(maneuver.speedWithBearingBefore.bearingInDegrees); +// Bearing bearingAfter = new DegreeBearingImpl(maneuver.speedWithBearingAfter.bearingInDegrees); +// Bearing MiddleManeuverAngle = bearingBefore.middle(bearingAfter); +// Distance middleManeuverAngleLineLength = new MeterDistance(100); +// Position middleManeuverAnglePosition1 = maneuver.position.translateRhumb(MiddleManeuverAngle.reverse(), +// middleManeuverAngleLineLength); +// Position middleManeuverAnglePosition2 = maneuver.position.translateRhumb(MiddleManeuverAngle, +// middleManeuverAngleLineLength); +// +// +// showOrRemoveOrUpdateLine(null, showManeuverVisualization, middleManeuverAnglePosition1, +// middleManeuverAnglePosition2, middleManeuverAngleLineInfoProvider, "#ffffff"); +// +// +// } /** * @param date diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 2036eb3334b..25871a6a735 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -3626,8 +3626,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final Date markPassingTimePoint = maneuver.isMarkPassing() ? maneuver.getMarkPassing().getTimePoint().asDate() : null; final NauticalSide markPassingSide = maneuver.isMarkPassing() ? maneuver.getToSide() : null; - result.add(new ManeuverDTO(type, newTack, position, timepoint, timePointBefore, speedBefore, speedAfter, - directionChangeInDegrees, maneuverLossInMeters, maxTurningRateInDegreesPerSecond, + result.add(new ManeuverDTO(type, newTack, position, timepoint, timePointBefore, speedBefore, + speedAfter, directionChangeInDegrees, maneuverLossInMeters, maxTurningRateInDegreesPerSecond, averageTurningRateInDegreesPerSecond, lowestSpeedInKnots, markPassingTimePoint, markPassingSide)); } return result; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java index 06f0284da8b..27975dc0251 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java @@ -22,7 +22,7 @@ public class ManeuverDTO implements IsSerializable { public Date timePoint; public Date timePointBefore; - + public SpeedWithBearingDTO speedWithBearingBefore; public SpeedWithBearingDTO speedWithBearingAfter; From 049b2f44b653b25753f0cac3ed6460b0b8b6dbb6 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 17 May 2018 14:10:05 +0200 Subject: [PATCH 02/53] Updated ManeuverLoss and ManeuverDetectorImpl, created ManeuverLossDTO class to prepare for visualizing ManeuverLoss on Race Map --- .../impl/ManeuverDetectorImpl.java | 24 +++--- .../maneuverdetection/impl/ManeuverLoss.java | 73 +++++++++++++++---- .../sailing/gwt/ui/shared/ManeuverDTO.java | 2 + .../gwt/ui/shared/ManeuverLossDTO.java | 29 ++++++++ 4 files changed, 103 insertions(+), 25 deletions(-) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index 88ef1175865..d22d535c02d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -335,8 +335,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector { stepWithHighestSpeed.getSpeedWithBearing(), stepWithHighestSpeed.getTimePoint(), maneuverCurve.getMainCurveBoundaries().getTimePoint(), maneuverCurve.getMainCurveBoundaries().getMaxTurningRateInDegreesPerSecond(), courseAtMaxTurningRate, - distanceSailedWithinManeuver, projectedManeuverLoss.getDistanceSailed(), distanceSailedIfNotManeuvering, - projectedManeuverLoss.getDistanceSailedIfNotManeuvering(), + distanceSailedWithinManeuver, projectedManeuverLoss.getDistanceSailedProjectedOnMiddleManeuverAngle(), distanceSailedIfNotManeuvering, + projectedManeuverLoss.getDistanceSailedIfNotManeuveringProjectedOnMiddleManeuverAngle(), Math.abs(maneuverCurve.getMainCurveBoundaries().getDirectionChangeInDegrees()) / maneuverCurve.getMainCurveBoundaries().getDuration().asSeconds(), gpsFixCountWithinMainCurve, longestGpsFixIntervalBetweenTwoFixes); @@ -391,8 +391,8 @@ public class ManeuverDetectorImpl implements ManeuverDetector { durationAndAvgSpeedWithBearingBefore.getB(), durationAndAvgSpeedWithBearingBefore.getA(), gpsFixesCountFromPreviousManeuver, durationAndAvgSpeedWithBearingAfter.getB(), durationAndAvgSpeedWithBearingAfter.getA(), gpsFixesCountToNextManeuver, distanceSailedWithinManeuver, - projectedManeuverLoss.getDistanceSailed(), distanceSailedIfNotManeuvering, - projectedManeuverLoss.getDistanceSailedIfNotManeuvering(), gpsFixCountWithinWholeCurve, + projectedManeuverLoss.getDistanceSailedProjectedOnMiddleManeuverAngle(), distanceSailedIfNotManeuvering, + projectedManeuverLoss.getDistanceSailedIfNotManeuveringProjectedOnMiddleManeuverAngle(), gpsFixCountWithinWholeCurve, longestGpsFixIntervalBetweenTwoFixes, intervalBetweenLastFixOfCurveAndNextFix, intervalBetweenFirstFixOfCurveAndPreviousFix); TimePoint maneuverTimePoint = maneuverCurve.getMainCurveBoundaries().getTimePoint(); @@ -1089,7 +1089,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { } else { tackAfterManeuver = null; } - maneuverLoss = getManeuverLoss(maneuverUnstableCourseAndSpeedBoundaries).getDistanceLost(); + maneuverLoss = getManeuverLoss(maneuverUnstableCourseAndSpeedBoundaries).getProjectedDistanceLost(); maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverLoss, maneuverMainCurveDetails.getTimePoint(), maneuverMainCurveDetails.extractCurveBoundariesOnly(), maneuverUnstableCourseAndSpeedBoundaries, @@ -1125,14 +1125,15 @@ public class ManeuverDetectorImpl implements ManeuverDetector { */ private ManeuverLoss getManeuverLoss(ManeuverCurveBoundaries maneuverBoundaries) { final GPSFixTrack track = trackedRace.getTrack(competitor); - SpeedWithBearing speedWhenSpeedStartedToDrop = maneuverBoundaries.getSpeedWithBearingBefore(); - SpeedWithBearing speedAfterManeuver = maneuverBoundaries.getSpeedWithBearingAfter(); + SpeedWithBearing speedWithBearingWhenSpeedStartedToDrop = maneuverBoundaries.getSpeedWithBearingBefore(); + SpeedWithBearing speedWithBearingAfterManeuver = maneuverBoundaries.getSpeedWithBearingAfter(); TimePoint timePointWhenSpeedStartedToDrop = maneuverBoundaries.getTimePointBefore(); TimePoint timePointWhenSpeedLevelledOffAfterManeuver = maneuverBoundaries.getTimePointAfter(); + Duration maneuverDuration = timePointWhenSpeedStartedToDrop.until(timePointWhenSpeedLevelledOffAfterManeuver); // For upwind/downwind legs, find the mean course between inbound and outbound course and project actual // and // extrapolated positions onto it: - Bearing middleManeuverAngle = speedWhenSpeedStartedToDrop.getBearing().middle(speedAfterManeuver.getBearing()); + Bearing middleManeuverAngle = speedWithBearingWhenSpeedStartedToDrop.getBearing().middle(speedWithBearingAfterManeuver.getBearing()); // extrapolate maximum speed before maneuver to time point of maximum speed after maneuver and project // resulting position // onto the average maneuver course; compare to the projected position actually reached at the time @@ -1140,7 +1141,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { // maneuver: Position positionWhenSpeedStartedToDrop = track.getEstimatedPosition(timePointWhenSpeedStartedToDrop, /* extrapolate */ false); - Position extrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver = speedWhenSpeedStartedToDrop.travelTo( + Position extrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver = speedWithBearingWhenSpeedStartedToDrop.travelTo( positionWhenSpeedStartedToDrop, timePointWhenSpeedStartedToDrop, timePointWhenSpeedLevelledOffAfterManeuver); Position actualPositionAtTimePointOfMaxSpeedAfterManeuver = track @@ -1153,7 +1154,10 @@ public class ManeuverDetectorImpl implements ManeuverDetector { .getDistance(projectedActualPositionAtTimePointOfMaxSpeedAfterManeuver); Distance projectedDistanceSailedIfNotManeuvering = positionWhenSpeedStartedToDrop .getDistance(projectedExtrapolatedPositionAtTimePointOfMaxSpeedAfterManeuver); - return new ManeuverLoss(projectedDistanceSailed, projectedDistanceSailedIfNotManeuvering); + return new ManeuverLoss(projectedDistanceSailed, projectedDistanceSailedIfNotManeuvering, + positionWhenSpeedStartedToDrop, + actualPositionAtTimePointOfMaxSpeedAfterManeuver, maneuverDuration, speedWithBearingWhenSpeedStartedToDrop, + middleManeuverAngle); } protected Duration getDurationForDouglasPeuckerExtensionForMainCurveAnalysis(Duration approximateManeuverDuration) { diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java index d005d866490..ab9dc1315ae 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java @@ -1,40 +1,83 @@ package com.sap.sailing.domain.maneuverdetection.impl; +import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sse.common.Duration; /** * * @author Vladislav Chumak (D069712) * */ + +/** + * Updated ManeuverLoss class to use it to visualize the correct maneuver loss on the race map. To draw the ManeuverLoss + * needs the maneuverStartPosition, maneuverEndPosition, middleManeuverAngle, speedWithBearingBefore and + * maneuverDuration. The ManeuverLoss is calculated by {@link ManeuverDetectorImpl#getManeuverLoss()}. + * + * + * | maneuverEndPosition -> x--¦ \ | )| / maneuverStartPosition -> x´ + * + */ public class ManeuverLoss { - private final Distance distanceSailed; - private final Distance distanceSailedIfNotManeuvering; - - public ManeuverLoss(Distance distanceSailed, Distance distanceSailedIfNotManeuvering) { - this.distanceSailed = distanceSailed; - this.distanceSailedIfNotManeuvering = distanceSailedIfNotManeuvering; + private final Distance distanceSailedProjectedOnMiddleManeuverAngle; + private final Distance distanceSailedIfNotManeuveringProjectedOnMiddeManeuverAngle; + private final Position maneuverStartPosition; + private final Position maneuverEndPosition; + private final Duration maneuverDuration; + private final SpeedWithBearing speedWithBearingBefore; + private final Bearing middleManeuverAngle; + + public ManeuverLoss(Distance distanceSailedProjectedOnMiddleManeuverAngle, + Distance distanceSailedIfNotManeuveringProjectedOnMiddleManeuverAngle, Position maneuverStartPosition, + Position maneuverEndPosition, Duration maneuverDuration, SpeedWithBearing speedWithBearingBefore, + Bearing middleManeuverAngle) { + this.distanceSailedProjectedOnMiddleManeuverAngle = distanceSailedProjectedOnMiddleManeuverAngle; + this.distanceSailedIfNotManeuveringProjectedOnMiddeManeuverAngle = distanceSailedIfNotManeuveringProjectedOnMiddleManeuverAngle; + this.maneuverStartPosition = maneuverStartPosition; + this.maneuverEndPosition = maneuverEndPosition; + this.maneuverDuration = maneuverDuration; + this.speedWithBearingBefore = speedWithBearingBefore; + this.middleManeuverAngle = middleManeuverAngle; } - public Distance getDistanceSailed() { - return distanceSailed; + public Distance getDistanceSailedProjectedOnMiddleManeuverAngle() { + return distanceSailedProjectedOnMiddleManeuverAngle; } - public Distance getDistanceSailedIfNotManeuvering() { - return distanceSailedIfNotManeuvering; + public Distance getDistanceSailedIfNotManeuveringProjectedOnMiddleManeuverAngle() { + return distanceSailedIfNotManeuveringProjectedOnMiddeManeuverAngle; } - public Distance getDistanceLost() { - return distanceSailedIfNotManeuvering.add(distanceSailed.scale(-1)); + public Distance getProjectedDistanceLost() { + return distanceSailedIfNotManeuveringProjectedOnMiddeManeuverAngle.add(distanceSailedProjectedOnMiddleManeuverAngle.scale(-1)); } /** - * Gets the ratio between {@link #getDistanceSailed()} and - * {@link #getDistanceSailedIfNotManeuvering()}. + * Gets the ratio between {@link #getDistanceSailedProjectedOnMiddleManeuverAngle()} and + * {@link #getDistanceSailedIfNotManeuveringProjectedOnMiddleManeuverAngle()}. */ public double getRatioBetweenDistanceSailedWithAndWithoutManeuver() { - return getDistanceSailed().getMeters() / getDistanceSailedIfNotManeuvering().getMeters(); + return getDistanceSailedProjectedOnMiddleManeuverAngle().getMeters() / getDistanceSailedIfNotManeuveringProjectedOnMiddleManeuverAngle().getMeters(); + } + + public Position getManeuverStartPosition() { + return maneuverStartPosition; + } + public Position getManeuverEndPosition() { + return maneuverEndPosition; + } + public Duration getManeuverDuration() { + return maneuverDuration; + } + public SpeedWithBearing getSpeedWithBearingBefore() { + return speedWithBearingBefore; + } + public Bearing getMiddleManeuverAngle() { + return middleManeuverAngle; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java index 27975dc0251..7a08983b78b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java @@ -40,6 +40,8 @@ public class ManeuverDTO implements IsSerializable { public Date markPassingTimePoint; public NauticalSide markPassingSide; + +// public ManeuverLossDTO maneuverLoss; public ManeuverDTO() {} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java new file mode 100755 index 00000000000..45abb858522 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java @@ -0,0 +1,29 @@ +package com.sap.sailing.gwt.ui.shared; + +import com.google.gwt.user.client.rpc.IsSerializable; +import com.sap.sailing.domain.common.Position; +import com.sap.sse.common.Duration; + +public class ManeuverLossDTO implements IsSerializable { + public Position maneuverStartPosition; + public Position maneuverEndPosition; + public SpeedWithBearingDTO speedWithBearingbefore; + public Double middleManeuverAngle; + public Duration maneuverDuration; + + public ManeuverLossDTO() { + + } + + public ManeuverLossDTO(Position maneuverStartPosition, Position maneuverEndPosition, + SpeedWithBearingDTO speedWithBearingbefore, Double middleManeuverAngle, Duration maneuverDuration) { + this.maneuverStartPosition = maneuverStartPosition; + this.maneuverEndPosition = maneuverEndPosition; + this.speedWithBearingbefore = speedWithBearingbefore; + this.middleManeuverAngle = middleManeuverAngle; + this.maneuverDuration = maneuverDuration; + } + + + +} From d2d38412ef466c49a0995e89e455ce0e54449406 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 17 May 2018 15:21:20 +0200 Subject: [PATCH 03/53] Renamed all getManeuver() methods that have return type Distance to getManeuverLossDistanceLost() Renamed all variables maneuverLoss of type Distance to maneuverLossDistanceLost --- .../sailing/datamining/data/HasManeuverContext.java | 2 +- .../datamining/impl/data/ManeuverWithContext.java | 6 +++--- .../domain/test/ManeuverLossOnPenaltyCircleTest.java | 2 +- .../impl/AbstractLeaderboardWithCache.java | 4 ++-- .../maneuverdetection/impl/ManeuverDetectorImpl.java | 8 ++++---- .../com/sap/sailing/domain/tracking/Maneuver.java | 2 +- .../sailing/domain/tracking/impl/ManeuverImpl.java | 12 ++++++------ .../impl/ManeuverWithMainCurveBoundariesImpl.java | 4 ++-- ...neuverWithStableSpeedAndCourseBoundariesImpl.java | 4 ++-- .../sailing/gwt/ui/server/SailingServiceImpl.java | 3 ++- .../sap/sailing/gwt/ui/shared/ManeuverLossDTO.java | 6 +++--- .../ManeuverBasedWindEstimationTrackImpl.java | 10 +++++----- .../serialization/impl/ManeuverJsonSerializer.java | 2 +- .../com/sap/sailing/xmlexport/LeaderboardData.java | 4 ++-- 14 files changed, 35 insertions(+), 34 deletions(-) diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java index 4f2b52a5fbb..f3ae717e7b7 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/data/HasManeuverContext.java @@ -26,7 +26,7 @@ public interface HasManeuverContext extends HasManeuver, HasTrackedLegOfCompetit Double getAbsoluteDirectionChangeInDegrees(); @Statistic(messageKey = "ManeuverLoss", resultDecimals = 0, ordinal = 4) - Distance getManeuverLoss(); + Distance getManeuverLossDistanceLost(); @Statistic(messageKey = "WindSpeedVsManeuverLoss", resultDecimals = 2, ordinal = 4) Pair getWindSpeedVsManeuverLoss(); diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java index 8c6d72f6325..a45a75b062f 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java @@ -107,8 +107,8 @@ public class ManeuverWithContext implements HasManeuverContext { } @Override - public Distance getManeuverLoss() { - return getManeuver().getManeuverLoss(); + public Distance getManeuverLossDistanceLost() { + return getManeuver().getManeuverLossDistanceLost(); } @Override @@ -147,7 +147,7 @@ public class ManeuverWithContext implements HasManeuverContext { public Pair getWindSpeedVsManeuverLoss(){ Wind wind = getTrackedLegOfCompetitorContext().getTrackedRace().getWind(maneuver.getPosition(), getTimePointBeforeForAnalysis()); - return new Pair<>(wind.getKnots(), getManeuverLoss().getMeters()); + return new Pair<>(wind.getKnots(), getManeuverLossDistanceLost().getMeters()); } @Override diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverLossOnPenaltyCircleTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverLossOnPenaltyCircleTest.java index 03dfa7acbdb..bf8b4461697 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverLossOnPenaltyCircleTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverLossOnPenaltyCircleTest.java @@ -56,6 +56,6 @@ public class ManeuverLossOnPenaltyCircleTest extends OnlineTracTracBasedTest { Competitor canottieri = getCompetitorByName("Club Canottieri Roggero di Lauria"); final Iterable maneuversCanottieri = getTrackedRace().getManeuvers(canottieri, getTrackedRace().getStartOfRace(), getTrackedRace().getEndOfRace(), /* waitForLatest */ true); final Optional penaltyCircleCanottieri = StreamSupport.stream(maneuversCanottieri.spliterator(), /* parallel */ false).filter(m->m.getType()==ManeuverType.PENALTY_CIRCLE).findAny(); - assertThat(penaltyCircleCanottieri.get().getManeuverLoss(), new IsGreaterThan<>(new MeterDistance(30))); + assertThat(penaltyCircleCanottieri.get().getManeuverLossDistanceLost(), new IsGreaterThan<>(new MeterDistance(30))); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java index 4a44d941236..b42ad24ae3d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java @@ -1006,12 +1006,12 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { case PENALTY_CIRCLE: if (!maneuver.getTimePoint().before(startOfLeg) && (legFinishTime == null || legFinishTime.after(timePoint) || maneuver.getTimePoint().before(legFinishTime))) { - if (maneuver.getManeuverLoss() != null) { + if (maneuver.getManeuverLossDistanceLost() != null) { result.numberOfManeuvers.put(maneuver.getType(), result.numberOfManeuvers.get(maneuver.getType()) + 1); totalManeuverLossInMeters.put(maneuver.getType(), totalManeuverLossInMeters.get(maneuver.getType()) - + maneuver.getManeuverLoss().getMeters()); + + maneuver.getManeuverLossDistanceLost().getMeters()); } } break; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index d22d535c02d..383c11607e8 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -1049,7 +1049,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { ManeuverCurveBoundaries maneuverUnstableCourseAndSpeedBoundaries, Wind wind, MarkPassing markPassing) { final Maneuver maneuver; final ManeuverType maneuverType; - Distance maneuverLoss = null; + Distance maneuverLossDistanceLost = null; Tack tackAfterManeuver = null; final Position maneuverPosition = track.getEstimatedPosition(maneuverMainCurveDetails.getTimePoint(), /* extrapolate */false); @@ -1089,9 +1089,9 @@ public class ManeuverDetectorImpl implements ManeuverDetector { } else { tackAfterManeuver = null; } - maneuverLoss = getManeuverLoss(maneuverUnstableCourseAndSpeedBoundaries).getProjectedDistanceLost(); + maneuverLossDistanceLost = getManeuverLoss(maneuverUnstableCourseAndSpeedBoundaries).getProjectedDistanceLost(); maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver, - maneuverPosition, maneuverLoss, maneuverMainCurveDetails.getTimePoint(), + maneuverPosition, maneuverLossDistanceLost, maneuverMainCurveDetails.getTimePoint(), maneuverMainCurveDetails.extractCurveBoundariesOnly(), maneuverUnstableCourseAndSpeedBoundaries, maneuverMainCurveDetails.getMaxTurningRateInDegreesPerSecond(), markPassing); } else { @@ -1108,7 +1108,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { tackAfterManeuver = null; } maneuver = new ManeuverWithMainCurveBoundariesImpl(maneuverType, tackAfterManeuver, maneuverPosition, - maneuverLoss, maneuverMainCurveDetails.getTimePoint(), + maneuverLossDistanceLost, maneuverMainCurveDetails.getTimePoint(), maneuverMainCurveDetails.extractCurveBoundariesOnly(), maneuverUnstableCourseAndSpeedBoundaries, maneuverMainCurveDetails.getMaxTurningRateInDegreesPerSecond(), markPassing); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java index 81ec0ceb343..4fd2e3f2b03 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java @@ -62,7 +62,7 @@ public interface Maneuver extends GPSFix { * considering the maneuver curve, which was performed between {@link #getTimePointBefore()} and * {@link #getTimePointAfter()}. */ - Distance getManeuverLoss(); + Distance getManeuverLossDistanceLost(); /** * Gets the time point of the corresponding maneuver. The time point refers to a point within the main curve of diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java index b314ec7338a..2a5de75e685 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java @@ -24,20 +24,20 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve private final Tack newTack; private final Position position; private final TimePoint timePoint; - private final Distance maneuverLoss; + private final Distance maneuverLossDistanceLost; private final double maxTurningRateInDegreesPerSecond; private final ManeuverCurveBoundaries mainCurveBoundaries; private final ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries; private final MarkPassing markPassing; - public ManeuverImpl(ManeuverType type, Tack newTack, Position position, Distance maneuverLoss, TimePoint timePoint, + public ManeuverImpl(ManeuverType type, Tack newTack, Position position, Distance maneuverLossDistanceLost, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries, double maxTurningRateInDegreesPerSecond, MarkPassing markPassing) { this.type = type; this.newTack = newTack; this.position = position; - this.maneuverLoss = maneuverLoss; + this.maneuverLossDistanceLost = maneuverLossDistanceLost; this.timePoint = timePoint; this.mainCurveBoundaries = mainCurveBoundaries; this.maneuverCurveWithStableSpeedAndCourseBoundaries = maneuverCurveWithStableSpeedAndCourseBoundaries; @@ -76,8 +76,8 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve } @Override - public Distance getManeuverLoss() { - return maneuverLoss; + public Distance getManeuverLossDistanceLost() { + return maneuverLossDistanceLost; } @Override @@ -105,7 +105,7 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve return super.toString() + " " + type + " on new tack " + newTack + " on position " + position + " at time point " + timePoint + ", " + getManeuverBoundaries() + ", max. turning rate: " + maxTurningRateInDegreesPerSecond - + (getManeuverLoss() == null ? "" : ", Lost approximately " + getManeuverLoss()) + ", Mark passing: " + + (getManeuverLossDistanceLost() == null ? "" : ", Lost approximately " + getManeuverLossDistanceLost()) + ", Mark passing: " + markPassing; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java index d37dec0d884..76c3af89823 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java @@ -20,10 +20,10 @@ public class ManeuverWithMainCurveBoundariesImpl extends ManeuverImpl { private static final long serialVersionUID = 5831188137884083419L; public ManeuverWithMainCurveBoundariesImpl(ManeuverType type, Tack newTack, Position position, - Distance maneuverLoss, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, + Distance maneuverLossDistanceLost, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries, double maxTurningRateInDegreesPerSecond, MarkPassing markPassing) { - super(type, newTack, position, maneuverLoss, timePoint, mainCurveBoundaries, + super(type, newTack, position, maneuverLossDistanceLost, timePoint, mainCurveBoundaries, maneuverCurveWithStableSpeedAndCourseBoundaries, maxTurningRateInDegreesPerSecond, markPassing); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java index 832b379d044..219a75e8e74 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java @@ -21,10 +21,10 @@ public class ManeuverWithStableSpeedAndCourseBoundariesImpl extends ManeuverImpl private static final long serialVersionUID = 5831188137884083419L; public ManeuverWithStableSpeedAndCourseBoundariesImpl(ManeuverType type, Tack newTack, Position position, - Distance maneuverLoss, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, + Distance maneuverLossDistanceLost, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries, double maxTurningRateInDegreesPerSecond, MarkPassing markPassing) { - super(type, newTack, position, maneuverLoss, timePoint, mainCurveBoundaries, + super(type, newTack, position, maneuverLossDistanceLost, timePoint, mainCurveBoundaries, maneuverCurveWithStableSpeedAndCourseBoundaries, maxTurningRateInDegreesPerSecond, markPassing); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 25871a6a735..a70184ef9b9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -3619,7 +3619,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final SpeedWithBearingDTO speedBefore = createSpeedWithBearingDTO(maneuver.getSpeedWithBearingBefore()); final SpeedWithBearingDTO speedAfter = createSpeedWithBearingDTO(maneuver.getSpeedWithBearingAfter()); final double directionChangeInDegrees = maneuver.getDirectionChangeInDegrees(); - final Double maneuverLossInMeters = maneuver.getManeuverLoss() == null ? null : maneuver.getManeuverLoss().getMeters(); + final Double maneuverLossInMeters = maneuver.getManeuverLossDistanceLost() == null ? null : maneuver.getManeuverLossDistanceLost().getMeters(); final double maxTurningRateInDegreesPerSecond = maneuver.getMaxTurningRateInDegreesPerSecond(); final double averageTurningRateInDegreesPerSecond = maneuver.getAvgTurningRateInDegreesPerSecond(); final double lowestSpeedInKnots = maneuver.getLowestSpeed().getKnots(); @@ -3629,6 +3629,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S result.add(new ManeuverDTO(type, newTack, position, timepoint, timePointBefore, speedBefore, speedAfter, directionChangeInDegrees, maneuverLossInMeters, maxTurningRateInDegreesPerSecond, averageTurningRateInDegreesPerSecond, lowestSpeedInKnots, markPassingTimePoint, markPassingSide)); +// final ManeuverLossDTO = new ManeuverLossDTO (maneuver.getMan) } return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java index 45abb858522..e0bf8a19772 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java @@ -7,7 +7,7 @@ import com.sap.sse.common.Duration; public class ManeuverLossDTO implements IsSerializable { public Position maneuverStartPosition; public Position maneuverEndPosition; - public SpeedWithBearingDTO speedWithBearingbefore; + public SpeedWithBearingDTO speedWithBearingBefore; public Double middleManeuverAngle; public Duration maneuverDuration; @@ -16,10 +16,10 @@ public class ManeuverLossDTO implements IsSerializable { } public ManeuverLossDTO(Position maneuverStartPosition, Position maneuverEndPosition, - SpeedWithBearingDTO speedWithBearingbefore, Double middleManeuverAngle, Duration maneuverDuration) { + SpeedWithBearingDTO speedWithBearingBefore, Double middleManeuverAngle, Duration maneuverDuration) { this.maneuverStartPosition = maneuverStartPosition; this.maneuverEndPosition = maneuverEndPosition; - this.speedWithBearingbefore = speedWithBearingbefore; + this.speedWithBearingBefore = speedWithBearingBefore; this.middleManeuverAngle = middleManeuverAngle; this.maneuverDuration = maneuverDuration; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java index 59678e3227c..020d9bad664 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java @@ -181,7 +181,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { private final double maneuverAngleDeg; private final SpeedWithBearing speedAtManeuverStart; private final Bearing middleManeuverCourse; - private final Distance maneuverLoss; + private final Distance maneuverLossDistanceLost; private Pair>[] likelihoodAndTWSBasedOnSpeedAndAngleCache; private ScalableBearingAndScalableDouble scalableMiddleManeuverCourseAndManeuverAngleDegCache; @@ -195,7 +195,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { this.speedAtManeuverStart = maneuver.getSpeedWithBearingBefore(); this.middleManeuverCourse = maneuver.getSpeedWithBearingBefore().getBearing() .middle(maneuver.getSpeedWithBearingAfter().getBearing()); - this.maneuverLoss = maneuver.getManeuverLoss(); + this.maneuverLossDistanceLost = maneuver.getManeuverLossDistanceLost(); @SuppressWarnings("unchecked") Pair>[] myLikelihoodAndTWSBasedOnSpeedAndAngleCache = (Pair>[]) new Pair[ManeuverType .values().length]; @@ -269,8 +269,8 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { return result; } - public Distance getManeuverLoss() { - return maneuverLoss; + public Distance getManeuverLossDistanceLost() { + return maneuverLossDistanceLost; } /** @@ -322,7 +322,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { result.append("\t"); result.append(mc.getMiddleManeuverCourse().getDegrees()); result.append("\t"); - result.append(mc.getManeuverLoss() == null ? 0.0 : mc.getManeuverLoss().getMeters()); + result.append(mc.getManeuverLossDistanceLost() == null ? 0.0 : mc.getManeuverLossDistanceLost().getMeters()); result.append("\t"); result.append(mc.getLikelihoodAndTWSBasedOnSpeedAndAngle(ManeuverType.TACK).getA()); result.append("\t"); diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java index d5ddb39c175..14e3f1f4681 100755 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java @@ -44,7 +44,7 @@ public class ManeuverJsonSerializer implements JsonSerializer { : maneuver.getSpeedWithBearingAfter().getBearing().getDegrees()); result.put(DIRECTION_CHANGE_IN_DEGREES, maneuver.getDirectionChangeInDegrees()); result.put(MANEUVER_LOSS, - maneuver.getManeuverLoss() == null ? null : distanceSerializer.serialize(maneuver.getManeuverLoss())); + maneuver.getManeuverLossDistanceLost() == null ? null : distanceSerializer.serialize(maneuver.getManeuverLossDistanceLost())); result.put(POSITION_AND_TIME, gpsFixSerializer.serialize(maneuver)); result.put(MAX_TURNING_RATE_IN_DEGREES_PER_SECOND, maneuver.getMaxTurningRateInDegreesPerSecond()); result.put(AVG_TURNING_RATE_IN_DEGREES_PER_SECOND, maneuver.getAvgTurningRateInDegreesPerSecond()); diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java index de0543b9f4d..e7ee6ccb9ce 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java @@ -796,8 +796,8 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(maneuverInformation, "type", man.getType().toString()); addNamedElementWithValue(maneuverInformation, "position", maneuverCounter); addNamedElementWithValue(maneuverInformation, "direction_change_in_degrees", man.getDirectionChangeInDegrees()); - Distance maneuverLoss = man.getManeuverLoss(); - addNamedElementWithValue(maneuverInformation, "loss_in_meters", maneuverLoss != null ? maneuverLoss.getMeters() : 0.0); + Distance maneuverLossDistanceLost = man.getManeuverLossDistanceLost(); + addNamedElementWithValue(maneuverInformation, "loss_in_meters", maneuverLossDistanceLost != null ? maneuverLossDistanceLost.getMeters() : 0.0); addNamedElementWithValue(maneuverInformation, "speed_before_in_knots", man.getSpeedWithBearingBefore().getKnots()); addNamedElementWithValue(maneuverInformation, "speed_after_in_knots", man.getSpeedWithBearingAfter().getKnots()); maneuversElement.addContent(maneuverInformation); From b177fe03f17c9b34669351deeae8d941770a4032 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 17 May 2018 16:52:28 +0200 Subject: [PATCH 04/53] Added ManeuverLossDTO to ManeuverDTO and updated SailingServiceImpl createManeuverDTOsForCompetitor to create ManeuverLossDTOs Implemented getManeuverLoss() that returns ManeuverLoss Changed location of ManeuverLoss class to com.sap.sailing.domain.tracking to avoid many imports of com.sap.sailing.domain.maneuverdetection.impl --- .../maneuverdetection/impl/ManeuverDetectorImpl.java | 7 +++++-- .../src/com/sap/sailing/domain/tracking/Maneuver.java | 7 +++++++ .../impl => tracking}/ManeuverLoss.java | 3 ++- .../sailing/domain/tracking/impl/ManeuverImpl.java | 11 ++++++++++- .../impl/ManeuverWithMainCurveBoundariesImpl.java | 5 +++-- ...aneuverWithStableSpeedAndCourseBoundariesImpl.java | 5 +++-- .../sap/sailing/gwt/ui/server/SailingServiceImpl.java | 11 +++++++++-- .../com/sap/sailing/gwt/ui/shared/ManeuverDTO.java | 5 +++-- 8 files changed, 42 insertions(+), 12 deletions(-) rename java/com.sap.sailing.domain/src/com/sap/sailing/domain/{maneuverdetection/impl => tracking}/ManeuverLoss.java (96%) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index 383c11607e8..5ae2e5e5784 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -35,6 +35,7 @@ import com.sap.sailing.domain.tracking.CompleteManeuverCurve; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; +import com.sap.sailing.domain.tracking.ManeuverLoss; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.SpeedWithBearingStep; import com.sap.sailing.domain.tracking.SpeedWithBearingStepsIterable; @@ -1051,6 +1052,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { final ManeuverType maneuverType; Distance maneuverLossDistanceLost = null; Tack tackAfterManeuver = null; + ManeuverLoss maneuverLoss = null; final Position maneuverPosition = track.getEstimatedPosition(maneuverMainCurveDetails.getTimePoint(), /* extrapolate */false); int numberOfJibes = getNumberOfJibes(maneuverMainCurveDetails, wind); @@ -1090,10 +1092,11 @@ public class ManeuverDetectorImpl implements ManeuverDetector { tackAfterManeuver = null; } maneuverLossDistanceLost = getManeuverLoss(maneuverUnstableCourseAndSpeedBoundaries).getProjectedDistanceLost(); + maneuverLoss = getManeuverLoss(maneuverUnstableCourseAndSpeedBoundaries); maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverLossDistanceLost, maneuverMainCurveDetails.getTimePoint(), maneuverMainCurveDetails.extractCurveBoundariesOnly(), maneuverUnstableCourseAndSpeedBoundaries, - maneuverMainCurveDetails.getMaxTurningRateInDegreesPerSecond(), markPassing); + maneuverMainCurveDetails.getMaxTurningRateInDegreesPerSecond(), markPassing, maneuverLoss); } else { // Logic for head-up and bear-away try { @@ -1110,7 +1113,7 @@ public class ManeuverDetectorImpl implements ManeuverDetector { maneuver = new ManeuverWithMainCurveBoundariesImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverLossDistanceLost, maneuverMainCurveDetails.getTimePoint(), maneuverMainCurveDetails.extractCurveBoundariesOnly(), maneuverUnstableCourseAndSpeedBoundaries, - maneuverMainCurveDetails.getMaxTurningRateInDegreesPerSecond(), markPassing); + maneuverMainCurveDetails.getMaxTurningRateInDegreesPerSecond(), markPassing, maneuverLoss); } return maneuver; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java index 4fd2e3f2b03..dd1bbe5b4f3 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java @@ -7,6 +7,7 @@ import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.tracking.GPSFix; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.datamining.annotations.Dimension; @@ -63,6 +64,12 @@ public interface Maneuver extends GPSFix { * {@link #getTimePointAfter()}. */ Distance getManeuverLossDistanceLost(); + + /** + * Gets the ManeuverLoss Object of a maneuver, that is calculated in {@link ManeuverDetectorImpl#getManeuverLoss} + * + */ + ManeuverLoss getManeuverLoss(); /** * Gets the time point of the corresponding maneuver. The time point refers to a point within the main curve of diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java similarity index 96% rename from java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java rename to java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java index ab9dc1315ae..dfe29c83720 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverLoss.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java @@ -1,9 +1,10 @@ -package com.sap.sailing.domain.maneuverdetection.impl; +package com.sap.sailing.domain.tracking; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.SpeedWithBearing; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; import com.sap.sse.common.Duration; /** diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java index 2a5de75e685..0f5f59b0f2f 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java @@ -10,6 +10,7 @@ import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.tracking.impl.AbstractGPSFixImpl; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; +import com.sap.sailing.domain.tracking.ManeuverLoss; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; @@ -29,11 +30,12 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve private final ManeuverCurveBoundaries mainCurveBoundaries; private final ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries; private final MarkPassing markPassing; + private final ManeuverLoss maneuverLoss; public ManeuverImpl(ManeuverType type, Tack newTack, Position position, Distance maneuverLossDistanceLost, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries, - double maxTurningRateInDegreesPerSecond, MarkPassing markPassing) { + double maxTurningRateInDegreesPerSecond, MarkPassing markPassing, ManeuverLoss maneuverLoss) { this.type = type; this.newTack = newTack; this.position = position; @@ -43,6 +45,7 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve this.maneuverCurveWithStableSpeedAndCourseBoundaries = maneuverCurveWithStableSpeedAndCourseBoundaries; this.maxTurningRateInDegreesPerSecond = maxTurningRateInDegreesPerSecond; this.markPassing = markPassing; + this.maneuverLoss = maneuverLoss; } @Override @@ -139,5 +142,11 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve return Math.abs(getMainCurveBoundaries().getDirectionChangeInDegrees()) / getMainCurveBoundaries().getDuration().asSeconds(); } + + @Override + public ManeuverLoss getManeuverLoss() { + return maneuverLoss; + } + } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java index 76c3af89823..e845c231031 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java @@ -6,6 +6,7 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; +import com.sap.sailing.domain.tracking.ManeuverLoss; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sse.common.TimePoint; @@ -22,9 +23,9 @@ public class ManeuverWithMainCurveBoundariesImpl extends ManeuverImpl { public ManeuverWithMainCurveBoundariesImpl(ManeuverType type, Tack newTack, Position position, Distance maneuverLossDistanceLost, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries, - double maxTurningRateInDegreesPerSecond, MarkPassing markPassing) { + double maxTurningRateInDegreesPerSecond, MarkPassing markPassing, ManeuverLoss maneuverLoss) { super(type, newTack, position, maneuverLossDistanceLost, timePoint, mainCurveBoundaries, - maneuverCurveWithStableSpeedAndCourseBoundaries, maxTurningRateInDegreesPerSecond, markPassing); + maneuverCurveWithStableSpeedAndCourseBoundaries, maxTurningRateInDegreesPerSecond, markPassing, maneuverLoss); } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java index 219a75e8e74..54d88beb0f0 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java @@ -6,6 +6,7 @@ import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; +import com.sap.sailing.domain.tracking.ManeuverLoss; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sse.common.TimePoint; @@ -23,9 +24,9 @@ public class ManeuverWithStableSpeedAndCourseBoundariesImpl extends ManeuverImpl public ManeuverWithStableSpeedAndCourseBoundariesImpl(ManeuverType type, Tack newTack, Position position, Distance maneuverLossDistanceLost, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries, - double maxTurningRateInDegreesPerSecond, MarkPassing markPassing) { + double maxTurningRateInDegreesPerSecond, MarkPassing markPassing, ManeuverLoss maneuverLoss) { super(type, newTack, position, maneuverLossDistanceLost, timePoint, mainCurveBoundaries, - maneuverCurveWithStableSpeedAndCourseBoundaries, maxTurningRateInDegreesPerSecond, markPassing); + maneuverCurveWithStableSpeedAndCourseBoundaries, maxTurningRateInDegreesPerSecond, markPassing, maneuverLoss); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index a70184ef9b9..233427707e6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -395,6 +395,7 @@ import com.sap.sailing.gwt.ui.shared.LeaderboardGroupBaseDTO; import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO; import com.sap.sailing.gwt.ui.shared.LegInfoDTO; import com.sap.sailing.gwt.ui.shared.ManeuverDTO; +import com.sap.sailing.gwt.ui.shared.ManeuverLossDTO; import com.sap.sailing.gwt.ui.shared.MarkDTO; import com.sap.sailing.gwt.ui.shared.MarkPassingTimesDTO; import com.sap.sailing.gwt.ui.shared.PathDTO; @@ -3626,10 +3627,16 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final Date markPassingTimePoint = maneuver.isMarkPassing() ? maneuver.getMarkPassing().getTimePoint().asDate() : null; final NauticalSide markPassingSide = maneuver.isMarkPassing() ? maneuver.getToSide() : null; + final SpeedWithBearingDTO speedWithBearingBeforeManeuverLoss = maneuver.getManeuverLoss() == null ? null + : createSpeedWithBearingDTO(maneuver.getManeuverLoss().getSpeedWithBearingBefore()); + final Double middleManeuverAngle = maneuver.getManeuverLoss() == null ? null : maneuver.getManeuverLoss().getMiddleManeuverAngle().getDegrees(); + final ManeuverLossDTO maneuverLoss = maneuver.getManeuverLoss() == null ? null + : new ManeuverLossDTO(maneuver.getManeuverLoss().getManeuverStartPosition(), + maneuver.getManeuverLoss().getManeuverEndPosition(), speedWithBearingBeforeManeuverLoss, middleManeuverAngle, + maneuver.getManeuverLoss().getManeuverDuration()); result.add(new ManeuverDTO(type, newTack, position, timepoint, timePointBefore, speedBefore, speedAfter, directionChangeInDegrees, maneuverLossInMeters, maxTurningRateInDegreesPerSecond, - averageTurningRateInDegreesPerSecond, lowestSpeedInKnots, markPassingTimePoint, markPassingSide)); -// final ManeuverLossDTO = new ManeuverLossDTO (maneuver.getMan) + averageTurningRateInDegreesPerSecond, lowestSpeedInKnots, markPassingTimePoint, markPassingSide, maneuverLoss)); } return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java index 7a08983b78b..37fb2fe1ecb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java @@ -41,7 +41,7 @@ public class ManeuverDTO implements IsSerializable { public NauticalSide markPassingSide; -// public ManeuverLossDTO maneuverLoss; + public ManeuverLossDTO maneuverLoss; public ManeuverDTO() {} @@ -49,7 +49,7 @@ public class ManeuverDTO implements IsSerializable { SpeedWithBearingDTO speedWithBearingBefore, SpeedWithBearingDTO speedWithBearingAfter, double directionChangeInDegrees, Double maneuverLossInMeters, double maxTurningRateInDegreesPerSecond, double avgTurningRateInDegreesPerSecond, double lowestSpeedInKnots, Date markPassingTimePoint, - NauticalSide markPassingSide) { + NauticalSide markPassingSide, ManeuverLossDTO maneuverLoss) { this.type = type; this.newTack = newTack; this.position = position; @@ -64,6 +64,7 @@ public class ManeuverDTO implements IsSerializable { this.lowestSpeedInKnots = lowestSpeedInKnots; this.markPassingTimePoint = markPassingTimePoint; this.markPassingSide = markPassingSide; + this.maneuverLoss = maneuverLoss; } public String toString(StringMessages stringMessages) { From 3d6ec7d32e021b11d7f83be1eecdb60e36fac7ff Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 17 May 2018 18:12:00 +0200 Subject: [PATCH 05/53] introduced a visualizeManeuver() in RaceMap to show Maneuver Geometry on Map --- .../gwt/ui/client/shared/racemap/RaceMap.java | 100 ++++++++++++------ 1 file changed, 67 insertions(+), 33 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index c5d2d3f0815..dea3c79bfaf 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2433,44 +2433,78 @@ public class RaceMap extends AbstractCompositeComponent impleme LatLng where = getCoordinateSystem().toLatLng(maneuver.position); Widget content = getInfoWindowContent(maneuver); managedInfoWindow.openAtPosition(content, where); -// visualizeManeuver(maneuver, competitor, true); + visualizeManeuver(maneuver, maneuver.position, true); }); maneuverMarkers.add(marker); marker.setMap(map); } -// private final StringBuilder middleManeuverAngleLineText = new StringBuilder(); -// final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { -// @Override -// public String getLineInfo() { -// return stringMessages.finishLine()+middleManeuverAngleLineText; -// } -// }; -// private final StringBuilder bearingBeforeManeuverLineText = new StringBuilder(); -// final LineInfoProvider bearingBeforeManeuverLineInfoProvider = new LineInfoProvider() { -// @Override -// public String getLineInfo() { -// return stringMessages.finishLine()+bearingBeforeManeuverLineText; -// } -// }; -// -// private void visualizeManeuver(ManeuverDTO maneuver, CompetitorDTO competitor, boolean showManeuverVisualization) { -// -// Bearing bearingBefore = new DegreeBearingImpl(maneuver.speedWithBearingBefore.bearingInDegrees); -// Bearing bearingAfter = new DegreeBearingImpl(maneuver.speedWithBearingAfter.bearingInDegrees); -// Bearing MiddleManeuverAngle = bearingBefore.middle(bearingAfter); -// Distance middleManeuverAngleLineLength = new MeterDistance(100); -// Position middleManeuverAnglePosition1 = maneuver.position.translateRhumb(MiddleManeuverAngle.reverse(), -// middleManeuverAngleLineLength); -// Position middleManeuverAnglePosition2 = maneuver.position.translateRhumb(MiddleManeuverAngle, -// middleManeuverAngleLineLength); -// -// -// showOrRemoveOrUpdateLine(null, showManeuverVisualization, middleManeuverAnglePosition1, -// middleManeuverAnglePosition2, middleManeuverAngleLineInfoProvider, "#ffffff"); -// -// -// } + private final StringBuilder middleManeuverAngleLineText = new StringBuilder(); + final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.finishLine()+middleManeuverAngleLineText; + } + }; + private final StringBuilder LineText1 = new StringBuilder(); + final LineInfoProvider LineInfoProvider1 = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.finishLine()+LineText1; + } + }; + private final StringBuilder LineText2 = new StringBuilder(); + final LineInfoProvider LineInfoProvider2 = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.finishLine()+LineText2; + } + }; + private final StringBuilder LineText3 = new StringBuilder(); + final LineInfoProvider LineInfoProvider3 = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.finishLine()+LineText3; + } + }; + + private void visualizeManeuver(ManeuverDTO maneuver, Position centerPoint, boolean showManeuverVisualization) { + + // Bearing bearingBefore = new DegreeBearingImpl(maneuver.speedWithBearingBefore.bearingInDegrees); + // Bearing bearingAfter = new DegreeBearingImpl(maneuver.speedWithBearingAfter.bearingInDegrees); + // Bearing MiddleManeuverAngle = bearingBefore.middle(bearingAfter); + // Distance middleManeuverAngleLineLength = new MeterDistance(100); + // Position middleManeuverAnglePosition1 = maneuver.maneuverLoss.maneuverStartPosition, + // middleManeuverAngleLineLength); + // Position middleManeuverAnglePosition2 = maneuver.position.translateRhumb(MiddleManeuverAngle, + // middleManeuverAngleLineLength); + // + + Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); + Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); + Distance extrapolationOfManeuverStartPoint = new MeterDistance( + maneuver.maneuverLoss.speedWithBearingBefore.speedInKnots + * maneuver.maneuverLoss.maneuverDuration.asHours() * 1852); + Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.maneuverStartPosition + .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); + Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition + .getIntersection(bearingBefore, centerPoint, middleManeuverAngle); + Position extrapolatedManeuverStartPositionProjectedOnMiddleManeuverAngle = extrapolatedManeuverStartPosition + .projectToLineThrough(intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, + middleManeuverAngle); + Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + + showOrRemoveOrUpdateLine(null, showManeuverVisualization, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, + projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, "#ffffff"); + showOrRemoveOrUpdateLine(null, showManeuverVisualization, maneuver.maneuverLoss.maneuverStartPosition, + extrapolatedManeuverStartPosition, LineInfoProvider1, "#070707"); + showOrRemoveOrUpdateLine(null, showManeuverVisualization, extrapolatedManeuverStartPosition, + extrapolatedManeuverStartPositionProjectedOnMiddleManeuverAngle, LineInfoProvider2, "#FF0000"); + showOrRemoveOrUpdateLine(null, showManeuverVisualization, maneuver.maneuverLoss.maneuverEndPosition, + projectedManeuverEndPosition, LineInfoProvider3, "#F7FF00"); + + } /** * @param date From b82c40be45fc14d95f30ea1cdd3e8b07ce6bfc9b Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 17 May 2018 18:40:52 +0200 Subject: [PATCH 06/53] Improved visualization --- .../gwt/ui/client/shared/racemap/RaceMap.java | 21 ++++++++++++++----- 1 file changed, 16 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index dea3c79bfaf..441b3f03dc5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2467,6 +2467,13 @@ public class RaceMap extends AbstractCompositeComponent impleme return stringMessages.finishLine()+LineText3; } }; + private final StringBuilder LineText4 = new StringBuilder(); + final LineInfoProvider LineInfoProvider4 = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.finishLine()+LineText4; + } + }; private void visualizeManeuver(ManeuverDTO maneuver, Position centerPoint, boolean showManeuverVisualization) { @@ -2479,7 +2486,7 @@ public class RaceMap extends AbstractCompositeComponent impleme // Position middleManeuverAnglePosition2 = maneuver.position.translateRhumb(MiddleManeuverAngle, // middleManeuverAngleLineLength); // - + Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); Distance extrapolationOfManeuverStartPoint = new MeterDistance( @@ -2489,20 +2496,24 @@ public class RaceMap extends AbstractCompositeComponent impleme .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition .getIntersection(bearingBefore, centerPoint, middleManeuverAngle); - Position extrapolatedManeuverStartPositionProjectedOnMiddleManeuverAngle = extrapolatedManeuverStartPosition + Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition .projectToLineThrough(intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + String color = maneuver.maneuverLossInMeters >0 ? color = "#FF0000" : "#00FF00"; showOrRemoveOrUpdateLine(null, showManeuverVisualization, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, "#ffffff"); showOrRemoveOrUpdateLine(null, showManeuverVisualization, maneuver.maneuverLoss.maneuverStartPosition, - extrapolatedManeuverStartPosition, LineInfoProvider1, "#070707"); + extrapolatedManeuverStartPosition, LineInfoProvider1, "#ffffff"); showOrRemoveOrUpdateLine(null, showManeuverVisualization, extrapolatedManeuverStartPosition, - extrapolatedManeuverStartPositionProjectedOnMiddleManeuverAngle, LineInfoProvider2, "#FF0000"); + projectedExtrapolatedManeuverStartPosition, LineInfoProvider2, "#FFFF00"); showOrRemoveOrUpdateLine(null, showManeuverVisualization, maneuver.maneuverLoss.maneuverEndPosition, - projectedManeuverEndPosition, LineInfoProvider3, "#F7FF00"); + projectedManeuverEndPosition, LineInfoProvider3, "#FFFF00"); + showOrRemoveOrUpdateLine(null, showManeuverVisualization, + projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, + LineInfoProvider4, color); } From 6e375a8abbca3a5e607ea21a28709bc691172869 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 24 May 2018 16:09:43 +0200 Subject: [PATCH 07/53] bug4572: improved maneuver loss visualization on RaceMap. Added a CheckBox for displaying the maneuver loss. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 139 ++++++++++++------ 1 file changed, 92 insertions(+), 47 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 441b3f03dc5..d8c905fb4ae 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -65,9 +65,10 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Image; +import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Label; import com.google.gwt.user.client.ui.RequiresResize; import com.google.gwt.user.client.ui.VerticalPanel; @@ -2203,9 +2204,41 @@ public class RaceMap extends AbstractCompositeComponent impleme NumberFormat.getDecimalFormat().format(maneuver.lowestSpeedInKnots) + " " + stringMessages.knotsUnit())); } - if (maneuver.maneuverLossInMeters != null) { - vPanel.add(createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), - numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit())); + if (maneuver.maneuverLoss != null) { + Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); + + CheckBox maneuverLossLinesCheckBox = new CheckBox("Show"); + maneuverLossLinesCheckBox.setValue(maneuverLossLinesCheckBoxValueStore); + maneuverLossLinesCheckBox.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + visualizeManeuver(maneuver, maneuverLossLinesCheckBox.getValue()); + maneuverLossLinesCheckBoxValueStore = maneuverLossLinesCheckBox.getValue(); + } + }); + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(maneuverLossWidget); + hPanel.add(maneuverLossLinesCheckBox); + vPanel.add(hPanel); + + +// Button visualizeManeuverButton1 = new Button("Show"); +// visualizeManeuverButton1.addClickHandler(new ClickHandler() { +// @Override +// public void onClick(ClickEvent event) { +// visualizeManeuver(maneuver, true); +// } +// }); +// Button visualizeManeuverButton2 = new Button("Dont"); +// visualizeManeuverButton2.addClickHandler(new ClickHandler() { +// @Override +// public void onClick(ClickEvent event) { +// visualizeManeuver(maneuver, false); +// } +// }); +// vPanel.add(visualizeManeuverButton1); +// vPanel.add(visualizeManeuverButton2); } if (maneuver.markPassingTimePoint != null) { vPanel.add( @@ -2380,6 +2413,13 @@ public class RaceMap extends AbstractCompositeComponent impleme } maneuverMarkers = null; } + if (maneuverLossLines != null) { + for (Polyline maneuverLossLine : maneuverLossLines) { + maneuverLossLine.setMap((MapWidget) null); + } + maneuverLossLines = null; + maneuverLossLinesCheckBoxValueStore = false; + } } private void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { @@ -2433,60 +2473,60 @@ public class RaceMap extends AbstractCompositeComponent impleme LatLng where = getCoordinateSystem().toLatLng(maneuver.position); Widget content = getInfoWindowContent(maneuver); managedInfoWindow.openAtPosition(content, where); - visualizeManeuver(maneuver, maneuver.position, true); }); maneuverMarkers.add(marker); marker.setMap(map); } - private final StringBuilder middleManeuverAngleLineText = new StringBuilder(); final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.finishLine()+middleManeuverAngleLineText; + return "Middle Maneuver Angle"; } }; - private final StringBuilder LineText1 = new StringBuilder(); final LineInfoProvider LineInfoProvider1 = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.finishLine()+LineText1; + return ""; } }; - private final StringBuilder LineText2 = new StringBuilder(); final LineInfoProvider LineInfoProvider2 = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.finishLine()+LineText2; + return ""; } }; - private final StringBuilder LineText3 = new StringBuilder(); final LineInfoProvider LineInfoProvider3 = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.finishLine()+LineText3; + return ""; } }; - private final StringBuilder LineText4 = new StringBuilder(); - final LineInfoProvider LineInfoProvider4 = new LineInfoProvider() { + final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.finishLine()+LineText4; + return stringMessages.maneuverLoss(); } }; - private void visualizeManeuver(ManeuverDTO maneuver, Position centerPoint, boolean showManeuverVisualization) { - - // Bearing bearingBefore = new DegreeBearingImpl(maneuver.speedWithBearingBefore.bearingInDegrees); - // Bearing bearingAfter = new DegreeBearingImpl(maneuver.speedWithBearingAfter.bearingInDegrees); - // Bearing MiddleManeuverAngle = bearingBefore.middle(bearingAfter); - // Distance middleManeuverAngleLineLength = new MeterDistance(100); - // Position middleManeuverAnglePosition1 = maneuver.maneuverLoss.maneuverStartPosition, - // middleManeuverAngleLineLength); - // Position middleManeuverAnglePosition2 = maneuver.position.translateRhumb(MiddleManeuverAngle, - // middleManeuverAngleLineLength); - // - + private Set maneuverLossLines; + private boolean maneuverLossLinesCheckBoxValueStore = false; + private void visualizeManeuver(ManeuverDTO maneuver, boolean showManeuverVisualization) { + if (showManeuverVisualization == false) { + for (Polyline maneuverLossLine : maneuverLossLines) { + maneuverLossLine.setMap((MapWidget) null); + } + maneuverLossLines = null; + maneuverLossLinesCheckBoxValueStore = false; + } else { + createManeuverLossLines(maneuver); + } + } +/** Visualizes the ManeuverLoss as calculated in {@link ManeuverLoss} + * + */ + private void createManeuverLossLines(ManeuverDTO maneuver) { + maneuverLossLines = new HashSet<>(); Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); Distance extrapolationOfManeuverStartPoint = new MeterDistance( @@ -2495,28 +2535,33 @@ public class RaceMap extends AbstractCompositeComponent impleme Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.maneuverStartPosition .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition - .getIntersection(bearingBefore, centerPoint, middleManeuverAngle); - Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition - .projectToLineThrough(intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, - middleManeuverAngle); + .getIntersection(bearingBefore, maneuver.position, middleManeuverAngle); + Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - String color = maneuver.maneuverLossInMeters >0 ? color = "#FF0000" : "#00FF00"; - - showOrRemoveOrUpdateLine(null, showManeuverVisualization, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, - projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, "#ffffff"); - showOrRemoveOrUpdateLine(null, showManeuverVisualization, maneuver.maneuverLoss.maneuverStartPosition, - extrapolatedManeuverStartPosition, LineInfoProvider1, "#ffffff"); - showOrRemoveOrUpdateLine(null, showManeuverVisualization, extrapolatedManeuverStartPosition, - projectedExtrapolatedManeuverStartPosition, LineInfoProvider2, "#FFFF00"); - showOrRemoveOrUpdateLine(null, showManeuverVisualization, maneuver.maneuverLoss.maneuverEndPosition, - projectedManeuverEndPosition, LineInfoProvider3, "#FFFF00"); - showOrRemoveOrUpdateLine(null, showManeuverVisualization, - projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, - LineInfoProvider4, color); - + String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, + middleManeuverAngleLineInfoProvider, "#ffffff")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, + middleManeuverAngleLineInfoProvider, "#ffffff")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, + extrapolatedManeuverStartPosition, LineInfoProvider1, "#ffffff")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, + projectedExtrapolatedManeuverStartPosition, LineInfoProvider2, "#FFFF00")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, + projectedManeuverEndPosition, LineInfoProvider3, "#FFFF00")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, + projectedManeuverEndPosition, maneuverLossLineInfoProvider, color)); + for (Polyline maneuverLossLine : maneuverLossLines) { + PolylineOptions options = PolylineOptions.newInstance(); + options.setStrokeOpacity(0.5); + maneuverLossLine.setOptions(options); + } } - + /** * @param date * the point in time for which to determine the competitor's boat position; approximated by using the fix From 422f1e830c98d08b6507335c9ff9606ab101b15a Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 24 May 2018 16:11:43 +0200 Subject: [PATCH 08/53] bug4572: deleted unnecessary comments. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 20 +------------------ 1 file changed, 1 insertion(+), 19 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index d8c905fb4ae..e752d144cdb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2220,25 +2220,7 @@ public class RaceMap extends AbstractCompositeComponent impleme HorizontalPanel hPanel = new HorizontalPanel(); hPanel.add(maneuverLossWidget); hPanel.add(maneuverLossLinesCheckBox); - vPanel.add(hPanel); - - -// Button visualizeManeuverButton1 = new Button("Show"); -// visualizeManeuverButton1.addClickHandler(new ClickHandler() { -// @Override -// public void onClick(ClickEvent event) { -// visualizeManeuver(maneuver, true); -// } -// }); -// Button visualizeManeuverButton2 = new Button("Dont"); -// visualizeManeuverButton2.addClickHandler(new ClickHandler() { -// @Override -// public void onClick(ClickEvent event) { -// visualizeManeuver(maneuver, false); -// } -// }); -// vPanel.add(visualizeManeuverButton1); -// vPanel.add(visualizeManeuverButton2); + vPanel.add(hPanel); } if (maneuver.markPassingTimePoint != null) { vPanel.add( From dc3f9bbe348eddc6d5f27d818fd903a30910831a Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Thu, 24 May 2018 19:12:00 +0200 Subject: [PATCH 09/53] bug4572: added SmallTransparentInfoOverlay for maneuver loss visualization --- .../gwt/ui/client/shared/racemap/RaceMap.java | 37 ++++++++++++------- 1 file changed, 24 insertions(+), 13 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index e752d144cdb..ed3b5372108 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2401,6 +2401,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } maneuverLossLines = null; maneuverLossLinesCheckBoxValueStore = false; + maneuverLossInfoOverlay.removeFromMap(); } } @@ -2466,22 +2467,22 @@ public class RaceMap extends AbstractCompositeComponent impleme return "Middle Maneuver Angle"; } }; - final LineInfoProvider LineInfoProvider1 = new LineInfoProvider() { + final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return ""; + return "Extrapolated Maneuver Start Position"; } }; - final LineInfoProvider LineInfoProvider2 = new LineInfoProvider() { + final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return ""; + return "Projected extrapolated Maneuver Start Position"; } }; - final LineInfoProvider LineInfoProvider3 = new LineInfoProvider() { + final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return ""; + return "Projected Maneuver End Position"; } }; final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { @@ -2493,13 +2494,15 @@ public class RaceMap extends AbstractCompositeComponent impleme private Set maneuverLossLines; private boolean maneuverLossLinesCheckBoxValueStore = false; + private SmallTransparentInfoOverlay maneuverLossInfoOverlay; private void visualizeManeuver(ManeuverDTO maneuver, boolean showManeuverVisualization) { if (showManeuverVisualization == false) { for (Polyline maneuverLossLine : maneuverLossLines) { maneuverLossLine.setMap((MapWidget) null); } maneuverLossLines = null; - maneuverLossLinesCheckBoxValueStore = false; + maneuverLossLinesCheckBoxValueStore = false; + maneuverLossInfoOverlay.removeFromMap(); } else { createManeuverLossLines(maneuver); } @@ -2523,20 +2526,28 @@ public class RaceMap extends AbstractCompositeComponent impleme Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, - middleManeuverAngleLineInfoProvider, "#ffffff")); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, "#ffffff")); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, - extrapolatedManeuverStartPosition, LineInfoProvider1, "#ffffff")); + extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff")); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, - projectedExtrapolatedManeuverStartPosition, LineInfoProvider2, "#FFFF00")); + projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00")); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, - projectedManeuverEndPosition, LineInfoProvider3, "#FFFF00")); + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00")); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, maneuverLossLineInfoProvider, color)); + StringBuilder sb = new StringBuilder(); + sb.append(maneuverLossLineInfoProvider.getLineInfo() + ": "); + sb.append(numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); + sb.append('\n'); + sb.append("Maneuver Angle" + ": "); + sb.append(numberFormatOneDecimal.format(maneuver.directionChangeInDegrees) + " " + stringMessages.degreesUnit()); + maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, + RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); + maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, + new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); + maneuverLossInfoOverlay.draw(); for (Polyline maneuverLossLine : maneuverLossLines) { PolylineOptions options = PolylineOptions.newInstance(); options.setStrokeOpacity(0.5); From 1a2973dfda7edfca84f4298ff1af04478990dd20 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 25 May 2018 16:05:58 +0200 Subject: [PATCH 10/53] bug4572: improved maneuver visualization. maneuver loss lines are not tied to maneuver marks anymore, downside they use maneuver position with possible problems if there is more than one maneuver at a given position. Added some stringMessages for i18n, but still missing in some languages. --- .../sailing/gwt/ui/client/StringMessages.java | 3 + .../gwt/ui/client/StringMessages.properties | 5 +- .../ui/client/StringMessages_de.properties | 3 + .../gwt/ui/client/shared/racemap/RaceMap.java | 1531 ++++++++++------- .../gwt/ui/shared/ManeuverLossDTO.java | 4 - 5 files changed, 872 insertions(+), 674 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index bc8d458077f..e281ce3b70e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2184,4 +2184,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String multiVideoDoNoAdd(); String multiVideoOffsetInput(); String multiVideoDescription(); + String maneuverAngle(); + String jibeAngle(); + String tackAngle(); } 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 f028d7ac666..9689a3b9744 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 @@ -2172,4 +2172,7 @@ multiVideoWaitingForSave=Waiting for confirmation multiVideoIdle=Work queue is idle multiVideoDoNoAdd=Do not add multiVideoOffsetInput=Global video offset in milliseconds: -multiVideoDescription=The webserver is required to provide index listing (subfolders supported if indexed) to allow file discovery. After an initial analysis of the metadata contained in the mp4 files, the videos that should be added, need to be selected via the checkbox column on the left. The "Add Audio/Video" button will create media tracks for all selected files, and add those files to all races selected in the right column. \ No newline at end of file +multiVideoDescription=The webserver is required to provide index listing (subfolders supported if indexed) to allow file discovery. After an initial analysis of the metadata contained in the mp4 files, the videos that should be added, need to be selected via the checkbox column on the left. The "Add Audio/Video" button will create media tracks for all selected files, and add those files to all races selected in the right column. +jibeAngle=Jibe angle +tackAngle=Tacking angle +maneuverAngle= Maneuver angle \ 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 6c29b809797..ecf53ea93af 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 @@ -2169,3 +2169,6 @@ multiVideoIdle=Keine Arbeit offen multiVideoDoNoAdd=Nicht hinzufügen multiVideoOffsetInput=Globales Videooffset in Millisekunden: multiVideoDescription=Ein Webserver mit Folderindex wird benötigt (Unterordner werden unterstützt, sofern diese auch indiziert sind), um die Erkennung von Datein zu ermöglichen. Nach einer initialen Analyse der Metadaten von den mp4 Dateien, müssen die gewünschen Videos in der linken Spalte ausgewählt werden. Der "Audio/Video hinzufügen" Knopf erstellt für die ausgewählten Videos Mediatracks, und fügt diese zu allen Rennen hinzu, die in der rechten Spalte ausgewählt wurden. +jibeAngle=Halsenwinkel +tackAngle=Wendewinkel +maneuverAngle=Manöverwinkel \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index ed3b5372108..bebd66578ca 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -161,30 +161,31 @@ import com.sap.sse.gwt.client.shared.components.SettingsDialog; import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; import com.sap.sse.gwt.client.shared.settings.ComponentContext; -public class RaceMap extends AbstractCompositeComponent implements TimeListener, CompetitorSelectionChangeListener, - RaceTimesInfoProviderListener, TailFactory, RequiresDataInitialization, RequiresResize, QuickRankProvider { +public class RaceMap extends AbstractCompositeComponent + implements TimeListener, CompetitorSelectionChangeListener, RaceTimesInfoProviderListener, TailFactory, + RequiresDataInitialization, RequiresResize, QuickRankProvider { private static final String ADVANTAGE_LINE_COLOR = "#ff9900"; // orange private static final String FINISH_LINE_COLOR = "#000000"; private static final Color LOWLIGHTED_TAIL_COLOR = new RGBColor(200, 200, 200); public static final String GET_RACE_MAP_DATA_CATEGORY = "getRaceMapData"; public static final String GET_WIND_DATA_CATEGORY = "getWindData"; private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; - + private static final String COMPACT_HEADER_STYLE = "compactHeader"; public static final Color WATER_COLOR = new RGBColor(0, 67, 125); - + private AbsolutePanel rootPanel = new AbsolutePanel(); - + private MapWidget map; - + /** * Always valid, non-null. Must be used to map all coordinates, headings, bearings, and directions * displayed on the map, including the orientations of any canvases such as boat icons, wind displays etc. that are - * embedded in the map. The coordinate systems facilitates the possibility of transformed displays such as - * rotated and translated versions of the map, implementing the "wind-up" view. + * embedded in the map. The coordinate systems facilitates the possibility of transformed displays such as rotated + * and translated versions of the map, implementing the "wind-up" view. */ private DelegateCoordinateSystem coordinateSystem; - + private FlowPanel headerPanel; private AbsolutePanel panelForLeftHeaderLabels; private AbsolutePanel panelForRightHeaderLabels; @@ -205,17 +206,18 @@ public class RaceMap extends AbstractCompositeComponent impleme private Polyline finishLine; /** - * Polyline for the advantage line (the leading line for the boats, orthogonal to the wind direction; touching the leading boat). + * Polyline for the advantage line (the leading line for the boats, orthogonal to the wind direction; touching the + * leading boat). */ private Polyline advantageLine; - + private AdvantageLineAnimator advantageTimer; - + /** * The windward of two Polylines representing a triangle between startline and first mark. */ private Polyline windwardStartLineMarkToFirstMarkLine; - + /** * The leeward of two Polylines representing a triangle between startline and first mark. */ @@ -224,12 +226,12 @@ public class RaceMap extends AbstractCompositeComponent impleme private class AdvantageLineMouseOverMapHandler implements MouseOverMapHandler { private double trueWindAngle; private Date date; - + public AdvantageLineMouseOverMapHandler(double trueWindAngle, Date date) { this.trueWindAngle = trueWindAngle; this.date = date; } - + public void setTrueWindBearing(double trueWindAngle) { this.trueWindAngle = trueWindAngle; } @@ -240,42 +242,43 @@ public class RaceMap extends AbstractCompositeComponent impleme @Override public void onEvent(MouseOverMapEvent event) { - map.setTitle(stringMessages.advantageLine()+" (from "+new DegreeBearingImpl(Math.round(trueWindAngle)).reverse().getDegrees()+"deg"+ - (date == null ? ")" : ", "+ date) + ")"); + map.setTitle(stringMessages.advantageLine() + " (from " + + new DegreeBearingImpl(Math.round(trueWindAngle)).reverse().getDegrees() + "deg" + + (date == null ? ")" : ", " + date) + ")"); } }; - + private AdvantageLineMouseOverMapHandler advantageLineMouseOverHandler; - + /** * Polylines for the course middle lines; keys are the two control points delimiting the leg for which the - * {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there - * are multiple legs using the same control points in different directions, using a {@link Set} makes this - * independent of the order of the two control points. If no course middle line is currently being shown for - * a pair of control points, the map will not contain a value for this pair. + * {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there are + * multiple legs using the same control points in different directions, using a {@link Set} makes this independent + * of the order of the two control points. If no course middle line is currently being shown for a pair of control + * points, the map will not contain a value for this pair. */ private final Map, Polyline> courseMiddleLines; private final Map courseSidelines; - + /** - * When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the - * lines that show the tooltip text in a little info box linked to the line. When the line is removed by + * When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the lines + * that show the tooltip text in a little info box linked to the line. When the line is removed by * {@link #showOrRemoveOrUpdateLine(Polyline, boolean, Position, Position, LineInfoProvider, String)}, these - * overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is - * deactivated, all these overlays need to go. + * overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is deactivated, + * all these overlays need to go. */ private final Map infoOverlaysForLinesForCourseGeometry; - + /** * Wind data used to display the advantage line. Retrieved by a {@link GetWindInfoAction} execution and used in * {@link #showAdvantageLine(Iterable, Date)}. */ private WindInfoForRaceDTO lastCombinedWindTrackInfoDTO; - + /** - * Manages the cached set of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the boat positions as well as their graphical counterpart in the - * form of {@link Polyline}s. + * Manages the cached set of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the boat positions as well as their + * graphical counterpart in the form of {@link Polyline}s. */ private final FixesAndTails fixesAndTails; @@ -288,7 +291,7 @@ public class RaceMap extends AbstractCompositeComponent impleme * html5 canvases used for competitor info display on the map */ private final CompetitorInfoOverlays competitorInfoOverlays; - + private SmallTransparentInfoOverlay countDownOverlay; /** @@ -301,11 +304,12 @@ public class RaceMap extends AbstractCompositeComponent impleme * html5 canvas used to display course marks including buoy zones */ private final Map courseMarkOverlays; - + private final Map courseMarkClickHandlers; /** - * Maps from the {@link MarkDTO#getIdAsString() mark's ID converted to a string} to the corresponding {@link MarkDTO} + * Maps from the {@link MarkDTO#getIdAsString() mark's ID converted to a string} to the corresponding + * {@link MarkDTO} */ private final Map markDTOs; @@ -324,9 +328,9 @@ public class RaceMap extends AbstractCompositeComponent impleme private Map> lastManeuverResult; private Map> lastDouglasPeuckerResult; - + private final RaceCompetitorSelectionProvider competitorSelection; - + private final RaceCompetitorSet raceCompetitorSet; /** @@ -337,7 +341,7 @@ public class RaceMap extends AbstractCompositeComponent impleme private final Timer timer; private RaceTimesInfoDTO lastRaceTimesInfo; - + /** * 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, @@ -354,81 +358,83 @@ public class RaceMap extends AbstractCompositeComponent impleme */ private int startedProcessingRequestID; - private RaceMapImageManager raceMapImageManager; + private RaceMapImageManager raceMapImageManager; private RaceMapSettings settings; private final RaceMapLifecycle raceMapLifecycle; - + private final StringMessages stringMessages; - + private boolean isMapInitialized; private Date lastTimeChangeBeforeInitialization; - + private int lastLegNumber; /** * The strategy for maintaining and delivering the "quick ranks" information. The provider will be informed about - * quick ranks received from a {@link RaceMapDataDTO#quickRanks} field but may choose to ignore this information, e.g., - * if it can assume that more current information about ranks and leg numbers is available from a {@link LeaderboardDTO}. + * quick ranks received from a {@link RaceMapDataDTO#quickRanks} field but may choose to ignore this information, + * e.g., if it can assume that more current information about ranks and leg numbers is available from a + * {@link LeaderboardDTO}. */ private QuickRanksDTOProvider quickRanksDTOProvider; - + private final CombinedWindPanel combinedWindPanel; private final TrueNorthIndicatorPanel trueNorthIndicatorPanel; private final FlowPanel topLeftControlsWrapperPanel; - + private final AsyncActionsExecutor asyncActionsExecutor; /** - * The map bounds as last received by map callbacks; used to determine whether to suppress the boat animation during zoom/pan + * The map bounds as last received by map callbacks; used to determine whether to suppress the boat animation during + * zoom/pan */ private LatLngBounds currentMapBounds; // bounds to which bounds-changed-handler compares - private int currentZoomLevel; // zoom-level to which bounds-changed-handler compares - - private boolean autoZoomIn = false; // flags auto-zoom-in in progress + private int currentZoomLevel; // zoom-level to which bounds-changed-handler compares + + private boolean autoZoomIn = false; // flags auto-zoom-in in progress private boolean autoZoomOut = false; // flags auto-zoom-out in progress - private int autoZoomLevel; // zoom-level to which auto-zoom-in/-out is zooming - LatLngBounds autoZoomLatLngBounds; // bounds to which auto-zoom-in/-out is panning&zooming - + private int autoZoomLevel; // zoom-level to which auto-zoom-in/-out is zooming + LatLngBounds autoZoomLatLngBounds; // bounds to which auto-zoom-in/-out is panning&zooming + private RaceSimulationOverlay simulationOverlay; private WindStreamletsRaceboardOverlay streamletOverlay; - + private final boolean isSimulationEnabled; - + private static final String GET_POLAR_CATEGORY = "getPolar"; - + /** * Tells about the availability of polar / VPP data for this race. If available, the simulation feature can be * offered to the user. */ private boolean hasPolar; - + private final RegattaAndRaceIdentifier raceIdentifier; - + /** - * When the user requests wind-up display this may happen at a point where no mark positions are known or when - * no wind direction is known yet. In this case, this flag will be set, and when wind information or course mark + * When the user requests wind-up display this may happen at a point where no mark positions are known or when no + * wind direction is known yet. In this case, this flag will be set, and when wind information or course mark * positions are received later, this flag is checked, and if set, a {@link #updateCoordinateSystemFromSettings()} * call is issued to make sure that the user's request for a new coordinate system is honored. */ private boolean requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown; - + /** - * Tells whether currently an auto-zoom is in progress; this is used particularly to keep the smooth CSS boat transitions - * active while auto-zooming whereas stopping them seems the better option for manual zooms. + * Tells whether currently an auto-zoom is in progress; this is used particularly to keep the smooth CSS boat + * transitions active while auto-zooming whereas stopping them seems the better option for manual zooms. */ private boolean autoZoomInProgress; - + /** - * Tells whether currently an orientation change is in progress; this is required handle map events during the configuration of the map - * during an orientation change. + * Tells whether currently an orientation change is in progress; this is required handle map events during the + * configuration of the map during an orientation change. */ private boolean orientationChangeInProgress; - + private final NumberFormat numberFormatOneDecimal = NumberFormat.getFormat("0.0"); private final NumberFormat numberFormatNoDecimal = NumberFormat.getFormat("0"); - + /** * The competitor for which the advantage line is currently showing. Should this competitor's quick rank change, or * should ranks be received while this field is {@code null}, the advantage line @@ -438,7 +444,7 @@ public class RaceMap extends AbstractCompositeComponent impleme protected Label estimatedDurationOverlay; private RaceMapStyle raceMapStyle; private final boolean showHeaderPanel; - + /** Callback to set the visibility of the wind chart. */ private final Consumer showWindChartForProvider; private ManagedInfoWindow managedInfoWindow; @@ -446,31 +452,36 @@ public class RaceMap extends AbstractCompositeComponent impleme private class AdvantageLineUpdater implements QuickRanksListener { @Override public void rankChanged(String competitorIdAsString, QuickRankDTO oldQuickRank, QuickRankDTO quickRank) { - if (advantageLineCompetitor == null || - (oldQuickRank != null && advantageLineCompetitor.getIdAsString().equals(oldQuickRank.competitor.getIdAsString())) || - (quickRank != null && advantageLineCompetitor.getIdAsString().equals(quickRank.competitor.getIdAsString()))) { - showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), /* timeForPositionTransitionMillis */ -1 /* (no transition) */); + if (advantageLineCompetitor == null + || (oldQuickRank != null + && advantageLineCompetitor.getIdAsString().equals(oldQuickRank.competitor.getIdAsString())) + || (quickRank != null + && advantageLineCompetitor.getIdAsString().equals(quickRank.competitor.getIdAsString()))) { + showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), + /* timeForPositionTransitionMillis */ -1 /* (no transition) */); } } } - + public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, - RaceMapSettings raceMapSettings, - SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, - ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, - RaceCompetitorSet raceCompetitorSet, StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, - RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider) { + RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, + AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer timer, + RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, + StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, + boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider) { this(parent, context, raceMapLifecycle, raceMapSettings, sailingService, asyncActionsExecutor, errorReporter, timer, competitorSelection, raceCompetitorSet, stringMessages, raceIdentifier, raceMapResources, - showHeaderPanel, quickRanksDTOProvider, visible -> {}); + showHeaderPanel, quickRanksDTOProvider, visible -> { + }); } - + public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, - RaceMapSettings raceMapSettings, - SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, - ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, - StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, - RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, Consumer showWindChartForProvider) { + RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, + AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer timer, + RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, + StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, + boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, + Consumer showWindChartForProvider) { super(parent, context); this.showHeaderPanel = showHeaderPanel; this.quickRanksDTOProvider = quickRanksDTOProvider; @@ -513,9 +524,11 @@ public class RaceMap extends AbstractCompositeComponent impleme initializeData(settings.isShowMapControls(), showHeaderPanel); raceMapStyle = raceMapResources.raceMapStyle(); raceMapStyle.ensureInjected(); - combinedWindPanel = new CombinedWindPanel(this, raceMapImageManager, raceMapStyle, stringMessages, coordinateSystem); + combinedWindPanel = new CombinedWindPanel(this, raceMapImageManager, raceMapStyle, stringMessages, + coordinateSystem); combinedWindPanel.setVisible(false); - trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, raceMapStyle, stringMessages, coordinateSystem); + trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, raceMapStyle, stringMessages, + coordinateSystem); trueNorthIndicatorPanel.setVisible(true); topLeftControlsWrapperPanel = new FlowPanel(); topLeftControlsWrapperPanel.add(combinedWindPanel); @@ -526,20 +539,22 @@ public class RaceMap extends AbstractCompositeComponent impleme initWidget(rootPanel); this.setSize("100%", "100%"); } - + public void setQuickRanksDTOProvider(QuickRanksDTOProvider newQuickRanksDTOProvider) { if (this.quickRanksDTOProvider != null) { this.quickRanksDTOProvider.moveListernersTo(newQuickRanksDTOProvider); } this.quickRanksDTOProvider = newQuickRanksDTOProvider; } + /** * The {@link WindDTO#dampenedTrueWindFromDeg} direction if {@link #lastCombinedWindTrackInfoDTO} has a * {@link WindSourceType#COMBINED} source which has at least one fix recorded; null otherwise. */ private Bearing getLastCombinedTrueWindFromDirection() { if (lastCombinedWindTrackInfoDTO != null) { - for (Entry e : lastCombinedWindTrackInfoDTO.windTrackInfoByWindSource.entrySet()) { + for (Entry e : lastCombinedWindTrackInfoDTO.windTrackInfoByWindSource + .entrySet()) { if (e.getKey().getType() == WindSourceType.COMBINED) { final List windFixes = e.getValue().windFixes; if (!windFixes.isEmpty()) { @@ -550,7 +565,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } return null; } - + /** * @return {@code true} if the map was redrawn by the call to this method */ @@ -563,7 +578,8 @@ public class RaceMap extends AbstractCompositeComponent impleme if (centerOfCourse != null) { final Bearing lastCombinedTrueWindFromDirection = getLastCombinedTrueWindFromDirection(); if (lastCombinedTrueWindFromDirection != null) { - // new equator shall point 90deg right of the "from" wind direction to make wind come from top of map + // new equator shall point 90deg right of the "from" wind direction to make wind come from top of + // map coordinateSystem.setCoordinateSystem(new RotateAndTranslateCoordinateSystem(centerOfCourse, lastCombinedTrueWindFromDirection.add(new DegreeBearingImpl(90)))); if (map != null) { @@ -590,17 +606,19 @@ public class RaceMap extends AbstractCompositeComponent impleme } coordinateSystem.setCoordinateSystem(new IdentityCoordinateSystem()); } - if (mapOptions != null) { // if no coordinate system change happened that affects an existing map, don't redraw + if (mapOptions != null) { // if no coordinate system change happened that affects an existing map, don't redraw fixesAndTails.clearTails(); redraw(); redrawn = true; - // zooming and setting options while the event loop is still working doesn't work reliably; defer until event loop returns + // zooming and setting options while the event loop is still working doesn't work reliably; defer until + // event loop returns Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { if (map != null) { map.setOptions(mapOptions); - // ensure zooming to what the settings tell, or defaults if what the settings tell isn't possible right now + // ensure zooming to what the settings tell, or defaults if what the settings tell isn't + // possible right now mapFirstZoomDone = false; trueNorthIndicatorPanel.redraw(); orientationChangeInProgress = false; @@ -620,116 +638,122 @@ public class RaceMap extends AbstractCompositeComponent impleme loadLibraries.add(LoadLibrary.GEOMETRY); Runnable onLoad = new Runnable() { - @Override - public void run() { - MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false); - map = new MapWidget(mapOptions); - rootPanel.add(map, 0, 0); - if (showHeaderPanel) { - Image sapLogo = createSAPLogo(); - rootPanel.add(sapLogo); - } - - map.setControls(ControlPosition.LEFT_TOP, topLeftControlsWrapperPanel); - adjustLeftControlsIndent(); + @Override + public void run() { + MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false); + map = new MapWidget(mapOptions); + rootPanel.add(map, 0, 0); + if (showHeaderPanel) { + Image sapLogo = createSAPLogo(); + rootPanel.add(sapLogo); + } - RaceMap.this.raceMapImageManager.loadMapIcons(map); - map.setSize("100%", "100%"); - map.addZoomChangeHandler(new ZoomChangeMapHandler() { - @Override - public void onEvent(ZoomChangeMapEvent event) { - if (!autoZoomIn && !autoZoomOut && !orientationChangeInProgress) { - // stop automatic zoom after a manual zoom event; automatic zoom in zoomMapToNewBounds will restore old settings - final List emptyList = Collections.emptyList(); - RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, settings.getZoomSettings().isZoomToSelectedCompetitors()); - settings = new RaceMapSettings(settings, clearedZoomSettings); - } - // TODO bug489 when in wind-up mode, avoid zooming out too far; perhaps zoom back in if zoomed out too far - } - }); - map.addDragEndHandler(new DragEndMapHandler() { - @Override - public void onEvent(DragEndMapEvent event) { - // stop automatic zoom after a manual drag event - autoZoomIn = false; - autoZoomOut = false; - final List emptyList = Collections.emptyList(); - RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, settings.getZoomSettings().isZoomToSelectedCompetitors()); - settings = new RaceMapSettings(settings, clearedZoomSettings); - } - }); - map.addIdleHandler(new IdleMapHandler() { - @Override - public void onEvent(IdleMapEvent event) { - // the "idle"-event is raised at the end of map-animations - if (autoZoomIn) { - // finalize zoom-in that was started with panTo() in zoomMapToNewBounds() - map.setZoom(autoZoomLevel); - autoZoomIn = false; - } - if (autoZoomOut) { - // finalize zoom-out that was started with setZoom() in zoomMapToNewBounds() - map.panTo(autoZoomLatLngBounds.getCenter()); - autoZoomOut = false; - } - } - }); - map.addBoundsChangeHandler(new BoundsChangeMapHandler() { - @Override - public void onEvent(BoundsChangeMapEvent event) { - int newZoomLevel = map.getZoom(); - if (!isAutoZoomInProgress() && (newZoomLevel != currentZoomLevel)) { - removeTransitions(); - } - if ((streamletOverlay != null) && !map.getBounds().equals(currentMapBounds)) { - streamletOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); - } - if ((simulationOverlay != null) && !map.getBounds().equals(currentMapBounds)) { - simulationOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); - } - currentMapBounds = map.getBounds(); - currentZoomLevel = newZoomLevel; - headerPanel.getElement().getStyle().setWidth(map.getOffsetWidth(), Unit.PX); - } - }); - - // If there was a time change before the API was loaded, reset the time - if (lastTimeChangeBeforeInitialization != null) { - timeChanged(lastTimeChangeBeforeInitialization, null); - lastTimeChangeBeforeInitialization = null; - } - // Initialize streamlet canvas for wind visualization; it shouldn't be doing anything unless it's visible - streamletOverlay = new WindStreamletsRaceboardOverlay(getMap(), /* zIndex */ 0, - timer, raceIdentifier, sailingService, asyncActionsExecutor, stringMessages, coordinateSystem); - streamletOverlay.addToMap(); - if (settings.isShowWindStreamletOverlay()) { - streamletOverlay.setColors(settings.isShowWindStreamletColors()); - streamletOverlay.setVisible(true); - } + map.setControls(ControlPosition.LEFT_TOP, topLeftControlsWrapperPanel); + adjustLeftControlsIndent(); - if (isSimulationEnabled) { - // determine availability of polar diagram - setHasPolar(); - // initialize simulation canvas - simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, sailingService, stringMessages, asyncActionsExecutor, coordinateSystem); - simulationOverlay.addToMap(); - showSimulationOverlay(settings.isShowSimulationOverlay()); - } - if (showHeaderPanel) { - createHeaderPanel(map); - } - if (showMapControls) { - createSettingsButton(map); - } - // Data has been initialized - RaceMap.this.isMapInitialized = true; - RaceMap.this.redraw(); - trueNorthIndicatorPanel.redraw(); - showAdditionalControls(map); - RaceMap.this.managedInfoWindow = new ManagedInfoWindow(map); - } + RaceMap.this.raceMapImageManager.loadMapIcons(map); + map.setSize("100%", "100%"); + map.addZoomChangeHandler(new ZoomChangeMapHandler() { + @Override + public void onEvent(ZoomChangeMapEvent event) { + if (!autoZoomIn && !autoZoomOut && !orientationChangeInProgress) { + // stop automatic zoom after a manual zoom event; automatic zoom in zoomMapToNewBounds will + // restore old settings + final List emptyList = Collections.emptyList(); + RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, + settings.getZoomSettings().isZoomToSelectedCompetitors()); + settings = new RaceMapSettings(settings, clearedZoomSettings); + } + // TODO bug489 when in wind-up mode, avoid zooming out too far; perhaps zoom back in if zoomed + // out too far + } + }); + map.addDragEndHandler(new DragEndMapHandler() { + @Override + public void onEvent(DragEndMapEvent event) { + // stop automatic zoom after a manual drag event + autoZoomIn = false; + autoZoomOut = false; + final List emptyList = Collections.emptyList(); + RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, + settings.getZoomSettings().isZoomToSelectedCompetitors()); + settings = new RaceMapSettings(settings, clearedZoomSettings); + } + }); + map.addIdleHandler(new IdleMapHandler() { + @Override + public void onEvent(IdleMapEvent event) { + // the "idle"-event is raised at the end of map-animations + if (autoZoomIn) { + // finalize zoom-in that was started with panTo() in zoomMapToNewBounds() + map.setZoom(autoZoomLevel); + autoZoomIn = false; + } + if (autoZoomOut) { + // finalize zoom-out that was started with setZoom() in zoomMapToNewBounds() + map.panTo(autoZoomLatLngBounds.getCenter()); + autoZoomOut = false; + } + } + }); + map.addBoundsChangeHandler(new BoundsChangeMapHandler() { + @Override + public void onEvent(BoundsChangeMapEvent event) { + int newZoomLevel = map.getZoom(); + if (!isAutoZoomInProgress() && (newZoomLevel != currentZoomLevel)) { + removeTransitions(); + } + if ((streamletOverlay != null) && !map.getBounds().equals(currentMapBounds)) { + streamletOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); + } + if ((simulationOverlay != null) && !map.getBounds().equals(currentMapBounds)) { + simulationOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); + } + currentMapBounds = map.getBounds(); + currentZoomLevel = newZoomLevel; + headerPanel.getElement().getStyle().setWidth(map.getOffsetWidth(), Unit.PX); + } + }); + + // If there was a time change before the API was loaded, reset the time + if (lastTimeChangeBeforeInitialization != null) { + timeChanged(lastTimeChangeBeforeInitialization, null); + lastTimeChangeBeforeInitialization = null; + } + // Initialize streamlet canvas for wind visualization; it shouldn't be doing anything unless it's + // visible + streamletOverlay = new WindStreamletsRaceboardOverlay(getMap(), /* zIndex */ 0, timer, raceIdentifier, + sailingService, asyncActionsExecutor, stringMessages, coordinateSystem); + streamletOverlay.addToMap(); + if (settings.isShowWindStreamletOverlay()) { + streamletOverlay.setColors(settings.isShowWindStreamletColors()); + streamletOverlay.setVisible(true); + } + + if (isSimulationEnabled) { + // determine availability of polar diagram + setHasPolar(); + // initialize simulation canvas + simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, + sailingService, stringMessages, asyncActionsExecutor, coordinateSystem); + simulationOverlay.addToMap(); + showSimulationOverlay(settings.isShowSimulationOverlay()); + } + if (showHeaderPanel) { + createHeaderPanel(map); + } + if (showMapControls) { + createSettingsButton(map); + } + // Data has been initialized + RaceMap.this.isMapInitialized = true; + RaceMap.this.redraw(); + trueNorthIndicatorPanel.redraw(); + showAdditionalControls(map); + RaceMap.this.managedInfoWindow = new ManagedInfoWindow(map); + } }; - LoadApi.go(onLoad, loadLibraries, sensor, "key="+GoogleMapAPIKey.V3_APIKey); + LoadApi.go(onLoad, loadLibraries, sensor, "key=" + GoogleMapAPIKey.V3_APIKey); } /** @@ -758,14 +782,13 @@ public class RaceMap extends AbstractCompositeComponent impleme } /** - * Creates a header panel where additional information can be displayed by using - * {@link #getLeftHeaderPanel()} or {@link #getRightHeaderPanel()}. + * Creates a header panel where additional information can be displayed by using {@link #getLeftHeaderPanel()} or + * {@link #getRightHeaderPanel()}. * - * This panel is transparent and configured in such a way that it moves other controls - * down by its height. To achieve the goal of not having added widgets transparent - * this widget consists of two parts: First one is the transparent panel and the - * second one is the panel for the controls. The controls then need to moved onto - * the panel by using CSS. + * This panel is transparent and configured in such a way that it moves other controls down by its height. To + * achieve the goal of not having added widgets transparent this widget consists of two parts: First one is the + * transparent panel and the second one is the panel for the controls. The controls then need to moved onto the + * panel by using CSS. */ private void createHeaderPanel(MapWidget map) { // we need a panel that does not have any transparency to have the @@ -785,7 +808,7 @@ public class RaceMap extends AbstractCompositeComponent impleme // controls at RIGHT would not get the correct top setting map.setControls(ControlPosition.TOP_RIGHT, headerPanel); } - + private void createSettingsButton(MapWidget map) { final Component component = this; Button settingsButton = new Button(); @@ -822,11 +845,11 @@ public class RaceMap extends AbstractCompositeComponent impleme public void redraw() { timeChanged(timer.getTime(), null); } - + Map getBoatOverlays() { return Collections.unmodifiableMap(boatOverlays); } - + protected RaceCompetitorSelectionProvider getCompetitorSelection() { return competitorSelection; } @@ -842,33 +865,36 @@ public class RaceMap extends AbstractCompositeComponent impleme public MapWidget getMap() { return map; } - + public RaceSimulationOverlay getSimulationOverlay() { return simulationOverlay; } - + /** * @return the Panel where labels or other controls for the header can be positioned */ public AbsolutePanel getLeftHeaderPanel() { return panelForLeftHeaderLabels; } - + public AbsolutePanel getRightHeaderPanel() { return panelForRightHeaderLabels; } - + @Override - public void raceTimesInfosReceived(Map raceTimesInfos, long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { - timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived); + public void raceTimesInfosReceived(Map raceTimesInfos, + long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { + timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, + clientTimeWhenResponseWasReceived); this.lastRaceTimesInfo = raceTimesInfos.get(raceIdentifier); } /** - * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard contents}, null - * is used as time point. The condition for this is encapsulated in this method so others can find out. For example, when a time change - * is signaled due to local offset / delay adjustments, no additional call to {@link #loadCompleteLeaderboard(Date)} would be required - * as null will be passed in any case, not being affected by local time offsets. + * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard + * contents}, null is used as time point. The condition for this is encapsulated in this method so + * others can find out. For example, when a time change is signaled due to local offset / delay adjustments, no + * additional call to {@link #loadCompleteLeaderboard(Date)} would be required as null will be passed + * in any case, not being affected by local time offsets. */ private boolean useNullAsTimePoint() { return timer.getPlayMode() == PlayModes.Live; @@ -882,57 +908,66 @@ public class RaceMap extends AbstractCompositeComponent impleme final Iterable competitorsToShow = getCompetitorsToShow(); if (race != null) { final long transitionTimeInMillis = calculateTimeForPositionTransitionInMillis(newTime, oldTime); - final com.sap.sse.common.Util.Triple, Map, Map> fromAndToAndOverlap = - fixesAndTails.computeFromAndTo(newTime, competitorsToShow, settings.getEffectiveTailLengthInMilliseconds()); + final com.sap.sse.common.Util.Triple, Map, Map> fromAndToAndOverlap = fixesAndTails + .computeFromAndTo(newTime, competitorsToShow, + settings.getEffectiveTailLengthInMilliseconds()); // Request map data update, possibly in two calls; see method details - callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers(fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow); + callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers( + fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow); // draw the wind into the map, get the combined wind List windSourceTypeNames = new ArrayList(); windSourceTypeNames.add(WindSourceType.EXPEDITION.name()); windSourceTypeNames.add(WindSourceType.WINDFINDER.name()); windSourceTypeNames.add(WindSourceType.COMBINED.name()); - GetWindInfoAction getWindInfoAction = new GetWindInfoAction(sailingService, race, newTime, 1000L, 1, windSourceTypeNames, + GetWindInfoAction getWindInfoAction = new GetWindInfoAction(sailingService, race, newTime, 1000L, 1, + windSourceTypeNames, /* onlyUpToNewestEvent==false means get us any data we can get by a best effort */ false); - asyncActionsExecutor.execute(getWindInfoAction, GET_WIND_DATA_CATEGORY, new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining wind information: " + caught.getMessage(), true /*silentMode */); - } - - @Override - public void onSuccess(WindInfoForRaceDTO windInfo) { - List> windSourcesToShow = new ArrayList>(); - if (windInfo != null) { - lastCombinedWindTrackInfoDTO = windInfo; - showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); - for (WindSource windSource : windInfo.windTrackInfoByWindSource.keySet()) { - WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource.get(windSource); - switch (windSource.getType()) { - case EXPEDITION: - case WINDFINDER: - // we filter out measured wind sources with very low confidence - if (windTrackInfoDTO.minWindConfidence > 0.0001) { - windSourcesToShow.add(new com.sap.sse.common.Util.Pair(windSource, windTrackInfoDTO)); - } - break; - case COMBINED: - showCombinedWindOnMap(windSource, windTrackInfoDTO); - if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { - updateCoordinateSystemFromSettings(); - } - break; - default: - // Which wind sources are requested is defined in a list above this - // action. So we throw here an exception to notice a missing source. - throw new UnsupportedOperationException( - "There is currently no support for the enum value '" - + windSource.getType() + "' in this method."); - } + asyncActionsExecutor.execute(getWindInfoAction, GET_WIND_DATA_CATEGORY, + new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError( + "Error obtaining wind information: " + caught.getMessage(), + true /* silentMode */); } - } - showWindSensorsOnMap(windSourcesToShow); - } - }); + + @Override + public void onSuccess(WindInfoForRaceDTO windInfo) { + List> windSourcesToShow = new ArrayList>(); + if (windInfo != null) { + lastCombinedWindTrackInfoDTO = windInfo; + showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); + for (WindSource windSource : windInfo.windTrackInfoByWindSource.keySet()) { + WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource + .get(windSource); + switch (windSource.getType()) { + case EXPEDITION: + case WINDFINDER: + // we filter out measured wind sources with very low confidence + if (windTrackInfoDTO.minWindConfidence > 0.0001) { + windSourcesToShow.add( + new com.sap.sse.common.Util.Pair( + windSource, windTrackInfoDTO)); + } + break; + case COMBINED: + showCombinedWindOnMap(windSource, windTrackInfoDTO); + if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { + updateCoordinateSystemFromSettings(); + } + break; + default: + // Which wind sources are requested is defined in a list above this + // action. So we throw here an exception to notice a missing source. + throw new UnsupportedOperationException( + "There is currently no support for the enum value '" + + windSource.getType() + "' in this method."); + } + } + } + showWindSensorsOnMap(windSourcesToShow); + } + }); } } } @@ -941,6 +976,7 @@ public class RaceMap extends AbstractCompositeComponent impleme public WindInfoForRaceDTO getLastCombinedWindTrackInfoDTO() { return lastCombinedWindTrackInfoDTO; } + /** * Requests updates for map data and, when received, updates the map structures accordingly. *

@@ -961,12 +997,14 @@ public class RaceMap extends AbstractCompositeComponent impleme * current tail length setting the new tail segment does not overlap with the old one, requiring a full load of the * entire tail data for that competitor. For these non-overlapping requests, this method creates a * {@link GetBoatPositionsAction} request loading the entire tail required, but not quick ranks, sidelines and mark - * positions. Updating the results of this call is done in {@link #updateBoatPositions(Date, long, Map, Iterable, Map, boolean)}. + * positions. Updating the results of this call is done in + * {@link #updateBoatPositions(Date, long, Map, Iterable, Map, boolean)}. *

*/ private void callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers( final Triple, Map, Map> fromAndToAndOverlap, - RegattaAndRaceIdentifier race, final Date newTime, final long transitionTimeInMillis, final Iterable competitorsToShow) { + RegattaAndRaceIdentifier race, final Date newTime, final long transitionTimeInMillis, + final Iterable competitorsToShow) { final Map fromTimesForQuickCall = new HashMap<>(); final Map toTimesForQuickCall = new HashMap<>(); final Map fromTimesForNonOverlappingTailsCall = new HashMap<>(); @@ -977,7 +1015,8 @@ public class RaceMap extends AbstractCompositeComponent impleme fromTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); toTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getB().get(e.getKey())); } else { - // no overlap; add competitor to request with a zero-length interval asking only position at newTime, not the entire tail + // no overlap; add competitor to request with a zero-length interval asking only position at newTime, + // not the entire tail fromTimesForQuickCall.put(e.getKey(), newTime); toTimesForQuickCall.put(e.getKey(), newTime); fromTimesForNonOverlappingTailsCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); @@ -995,46 +1034,56 @@ public class RaceMap extends AbstractCompositeComponent impleme // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for // all its entries so that the other response received for GetBoatPositionsAction will consider this an // overlap if it happens after this update. - asyncActionsExecutor.execute(new GetRaceMapDataAction(sailingService, competitorsByIdAsString, - race, useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, /* extrapolate */true, - (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null), - raceCompetitorSet.getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID(), newTime, settings.isShowEstimatedDuration()), - GET_RACE_MAP_DATA_CATEGORY, - getRaceMapDataCallback(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter)); - // next, if necessary, do the full thing; the two calls have different action classes, so throttling should not drop one for the other + asyncActionsExecutor.execute( + new GetRaceMapDataAction(sailingService, competitorsByIdAsString, race, + useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, + /* extrapolate */true, + (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null), + raceCompetitorSet + .getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID(), + newTime, settings.isShowEstimatedDuration()), + GET_RACE_MAP_DATA_CATEGORY, getRaceMapDataCallback(newTime, transitionTimeInMillis, + fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter)); + // next, if necessary, do the full thing; the two calls have different action classes, so throttling should not + // drop one for the other if (!fromTimesForNonOverlappingTailsCall.keySet().isEmpty()) { - asyncActionsExecutor.execute(new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, toTimesForNonOverlappingTailsCall, - /* extrapolate */ true), GET_RACE_MAP_DATA_CATEGORY, - new MarkedAsyncCallback<>(new AsyncCallback() { + asyncActionsExecutor.execute( + new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, + toTimesForNonOverlappingTailsCall, /* extrapolate */ true), + GET_RACE_MAP_DATA_CATEGORY, new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable t) { - errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), true /*silentMode */); + errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), + true /* silentMode */); } - + @Override public void onSuccess(CompactBoatPositionsDTO result) { - // Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to updateBoatPositions for those - // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for - // all its entries so that the other response received for GetRaceMapDataAction will consider this an + // Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to + // updateBoatPositions for those + // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will + // contain true for + // all its entries so that the other response received for GetRaceMapDataAction will + // consider this an // overlap if it happens after this update. - updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, - result.getBoatPositionsForCompetitors(competitorsByIdAsString), /* updateTailsOnly */ true); + updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), + competitorsToShow, result.getBoatPositionsForCompetitors(competitorsByIdAsString), + /* updateTailsOnly */ true); } })); } } - private AsyncCallback getRaceMapDataCallback( - final Date newTime, - final long transitionTimeInMillis, + private AsyncCallback getRaceMapDataCallback(final Date newTime, final long transitionTimeInMillis, final Map hasTailOverlapForCompetitor, final Iterable competitorsToShow, final int requestID) { return new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining racemap data: " + caught.getMessage(), true /*silentMode */); + errorReporter.reportError("Error obtaining racemap data: " + caught.getMessage(), + true /* silentMode */); } - + @Override public void onSuccess(RaceMapDataDTO raceMapDataDTO) { if (map != null && raceMapDataDTO != null) { @@ -1043,27 +1092,33 @@ public class RaceMap extends AbstractCompositeComponent impleme startedProcessingRequestID = requestID; if (raceMapDataDTO.raceCompetitorIdsAsStrings != null) { try { - raceCompetitorSet.setIdsAsStringsOfCompetitorsInRace(raceMapDataDTO.raceCompetitorIdsAsStrings); + raceCompetitorSet + .setIdsAsStringsOfCompetitorsInRace(raceMapDataDTO.raceCompetitorIdsAsStrings); } catch (Exception e) { - GWT.log("Error trying to update competitor set for race "+raceIdentifier.getRaceName()+ - " in regatta "+raceIdentifier.getRegattaName(), e); + GWT.log("Error trying to update competitor set for race " + raceIdentifier.getRaceName() + + " in regatta " + raceIdentifier.getRegattaName(), e); } } quickRanksDTOProvider.quickRanksReceivedFromServer(raceMapDataDTO.quickRanks); if (isSimulationEnabled && settings.isShowSimulationOverlay()) { lastLegNumber = raceMapDataDTO.coursePositions.currentLegNumber; - simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion); + simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, + raceMapDataDTO.simulationResultVersion); } // Do boat specific actions Map> boatData = raceMapDataDTO.boatPositions; - updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, competitorsToShow, boatData, /* updateTailsOnly */ false); + updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, + competitorsToShow, boatData, /* updateTailsOnly */ false); if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); } if (maneuverMarkers != null) { removeAllManeuverMarkers(); } - + if (maneuverLossLines != null) { + removeManeuverLossLines(); + } + if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } @@ -1072,29 +1127,30 @@ public class RaceMap extends AbstractCompositeComponent impleme showCourseSidelinesOnMap(raceMapDataDTO.courseSidelines); showStartAndFinishAndCourseMiddleLines(raceMapDataDTO.coursePositions); showStartLineToFirstMarkTriangle(raceMapDataDTO.coursePositions); - + // Rezoom the map LatLngBounds zoomToBounds = null; - if (!settings.getZoomSettings().containsZoomType(ZoomTypes.NONE)) { // Auto zoom if setting is not manual + if (!settings.getZoomSettings().containsZoomType(ZoomTypes.NONE)) { // Auto zoom if setting is + // not manual zoomToBounds = settings.getZoomSettings().getNewBounds(RaceMap.this); if (zoomToBounds == null && !mapFirstZoomDone) { - zoomToBounds = getDefaultZoomBounds(); // the user-specified zoom couldn't find what it was looking for; try defaults once + zoomToBounds = getDefaultZoomBounds(); // the user-specified zoom couldn't find what it + // was looking for; try defaults once } } else if (!mapFirstZoomDone) { // Zoom once to the marks if marks exist zoomToBounds = new CourseMarksBoundsCalculator().calculateNewBounds(RaceMap.this); if (zoomToBounds == null) { - zoomToBounds = getDefaultZoomBounds(); // use default zoom, e.g., + zoomToBounds = getDefaultZoomBounds(); // use default zoom, e.g., } /* - * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that - * the map was just zoomed to the bounds of the marks, it was not a zoom or pan - * triggered by the user. As a consequence the - * mapZoomedOrPannedSinceLastRaceSelection option has to reset again. + * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that the map was + * just zoomed to the bounds of the marks, it was not a zoom or pan triggered by the user. + * As a consequence the mapZoomedOrPannedSinceLastRaceSelection option has to reset again. */ } zoomMapToNewBounds(zoomToBounds); mapFirstZoomDone = true; - + updateEstimatedDuration(raceMapDataDTO.estimatedDuration); } } else { @@ -1111,13 +1167,13 @@ public class RaceMap extends AbstractCompositeComponent impleme if (estimatedDurationOverlay == null) { estimatedDurationOverlay = new Label(""); estimatedDurationOverlay.setStyleName(raceMapStyle.estimatedDuration()); - if(showHeaderPanel) { + if (showHeaderPanel) { estimatedDurationOverlay.addStyleName(raceMapStyle.estimatedDurationWithHeader()); } map.setControls(ControlPosition.TOP_CENTER, estimatedDurationOverlay); } - estimatedDurationOverlay.setText(stringMessages.estimatedDuration() - + " " + DateAndTimeFormatterUtil.formatElapsedTime(estimatedDuration.asMillis())); + estimatedDurationOverlay.setText(stringMessages.estimatedDuration() + " " + + DateAndTimeFormatterUtil.formatElapsedTime(estimatedDuration.asMillis())); } @@ -1125,17 +1181,18 @@ public class RaceMap extends AbstractCompositeComponent impleme * @param hasTailOverlapForCompetitor * if for a competitor whose fixes are provided in fixesForCompetitors this holds * false, any fixes previously stored for that competitor are removed, and the tail is - * deleted from the map (see {@link #removeTail(CompetitorWithBoatDTO)}); the new fixes are then added to the - * {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). If - * this map holds true, {@link #mergeFixes(CompetitorWithBoatDTO, List, long)} is used to merge the - * new fixes from fixesForCompetitors into the {@link #fixes} collection, and the tail is - * left unchanged. NOTE: When a non-overlapping set of fixes is updated (false), this - * map's record for the competitor is UPDATED to true after the tail deletion and - * {@link #fixes} replacement has taken place. This helps in cases where this update is only one of two - * into which an original request was split (one quick update of the tail's head and another one for the - * longer tail itself), such that the second request that uses the same map will be considered - * having an overlap now, not leading to a replacement of the previous update originating from the same - * request. See also {@link FixesAndTails#updateFixes(Map, Map, TailFactory, long)}. + * deleted from the map (see {@link #removeTail(CompetitorWithBoatDTO)}); the new fixes are then added to + * the {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). + * If this map holds true, {@link #mergeFixes(CompetitorWithBoatDTO, List, long)} is used to + * merge the new fixes from fixesForCompetitors into the {@link #fixes} collection, and the + * tail is left unchanged. NOTE: When a non-overlapping set of fixes is updated + * (false), this map's record for the competitor is UPDATED to true + * after the tail deletion and {@link #fixes} replacement has taken place. This helps in cases where this + * update is only one of two into which an original request was split (one quick update of the tail's + * head and another one for the longer tail itself), such that the second request that uses the + * same map will be considered having an overlap now, not leading to a replacement of the + * previous update originating from the same request. See also + * {@link FixesAndTails#updateFixes(Map, Map, TailFactory, long)}. * @param updateTailsOnly * if true, only the tails are updated according to boatData and * hasTailOverlapForCompetitor, but the advantage line is not updated, and neither are the @@ -1146,13 +1203,15 @@ public class RaceMap extends AbstractCompositeComponent impleme */ private void updateBoatPositions(final Date newTime, final long transitionTimeInMillis, final Map hasTailOverlapForCompetitor, - final Iterable competitorsToShow, Map> boatData, boolean updateTailsOnly) { + final Iterable competitorsToShow, + Map> boatData, boolean updateTailsOnly) { fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis); showBoatsOnMap(newTime, transitionTimeInMillis, /* re-calculate; it could have changed since the asynchronous request was made: */ getCompetitorsToShow(), updateTailsOnly); if (!updateTailsOnly) { - showCompetitorInfoOnMap(newTime, transitionTimeInMillis, competitorSelection.getSelectedFilteredCompetitors()); + showCompetitorInfoOnMap(newTime, transitionTimeInMillis, + competitorSelection.getSelectedFilteredCompetitors()); // even though the wind data is retrieved by a separate call, re-draw the advantage line because it needs to // adjust to new boat positions showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); @@ -1160,13 +1219,13 @@ public class RaceMap extends AbstractCompositeComponent impleme } private void showCourseSidelinesOnMap(List sidelinesDTOs) { - if (map != null && sidelinesDTOs != null ) { + if (map != null && sidelinesDTOs != null) { Map toRemoveSidelines = new HashMap(courseSidelines); for (SidelineDTO sidelineDTO : sidelinesDTOs) { if (sidelineDTO.getMarks().size() == 2) { // right now we only support sidelines with 2 marks Polygon sideline = courseSidelines.get(sidelineDTO); LatLng[] sidelinePoints = new LatLng[sidelineDTO.getMarks().size()]; - int i=0; + int i = 0; for (MarkDTO sidelineMark : sidelineDTO.getMarks()) { sidelinePoints[i] = coordinateSystem.toLatLng(sidelineMark.position); i++; @@ -1179,7 +1238,7 @@ public class RaceMap extends AbstractCompositeComponent impleme options.setStrokeOpacity(1.0); options.setFillColor(null); options.setFillOpacity(1.0); - + sideline = Polygon.newInstance(options); MVCArray pointsAsArray = MVCArray.newInstance(sidelinePoints); sideline.setPath(pointsAsArray); @@ -1213,25 +1272,29 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + private void showCourseMarksOnMap(CoursePositionsDTO courseDTO, long transitionTimeInMillis) { if (map != null && courseDTO != null) { WaypointDTO endWaypointForCurrentLegNumber = null; - if(courseDTO.currentLegNumber > 0 && courseDTO.currentLegNumber <= courseDTO.totalLegsCount) { + if (courseDTO.currentLegNumber > 0 && courseDTO.currentLegNumber <= courseDTO.totalLegsCount) { endWaypointForCurrentLegNumber = courseDTO.getEndWaypointForLegNumber(courseDTO.currentLegNumber); } - - Map toRemoveCourseMarks = new HashMap(courseMarkOverlays); + + Map toRemoveCourseMarks = new HashMap( + courseMarkOverlays); if (courseDTO.marks != null) { for (MarkDTO markDTO : courseDTO.marks) { boolean isSelected = false; - if (endWaypointForCurrentLegNumber != null && Util.contains(endWaypointForCurrentLegNumber.controlPoint.getMarks(), markDTO)) { + if (endWaypointForCurrentLegNumber != null + && Util.contains(endWaypointForCurrentLegNumber.controlPoint.getMarks(), markDTO)) { isSelected = true; } CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTO.getIdAsString()); if (courseMarkOverlay == null) { - courseMarkOverlay = new CourseMarkOverlay(map, RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, markDTO, coordinateSystem, courseDTO); - courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); + courseMarkOverlay = new CourseMarkOverlay(map, RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, + markDTO, coordinateSystem, courseDTO); + courseMarkOverlay + .setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); courseMarkOverlay.setBuoyZoneRadius(settings.getBuoyZoneRadius()); courseMarkOverlay.setSelected(isSelected); courseMarkOverlays.put(markDTO.getIdAsString(), courseMarkOverlay); @@ -1240,7 +1303,8 @@ public class RaceMap extends AbstractCompositeComponent impleme courseMarkOverlay.addToMap(); } else { courseMarkOverlay.setMarkPosition(markDTO.position, transitionTimeInMillis); - courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); + courseMarkOverlay + .setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); courseMarkOverlay.setBuoyZoneRadius(settings.getBuoyZoneRadius()); courseMarkOverlay.setSelected(isSelected); courseMarkOverlay.setCourse(courseDTO); @@ -1257,10 +1321,10 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + /** - * Based on the mark positions in {@link #courseMarkOverlays}' values this method determines the center of gravity of these marks' - * {@link CourseMarkOverlay#getPosition() positions}. + * Based on the mark positions in {@link #courseMarkOverlays}' values this method determines the center of gravity + * of these marks' {@link CourseMarkOverlay#getPosition() positions}. */ private Position getCenterOfCourse() { ScalablePosition center = null; @@ -1284,16 +1348,18 @@ public class RaceMap extends AbstractCompositeComponent impleme } } - private void showWindSensorsOnMap(List> windSensorsList) { + private void showWindSensorsOnMap( + List> windSensorsList) { if (map != null) { Set toRemoveWindSources = new HashSet(windSensorOverlays.keySet()); for (com.sap.sse.common.Util.Pair windSourcePair : windSensorsList) { - WindSource windSource = windSourcePair.getA(); + WindSource windSource = windSourcePair.getA(); WindTrackInfoDTO windTrackInfoDTO = windSourcePair.getB(); WindSensorOverlay windSensorOverlay = windSensorOverlays.get(windSource); if (windSensorOverlay == null) { - windSensorOverlay = createWindSensorOverlay(RaceMapOverlaysZIndexes.WINDSENSOR_ZINDEX, windSource, windTrackInfoDTO); + windSensorOverlay = createWindSensorOverlay(RaceMapOverlaysZIndexes.WINDSENSOR_ZINDEX, windSource, + windTrackInfoDTO); windSensorOverlays.put(windSource, windSensorOverlay); windSensorOverlay.addToMap(); } else { @@ -1311,11 +1377,13 @@ public class RaceMap extends AbstractCompositeComponent impleme } } - private void showCompetitorInfoOnMap(final Date newTime, final long timeForPositionTransitionMillis, final Iterable competitorsToShow) { + private void showCompetitorInfoOnMap(final Date newTime, final long timeForPositionTransitionMillis, + final Iterable competitorsToShow) { if (map != null) { if (settings.isShowSelectedCompetitorsInfo()) { Set toRemoveCompetorInfoOverlaysIdsAsStrings = new HashSet<>(); - Util.addAll(competitorInfoOverlays.getCompetitorIdsAsStrings(), toRemoveCompetorInfoOverlaysIdsAsStrings); + Util.addAll(competitorInfoOverlays.getCompetitorIdsAsStrings(), + toRemoveCompetorInfoOverlaysIdsAsStrings); for (CompetitorDTO competitorDTO : competitorsToShow) { if (fixesAndTails.hasFixesFor(competitorDTO)) { GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = getBoatFix(competitorDTO, newTime); @@ -1323,11 +1391,13 @@ public class RaceMap extends AbstractCompositeComponent impleme CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(competitorDTO); final Integer rank = getRank(competitorDTO); if (competitorInfoOverlay == null) { - competitorInfoOverlay = competitorInfoOverlays.createCompetitorInfoOverlay(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO, - lastBoatFix, rank, timeForPositionTransitionMillis); + competitorInfoOverlay = competitorInfoOverlays.createCompetitorInfoOverlay( + RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO, lastBoatFix, rank, + timeForPositionTransitionMillis); competitorInfoOverlay.addToMap(); } else { - competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix, timeForPositionTransitionMillis); + competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix, + timeForPositionTransitionMillis); } toRemoveCompetorInfoOverlaysIdsAsStrings.remove(competitorDTO.getIdAsString()); } @@ -1373,7 +1443,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } return timeForPositionTransitionMillisSmoothed; } - + /** * @param updateTailsOnly * if false, tails of competitors not in competitorsToShow are removed from the @@ -1399,14 +1469,17 @@ public class RaceMap extends AbstractCompositeComponent impleme if (!fixesAndTails.hasTail(competitorDTO)) { fixesAndTails.createTailAndUpdateIndices(competitorDTO, tailsFromTime, tailsToTime, this); } else { - fixesAndTails.updateTail(competitorDTO, tailsFromTime, tailsToTime, (int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2)); + fixesAndTails.updateTail(competitorDTO, tailsFromTime, tailsToTime, + (int) (timeForPositionTransitionMillis == -1 ? -1 + : timeForPositionTransitionMillis / 2)); if (!updateTailsOnly) { competitorDTOsOfUnusedTails.remove(competitorDTO); } PolylineOptions newOptions = createTailStyle(competitorDTO, displayHighlighted(competitorDTO)); fixesAndTails.getTail(competitorDTO).setOptions(newOptions); } - boolean usedExistingBoatCanvas = updateBoatCanvasForCompetitor(competitorDTO, newTime, timeForPositionTransitionMillis); + boolean usedExistingBoatCanvas = updateBoatCanvasForCompetitor(competitorDTO, newTime, + timeForPositionTransitionMillis); if (usedExistingBoatCanvas && !updateTailsOnly) { competitorDTOsOfUnusedBoatCanvases.remove(competitorDTO); } @@ -1431,32 +1504,34 @@ public class RaceMap extends AbstractCompositeComponent impleme * necessary if greater distances are allowed. */ public LatLng calculatePositionAlongRhumbline(LatLng position, double bearingDeg, Distance distance) { - double distanceRad = distance.getCentralAngleRad(); + double distanceRad = distance.getCentralAngleRad(); double lat1 = position.getLatitude() / 180. * Math.PI; double lon1 = position.getLongitude() / 180. * Math.PI; double bearingRad = bearingDeg / 180. * Math.PI; - double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distanceRad) + - Math.cos(lat1) * Math.sin(distanceRad) * Math.cos(bearingRad)); - double lon2 = lon1 + Math.atan2(Math.sin(bearingRad)*Math.sin(distanceRad)*Math.cos(lat1), - Math.cos(distanceRad)-Math.sin(lat1)*Math.sin(lat2)); - lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalize to -180..+180� + double lat2 = Math.asin( + Math.sin(lat1) * Math.cos(distanceRad) + Math.cos(lat1) * Math.sin(distanceRad) * Math.cos(bearingRad)); + double lon2 = lon1 + Math.atan2(Math.sin(bearingRad) * Math.sin(distanceRad) * Math.cos(lat1), + Math.cos(distanceRad) - Math.sin(lat1) * Math.sin(lat2)); + lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180..+180� // position is already in LatLng space, so no mapping through coordinateSystem is required here - return LatLng.newInstance(lat2 / Math.PI * 180., lon2 / Math.PI * 180.); + return LatLng.newInstance(lat2 / Math.PI * 180., lon2 / Math.PI * 180.); } - + /** - * Returns a pair whose first component is the leg number (one-based) of the competitor returned as the second component. - * The competitor returned currently has the best ranking in the quick ranks provided by the {@link #quickRanksDTOProvider}. + * Returns a pair whose first component is the leg number (one-based) of the competitor returned as the second + * component. The competitor returned currently has the best ranking in the quick ranks provided by the + * {@link #quickRanksDTOProvider}. */ private com.sap.sse.common.Util.Pair getBestVisibleCompetitorWithOneBasedLegNumber( Iterable competitorsToShow) { CompetitorDTO leadingCompetitorDTO = null; int legOfLeaderCompetitor = -1; int bestOneBasedRank = Integer.MAX_VALUE; - for (QuickRankDTO competitorFromBestToWorstAndOneBasedLegNumber : quickRanksDTOProvider.getQuickRanks().values()) { - if (Util.contains(competitorsToShow, competitorFromBestToWorstAndOneBasedLegNumber.competitor) && - competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased != 0 && - competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank < bestOneBasedRank) { + for (QuickRankDTO competitorFromBestToWorstAndOneBasedLegNumber : quickRanksDTOProvider.getQuickRanks() + .values()) { + if (Util.contains(competitorsToShow, competitorFromBestToWorstAndOneBasedLegNumber.competitor) + && competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased != 0 + && competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank < bestOneBasedRank) { leadingCompetitorDTO = competitorFromBestToWorstAndOneBasedLegNumber.competitor; legOfLeaderCompetitor = competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased; bestOneBasedRank = competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank; @@ -1465,18 +1540,22 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - return leadingCompetitorDTO == null ? null : - new com.sap.sse.common.Util.Pair(legOfLeaderCompetitor, leadingCompetitorDTO); + return leadingCompetitorDTO == null ? null + : new com.sap.sse.common.Util.Pair(legOfLeaderCompetitor, leadingCompetitorDTO); } - final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with the visible area of the map; bug 616 - private void showAdvantageLine(Iterable competitorsToShow, Date date, long timeForPositionTransitionMillis) { + final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with + // the visible area of the map; bug 616 + + private void showAdvantageLine(Iterable competitorsToShow, Date date, + long timeForPositionTransitionMillis) { if (map != null && lastRaceTimesInfo != null && !quickRanksDTOProvider.getQuickRanks().isEmpty() && lastCombinedWindTrackInfoDTO != null) { boolean drawAdvantageLine = false; if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) { // find competitor with highest rank - com.sap.sse.common.Util.Pair visibleLeaderInfo = getBestVisibleCompetitorWithOneBasedLegNumber(competitorsToShow); + com.sap.sse.common.Util.Pair visibleLeaderInfo = getBestVisibleCompetitorWithOneBasedLegNumber( + competitorsToShow); // the boat fix may be null; may mean that no positions were loaded yet for the leading visible boat; // don't show anything GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = null; @@ -1484,8 +1563,7 @@ public class RaceMap extends AbstractCompositeComponent impleme boolean isLegTypeKnown = false; WindTrackInfoDTO windDataForLegMiddle = null; LegInfoDTO legInfoDTO = null; - if (visibleLeaderInfo != null - && visibleLeaderInfo.getA() > 0 + if (visibleLeaderInfo != null && visibleLeaderInfo.getA() > 0 && visibleLeaderInfo.getA() <= lastRaceTimesInfo.getLegInfos().size() // get wind at middle of leg for leading visible competitor && (windDataForLegMiddle = lastCombinedWindTrackInfoDTO @@ -1498,7 +1576,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } lastBoatFix = getBoatFix(visibleLeaderInfo.getB(), date); } - if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null && lastBoatFix.speedWithBearing != null) { + if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null + && lastBoatFix.speedWithBearing != null) { BoatDTO boat = competitorSelection.getBoat(visibleLeaderInfo.getB()); Distance distanceFromBoatPositionInKm = boat.getBoatClass().getHullLength(); // one hull length // implement and use Position.translateRhumb() @@ -1524,7 +1603,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (rotatedBearingDeg2 < 0.0) { rotatedBearingDeg2 += 360.0; } - break; + break; } case REACHING: { rotatedBearingDeg1 = legInfoDTO.legBearingInDegrees + 90.0; @@ -1535,7 +1614,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (rotatedBearingDeg2 < 0.0) { rotatedBearingDeg2 += 360.0; } - break; + break; } } MVCArray nextPath = MVCArray.newInstance(); @@ -1550,7 +1629,7 @@ public class RaceMap extends AbstractCompositeComponent impleme options.setStrokeColor(ADVANTAGE_LINE_COLOR); options.setStrokeWeight(1); options.setStrokeOpacity(0.5); - + advantageLine = Polyline.newInstance(options); advantageTimer = new AdvantageLineAnimator(advantageLine); MVCArray pointsAsArray = MVCArray.newInstance(); @@ -1559,7 +1638,7 @@ public class RaceMap extends AbstractCompositeComponent impleme advantageLine.setPath(pointsAsArray); advantageLine.setMap(map); Hoverline advantageHoverline = new Hoverline(advantageLine, options, this); - + advantageLineMouseOverHandler = new AdvantageLineMouseOverMapHandler( bearingOfCombinedWindInDeg, new Date(windFix.measureTimepoint)); advantageLine.addMouseOverHandler(advantageLineMouseOverHandler); @@ -1572,7 +1651,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } else { nextPath.push(advantageLinePos1); nextPath.push(advantageLinePos2); - advantageTimer.setNextPositionAndTransitionMillis(nextPath, timeForPositionTransitionMillis); + advantageTimer.setNextPositionAndTransitionMillis(nextPath, + timeForPositionTransitionMillis); if (advantageLineMouseOverHandler != null) { advantageLineMouseOverHandler.setTrueWindBearing(bearingOfCombinedWindInDeg); advantageLineMouseOverHandler.setDate(new Date(windFix.measureTimepoint)); @@ -1592,11 +1672,11 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + private final StringBuilder windwardStartLineMarkToFirstMarkLineText = new StringBuilder(); private final StringBuilder leewardStartLineMarkToFirstMarkLineText = new StringBuilder(); - - private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO){ + + private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO) { final List startMarkPositions = courseDTO.getStartMarkPositions(); if (startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1) { final Position windwardStartLinePosition = startMarkPositions.get(0); @@ -1604,12 +1684,10 @@ public class RaceMap extends AbstractCompositeComponent impleme final Position firstMarkPosition = courseDTO.waypointPositions.get(1); windwardStartLineMarkToFirstMarkLineText.replace(0, windwardStartLineMarkToFirstMarkLineText.length(), stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(windwardStartLinePosition.getDistance(firstMarkPosition) - .getMeters()))); + .format(windwardStartLinePosition.getDistance(firstMarkPosition).getMeters()))); leewardStartLineMarkToFirstMarkLineText.replace(0, leewardStartLineMarkToFirstMarkLineText.length(), stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(leewardStartLinePosition.getDistance(firstMarkPosition) - .getMeters()))); + .format(leewardStartLinePosition.getDistance(firstMarkPosition).getMeters()))); final LineInfoProvider windwardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { @@ -1622,12 +1700,16 @@ public class RaceMap extends AbstractCompositeComponent impleme return leewardStartLineMarkToFirstMarkLineText.toString(); } }; - windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* showLine */ + windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* + * showLine + */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider, "grey"); - leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */ + leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* + * showLine + */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider, @@ -1640,112 +1722,135 @@ public class RaceMap extends AbstractCompositeComponent impleme final LineInfoProvider startLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.startLine()+startLineAdvantageText; + return stringMessages.startLine() + startLineAdvantageText; } }; final LineInfoProvider finishLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.finishLine()+finishLineAdvantageText; + return stringMessages.finishLine() + finishLineAdvantageText; } }; private void showStartAndFinishAndCourseMiddleLines(final CoursePositionsDTO courseDTO) { - if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null && - !courseDTO.course.waypoints.isEmpty()) { + if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null + && !courseDTO.course.waypoints.isEmpty()) { // draw the start line final WaypointDTO startWaypoint = courseDTO.course.waypoints.get(0); updateCountdownCanvas(startWaypoint); - final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 : courseDTO.getStartMarkPositions().size(); - final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 : courseDTO.getFinishMarkPositions().size(); - final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null : courseDTO.getStartMarkPositions().get(0); - final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null : courseDTO.getStartMarkPositions().get(1); + final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 + : courseDTO.getStartMarkPositions().size(); + final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 + : courseDTO.getFinishMarkPositions().size(); + final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null + : courseDTO.getStartMarkPositions().get(0); + final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null + : courseDTO.getStartMarkPositions().get(1); if (courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { - startLineAdvantageText.replace(0, startLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( - numberFormatOneDecimal.format(courseDTO.startLineLengthInMeters), - numberFormatOneDecimal.format(Math.abs(courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), - courseDTO.startLineAdvantageousSide.name().charAt(0)+courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), - numberFormatOneDecimal.format(courseDTO.startLineAdvantageInMeters))); + startLineAdvantageText.replace(0, startLineAdvantageText.length(), + " " + stringMessages.lineAngleToWindAndAdvantage( + numberFormatOneDecimal.format(courseDTO.startLineLengthInMeters), + numberFormatOneDecimal.format(Math.abs( + courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), + courseDTO.startLineAdvantageousSide.name().charAt(0) + + courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), + numberFormatOneDecimal.format(courseDTO.startLineAdvantageInMeters))); } else { startLineAdvantageText.delete(0, startLineAdvantageText.length()); } - final boolean showStartLineBasedOnCurrentLeg = numberOfStartWaypointMarks == 2 && courseDTO.currentLegNumber <= 1; - final boolean showFinishLineBasedOnCurrentLeg = numberOfFinishWaypointMarks == 2 && courseDTO.currentLegNumber == courseDTO.totalLegsCount; + final boolean showStartLineBasedOnCurrentLeg = numberOfStartWaypointMarks == 2 + && courseDTO.currentLegNumber <= 1; + final boolean showFinishLineBasedOnCurrentLeg = numberOfFinishWaypointMarks == 2 + && courseDTO.currentLegNumber == courseDTO.totalLegsCount; // show the line when STARTLINE is selected and the current leg is around the start leg, - // or when COURSEGEOMETRY is selected and the finish line isn't equal and wouldn't be shown at the same time based on the current leg. - // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will not be displayed if + // or when COURSEGEOMETRY is selected and the finish line isn't equal and wouldn't be shown at the same time + // based on the current leg. + // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will not be + // displayed if // based on the race progress the finish line is to be preferred, so only the finish line will be shown. - final boolean reallyShowStartLine = - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) && showStartLineBasedOnCurrentLeg) || - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && - (!showFinishLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO))); + final boolean reallyShowStartLine = (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) + && showStartLineBasedOnCurrentLeg) + || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) + && (!showFinishLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO))); // show the line when FINISHLINE is selected and the current leg is the last leg, // or when COURSEGEOMETRY is selected and the start line isn't equal or the current leg is the last leg. - // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will be displayed unless + // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will be + // displayed unless // the finish line should take precedence based on race progress. - final boolean reallyShowFinishLine = showFinishLineBasedOnCurrentLeg && - (!showStartLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO)) && - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) && showFinishLineBasedOnCurrentLeg) || - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && - (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); - startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, - startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff"); + final boolean reallyShowFinishLine = showFinishLineBasedOnCurrentLeg + && (!showStartLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO)) + && (settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) + && showFinishLineBasedOnCurrentLeg) + || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) + && (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); + startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, startLineLeftPosition, + startLineRightPosition, startLineInfoProvider, "#ffffff"); // draw the finish line - final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0); - final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null : courseDTO.getFinishMarkPositions().get(1); + final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null + : courseDTO.getFinishMarkPositions().get(0); + final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null + : courseDTO.getFinishMarkPositions().get(1); if (courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { - finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( - numberFormatOneDecimal.format(courseDTO.finishLineLengthInMeters), - numberFormatOneDecimal.format(Math.abs(courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), - courseDTO.finishLineAdvantageousSide.name().charAt(0)+courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), - numberFormatOneDecimal.format(courseDTO.finishLineAdvantageInMeters))); + finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), + " " + stringMessages.lineAngleToWindAndAdvantage( + numberFormatOneDecimal.format(courseDTO.finishLineLengthInMeters), + numberFormatOneDecimal.format(Math.abs( + courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), + courseDTO.finishLineAdvantageousSide.name().charAt(0) + + courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), + numberFormatOneDecimal.format(courseDTO.finishLineAdvantageInMeters))); } else { finishLineAdvantageText.delete(0, finishLineAdvantageText.length()); } - finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, - finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR); + finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, finishLineLeftPosition, + finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR); // the control point pairs for which we already decided whether or not // to show a course middle line for; values tell whether to show the line and for which zero-based // start waypoint index to do so; when for an equal control point pair multiple decisions with different // outcome are made, a decision to show the line overrules the decision to not show it (OR-semantics) final Map, Pair> keysAlreadyHandled = new HashMap<>(); - for (int zeroBasedIndexOfStartWaypoint = 0; zeroBasedIndexOfStartWaypoint key = getCourseMiddleLinesKey(courseDTO, zeroBasedIndexOfStartWaypoint); - boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() || - settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) || - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE) - && courseDTO.currentLegNumber > 0 - && courseDTO.currentLegNumber-1 == zeroBasedIndexOfStartWaypoint); + boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() + || settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) + || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE) + && courseDTO.currentLegNumber > 0 + && courseDTO.currentLegNumber - 1 == zeroBasedIndexOfStartWaypoint); keysAlreadyHandled.put(key, new Pair<>(showCourseMiddleLine, zeroBasedIndexOfStartWaypoint)); } Set> keysToConsider = new HashSet<>(keysAlreadyHandled.keySet()); keysToConsider.addAll(courseMiddleLines.keySet()); for (final Set key : keysToConsider) { - final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) ? - keysAlreadyHandled.get(key).getB() : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter + final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) + ? keysAlreadyHandled.get(key).getB() + : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter final Pair showLineAndZeroBasedIndexOfStartWaypoint = keysAlreadyHandled.get(key); - final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null && showLineAndZeroBasedIndexOfStartWaypoint.getA(); - courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), zeroBasedIndexOfStartWaypoint, showCourseMiddleLine)); + final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null + && showLineAndZeroBasedIndexOfStartWaypoint.getA(); + courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), + zeroBasedIndexOfStartWaypoint, showCourseMiddleLine)); } } } private boolean startLineEqualsFinishLine(CoursePositionsDTO courseDTO) { final List waypoints; - return courseDTO != null && courseDTO.course != null && - (waypoints = courseDTO.course.waypoints) != null && - waypoints.get(0).controlPoint.equals(waypoints.get(waypoints.size()-1).controlPoint); + return courseDTO != null && courseDTO.course != null && (waypoints = courseDTO.course.waypoints) != null + && waypoints.get(0).controlPoint.equals(waypoints.get(waypoints.size() - 1).controlPoint); } /** - * Given a zero-based index into courseDTO's {@link RaceCourseDTO#waypoints waypoints list} that denotes the start - * waypoint of the leg in question, returns a key that can be used for the {@link #courseMiddleLines} map, consisting of a set - * that holds the two {@link ControlPointDTO}s representing the start and finish control point of that leg. + * Given a zero-based index into courseDTO's {@link RaceCourseDTO#waypoints waypoints list} that + * denotes the start waypoint of the leg in question, returns a key that can be used for the + * {@link #courseMiddleLines} map, consisting of a set that holds the two {@link ControlPointDTO}s representing the + * start and finish control point of that leg. */ private Set getCourseMiddleLinesKey(final CoursePositionsDTO courseDTO, final int zeroBasedIndexOfStartWaypoint) { ControlPointDTO startControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint).controlPoint; - ControlPointDTO endControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint+1).controlPoint; + ControlPointDTO endControlPoint = courseDTO.course.waypoints + .get(zeroBasedIndexOfStartWaypoint + 1).controlPoint; final Set key = new HashSet<>(); key.add(startControlPoint); key.add(endControlPoint); @@ -1761,26 +1866,28 @@ public class RaceMap extends AbstractCompositeComponent impleme * @return null if the line is not shown; the polyline object representing the line being displayed * otherwise */ - private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, Polyline lineToShowOrRemoveOrUpdate, - final int zeroBasedIndexOfStartWaypoint, final boolean showLine) { + private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, + Polyline lineToShowOrRemoveOrUpdate, final int zeroBasedIndexOfStartWaypoint, final boolean showLine) { final Position position1DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint); - final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint+1); + final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint + 1); final LineInfoProvider lineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { final StringBuilder sb = new StringBuilder(); sb.append(stringMessages.courseMiddleLine()); sb.append('\n'); - sb.append(numberFormatNoDecimal.format( - Math.abs(position1DTO.getDistance(position2DTO).getMeters()))+stringMessages.metersUnit()); + sb.append(numberFormatNoDecimal.format(Math.abs(position1DTO.getDistance(position2DTO).getMeters())) + + stringMessages.metersUnit()); if (lastCombinedWindTrackInfoDTO != null) { - final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO.getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint); - if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null && !windTrackAtLegMiddle.windFixes.isEmpty()) { + final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO + .getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint); + if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null + && !windTrackAtLegMiddle.windFixes.isEmpty()) { WindDTO windAtLegMiddle = windTrackAtLegMiddle.windFixes.get(0); final double legBearingDeg = position1DTO.getBearingGreatCircle(position2DTO).getDegrees(); - final String diff = numberFormatOneDecimal.format( - Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg-legBearingDeg), - Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg-legBearingDeg))); + final String diff = numberFormatOneDecimal + .format(Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg - legBearingDeg), + Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg - legBearingDeg))); sb.append(", "); sb.append(stringMessages.degreesToWind(diff)); } @@ -1788,13 +1895,14 @@ public class RaceMap extends AbstractCompositeComponent impleme return sb.toString(); } }; - return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, lineInfoProvider, "#2268a0"); + return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, + lineInfoProvider, "#2268a0"); } private interface LineInfoProvider { String getLineInfo(); } - + /** * @param showLine * tells whether or not to show the line; if the lineToShowOrRemoveOrUpdate references a @@ -1805,7 +1913,8 @@ public class RaceMap extends AbstractCompositeComponent impleme * otherwise */ private Polyline showOrRemoveOrUpdateLine(Polyline lineToShowOrRemoveOrUpdate, final boolean showLine, - final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, String lineColorRGB) { + final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, + String lineColorRGB) { if (position1DTO != null && position2DTO != null) { if (showLine) { LatLng courseMiddleLinePoint1 = coordinateSystem.toLatLng(position1DTO); @@ -1822,7 +1931,8 @@ public class RaceMap extends AbstractCompositeComponent impleme lineToShowOrRemoveOrUpdate = Polyline.newInstance(options); lineToShowOrRemoveOrUpdate.setPath(pointsAsArray); lineToShowOrRemoveOrUpdate.setMap(map); - Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, this); + Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, + this); lineToShowOrRemoveOrUpdate.addMouseOverHandler(new MouseOverMapHandler() { @Override public void onEvent(MouseOverMapEvent event) { @@ -1840,7 +1950,8 @@ public class RaceMap extends AbstractCompositeComponent impleme pointsAsArray.removeAt(1); pointsAsArray.removeAt(0); } - adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, lineInfoProvider); + adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, + lineInfoProvider); pointsAsArray.insertAt(0, courseMiddleLinePoint1); pointsAsArray.insertAt(1, courseMiddleLinePoint2); } else { @@ -1855,7 +1966,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } private void adjustInfoOverlayForRemovedLine(Polyline lineToShowOrRemoveOrUpdate) { - SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); + SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry + .remove(lineToShowOrRemoveOrUpdate); if (infoOverlay != null) { infoOverlay.removeFromMap(); } @@ -1866,9 +1978,10 @@ public class RaceMap extends AbstractCompositeComponent impleme SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.get(lineToShowOrRemoveOrUpdate); if (getSettings().getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) { if (infoOverlay == null) { - infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, lineInfoProvider.getLineInfo(), coordinateSystem); + infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, + lineInfoProvider.getLineInfo(), coordinateSystem); infoOverlaysForLinesForCourseGeometry.put(lineToShowOrRemoveOrUpdate, infoOverlay); - infoOverlay.addToMap(); + infoOverlay.addToMap(); } else { infoOverlay.setInfoText(lineInfoProvider.getLineInfo()); } @@ -1877,28 +1990,29 @@ public class RaceMap extends AbstractCompositeComponent impleme infoOverlay.draw(); } else { if (infoOverlay != null) { - infoOverlay.removeFromMap(); - infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); + infoOverlay.removeFromMap(); + infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); } } } - + /** - * If, according to {@link #lastRaceTimesInfo} and {@link #timer} the race is - * still in the pre-start phase, show a {@link SmallTransparentInfoOverlay} at the - * start line that shows the count down. + * If, according to {@link #lastRaceTimesInfo} and {@link #timer} the race is still in the pre-start phase, show a + * {@link SmallTransparentInfoOverlay} at the start line that shows the count down. */ private void updateCountdownCanvas(WaypointDTO startWaypoint) { - if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null || Util.isEmpty(startWaypoint.controlPoint.getMarks()) - || lastRaceTimesInfo == null || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { + if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null + || Util.isEmpty(startWaypoint.controlPoint.getMarks()) || lastRaceTimesInfo == null + || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { if (countDownOverlay != null) { countDownOverlay.removeFromMap(); countDownOverlay = null; } } else { long timeToStartInMs = lastRaceTimesInfo.startOfRace.getTime() - timer.getTime().getTime(); - String countDownText = timeToStartInMs > 1000 ? stringMessages.timeToStart(DateAndTimeFormatterUtil - .formatElapsedTime(timeToStartInMs)) : stringMessages.start(); + String countDownText = timeToStartInMs > 1000 + ? stringMessages.timeToStart(DateAndTimeFormatterUtil.formatElapsedTime(timeToStartInMs)) + : stringMessages.start(); if (countDownOverlay == null) { countDownOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, countDownText, coordinateSystem, getSettings().getStartCountDownFontSizeScaling()); @@ -1906,7 +2020,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } else { countDownOverlay.setInfoText(countDownText); } - countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, /* transition time */ -1); + countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, + /* transition time */ -1); countDownOverlay.draw(); } } @@ -1925,7 +2040,8 @@ public class RaceMap extends AbstractCompositeComponent impleme int MAX_ZOOM = 20; // maximum zoom-level that should be automatically selected double LOG2 = Math.log(2.0); double deltaLng = bounds.getNorthEast().getLongitude() - bounds.getSouthWest().getLongitude(); - double deltaLat = getMercatorLatitude(bounds.getNorthEast().getLatitude()) - getMercatorLatitude(bounds.getSouthWest().getLatitude()); + double deltaLat = getMercatorLatitude(bounds.getNorthEast().getLatitude()) + - getMercatorLatitude(bounds.getSouthWest().getLatitude()); if ((deltaLng == 0) && (deltaLat == 0)) { return MAX_ZOOM; } @@ -1933,27 +2049,28 @@ public class RaceMap extends AbstractCompositeComponent impleme deltaLng += 360; } int zoomLng = (int) Math.floor(Math.log(map.getDiv().getClientWidth() * 360 / deltaLng / GLOBE_PXSIZE) / LOG2); - int zoomLat = (int) Math.floor(Math.log(map.getDiv().getClientHeight() * 2 * Math.PI / deltaLat / GLOBE_PXSIZE) / LOG2); + int zoomLat = (int) Math + .floor(Math.log(map.getDiv().getClientHeight() * 2 * Math.PI / deltaLat / GLOBE_PXSIZE) / LOG2); return Math.min(Math.min(zoomLat, zoomLng), MAX_ZOOM); } - + private void zoomMapToNewBounds(LatLngBounds newBounds) { if (newBounds != null) { LatLngBounds currentMapBounds; - if (map.getBounds() == null - || !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds) + if (map.getBounds() == null || !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds) || graticuleAreaRatio(currentMapBounds, newBounds) > 10) { // only change bounds if the new bounds don't fit into the current map zoom - Iterable oldZoomTypesToConsiderSettings = settings.getZoomSettings().getTypesToConsiderOnZoom(); + Iterable oldZoomTypesToConsiderSettings = settings.getZoomSettings() + .getTypesToConsiderOnZoom(); setAutoZoomInProgress(true); autoZoomLatLngBounds = newBounds; - int newZoomLevel = getZoomLevel(autoZoomLatLngBounds); + int newZoomLevel = getZoomLevel(autoZoomLatLngBounds); if (newZoomLevel != map.getZoom()) { // distinguish between zoom-in and zoom-out, because the sequence of panTo() and setZoom() // appears different on the screen due to map-animations // following sequences keep the selected boats allways visible: - // zoom-in : 1. panTo(), 2. setZoom() - // zoom-out: 1. setZoom(), 2. panTo() + // zoom-in : 1. panTo(), 2. setZoom() + // zoom-out: 1. setZoom(), 2. panTo() autoZoomIn = newZoomLevel > map.getZoom(); autoZoomOut = !autoZoomIn; autoZoomLevel = newZoomLevel; @@ -1966,13 +2083,14 @@ public class RaceMap extends AbstractCompositeComponent impleme } else { map.panTo(autoZoomLatLngBounds.getCenter()); } - RaceMapZoomSettings restoredZoomSettings = new RaceMapZoomSettings(oldZoomTypesToConsiderSettings, settings.getZoomSettings().isZoomToSelectedCompetitors()); + RaceMapZoomSettings restoredZoomSettings = new RaceMapZoomSettings(oldZoomTypesToConsiderSettings, + settings.getZoomSettings().isZoomToSelectedCompetitors()); settings = new RaceMapSettings(settings, restoredZoomSettings); setAutoZoomInProgress(false); } } } - + private double graticuleAreaRatio(LatLngBounds containing, LatLngBounds contained) { assert BoundsUtil.contains(containing, contained); double containingAreaRatio = getGraticuleArea(containing) / getGraticuleArea(contained); @@ -1980,26 +2098,29 @@ public class RaceMap extends AbstractCompositeComponent impleme } /** - * A much simplified "area" calculation for a {@link Bounds} object, multiplying the differences in latitude and longitude degrees. - * The result therefore is in the order of magnitude of 60*60 square nautical miles. + * A much simplified "area" calculation for a {@link Bounds} object, multiplying the differences in latitude and + * longitude degrees. The result therefore is in the order of magnitude of 60*60 square nautical miles. */ private double getGraticuleArea(LatLngBounds bounds) { - return ((BoundsUtil.isCrossesDateLine(bounds) ? bounds.getNorthEast().getLongitude()+360 : bounds.getNorthEast().getLongitude())-bounds.getSouthWest().getLongitude()) * - (bounds.getNorthEast().getLatitude() - bounds.getSouthWest().getLatitude()); + return ((BoundsUtil.isCrossesDateLine(bounds) ? bounds.getNorthEast().getLongitude() + 360 + : bounds.getNorthEast().getLongitude()) - bounds.getSouthWest().getLongitude()) + * (bounds.getNorthEast().getLatitude() - bounds.getSouthWest().getLatitude()); } private void setAutoZoomInProgress(boolean autoZoomInProgress) { this.autoZoomInProgress = autoZoomInProgress; } - + boolean isAutoZoomInProgress() { return autoZoomInProgress; } - + /** - * @param timeForPositionTransitionMillis use -1 to not animate the position transition, e.g., during map zoom or non-play + * @param timeForPositionTransitionMillis + * use -1 to not animate the position transition, e.g., during map zoom or non-play */ - private boolean updateBoatCanvasForCompetitor(CompetitorDTO competitorDTO, Date date, long timeForPositionTransitionMillis) { + private boolean updateBoatCanvasForCompetitor(CompetitorDTO competitorDTO, Date date, + long timeForPositionTransitionMillis) { boolean hasTimeJumped = timeForPositionTransitionMillis > 3 * timer.getRefreshInterval(); if (hasTimeJumped) { timeForPositionTransitionMillis = -1; @@ -2009,7 +2130,8 @@ public class RaceMap extends AbstractCompositeComponent impleme if (lastBoatFix != null) { BoatOverlay boatOverlay = boatOverlays.get(competitorDTO); if (boatOverlay == null) { - boatOverlay = createBoatOverlay(RaceMapOverlaysZIndexes.BOATS_ZINDEX, competitorDTO, displayHighlighted(competitorDTO)); + boatOverlay = createBoatOverlay(RaceMapOverlaysZIndexes.BOATS_ZINDEX, competitorDTO, + displayHighlighted(competitorDTO)); boatOverlays.put(competitorDTO, boatOverlay); boatOverlay.setDisplayMode(displayHighlighted(competitorDTO)); boatOverlay.setBoatFix(lastBoatFix, timeForPositionTransitionMillis); @@ -2027,60 +2149,57 @@ public class RaceMap extends AbstractCompositeComponent impleme private DisplayMode displayHighlighted(CompetitorDTO competitorDTO) { boolean competitorisSelected = competitorSelection.isSelected(competitorDTO); - if(!settings.isShowOnlySelectedCompetitors()){ - if(competitorisSelected){ + if (!settings.isShowOnlySelectedCompetitors()) { + if (competitorisSelected) { return DisplayMode.SELECTED; - } - else{ - if(isSomeOtherCompetitorSelected()){ + } else { + if (isSomeOtherCompetitorSelected()) { return DisplayMode.NOT_SELECTED; - } - else{ + } else { return DisplayMode.DEFAULT; } } - } - else{ + } else { return competitorSelection.isSelected(competitorDTO) ? DisplayMode.SELECTED : DisplayMode.DEFAULT; } - + } - - private boolean isSomeOtherCompetitorSelected(){ + + private boolean isSomeOtherCompetitorSelected() { return Util.size(competitorSelection.getSelectedCompetitors()) > 0; } - + private class CourseMarkInfoWindowClickHandler implements ClickMapHandler { private final MarkDTO markDTO; private final CourseMarkOverlay courseMarkOverlay; - + public CourseMarkInfoWindowClickHandler(MarkDTO markDTO, CourseMarkOverlay courseMarkOverlay) { this.markDTO = markDTO; this.courseMarkOverlay = courseMarkOverlay; } - + @Override public void onEvent(ClickMapEvent event) { LatLng latlng = courseMarkOverlay.getMarkLatLngPosition(); showMarkInfoWindow(markDTO, latlng); } } - + private void registerCourseMarkInfoWindowClickHandler(final String markDTOIdAsString) { final CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTOIdAsString); - courseMarkClickHandlers.put(markDTOIdAsString, - courseMarkOverlay.addClickHandler(new CourseMarkInfoWindowClickHandler(markDTOs.get(markDTOIdAsString), courseMarkOverlay))); + courseMarkClickHandlers.put(markDTOIdAsString, courseMarkOverlay.addClickHandler( + new CourseMarkInfoWindowClickHandler(markDTOs.get(markDTOIdAsString), courseMarkOverlay))); } - + public void registerAllCourseMarkInfoWindowClickHandlers() { for (String markDTOIdAsString : markDTOs.keySet()) { registerCourseMarkInfoWindowClickHandler(markDTOIdAsString); } } - + public void unregisterAllCourseMarkInfoWindowClickHandlers() { Iterator> iterator = courseMarkClickHandlers.entrySet().iterator(); - while(iterator.hasNext()) { + while (iterator.hasNext()) { Entry handler = iterator.next(); handler.getValue().removeHandler(); iterator.remove(); @@ -2089,7 +2208,8 @@ public class RaceMap extends AbstractCompositeComponent impleme private BoatOverlay createBoatOverlay(int zIndex, final CompetitorDTO competitorDTO, DisplayMode displayMode) { final BoatDTO boatOfCompetitor = competitorSelection.getBoat(competitorDTO); - final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, boatOfCompetitor, competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); + final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, boatOfCompetitor, + competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); boatCanvas.setDisplayMode(displayMode); boatCanvas.addClickHandler(event -> { GPSFixDTOWithSpeedWindTackAndLegType latestFixForCompetitor = getBoatFix(competitorDTO, timer.getTime()); @@ -2114,8 +2234,10 @@ public class RaceMap extends AbstractCompositeComponent impleme return boatCanvas; } - protected WindSensorOverlay createWindSensorOverlay(int zIndex, final WindSource windSource, final WindTrackInfoDTO windTrackInfoDTO) { - final WindSensorOverlay windSensorOverlay = new WindSensorOverlay(map, zIndex, raceMapImageManager, stringMessages, coordinateSystem); + protected WindSensorOverlay createWindSensorOverlay(int zIndex, final WindSource windSource, + final WindTrackInfoDTO windTrackInfoDTO) { + final WindSensorOverlay windSensorOverlay = new WindSensorOverlay(map, zIndex, raceMapImageManager, + stringMessages, coordinateSystem); windSensorOverlay.setWindInfo(windTrackInfoDTO, windSource); windSensorOverlay.addClickHandler(new ClickMapHandler() { @Override @@ -2166,15 +2288,19 @@ public class RaceMap extends AbstractCompositeComponent impleme flowPanel.add(value); return flowPanel; } - + private Widget getInfoWindowContent(MarkDTO markDTO) { VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.mark(), markDTO.getName())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsSignedDecimalDegrees())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), + markDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); + vPanel.add( + createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsSignedDecimalDegrees())); return vPanel; } + private Map maneuverLossCheckBoxValueStore = new HashMap<>(); + private Widget getInfoWindowContent(ManeuverDTO maneuver) { SpeedWithBearingDTO before = maneuver.speedWithBearingBefore; SpeedWithBearingDTO after = maneuver.speedWithBearingAfter; @@ -2207,20 +2333,22 @@ public class RaceMap extends AbstractCompositeComponent impleme if (maneuver.maneuverLoss != null) { Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); - - CheckBox maneuverLossLinesCheckBox = new CheckBox("Show"); - maneuverLossLinesCheckBox.setValue(maneuverLossLinesCheckBoxValueStore); + CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); + if (maneuverLossCheckBoxValueStore.isEmpty()) { + maneuverLossCheckBoxValueStore.put(maneuver.position, false); + } + maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.get(maneuver.position)); maneuverLossLinesCheckBox.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { visualizeManeuver(maneuver, maneuverLossLinesCheckBox.getValue()); - maneuverLossLinesCheckBoxValueStore = maneuverLossLinesCheckBox.getValue(); + maneuverLossCheckBoxValueStore.put(maneuver.position, maneuverLossLinesCheckBox.getValue()); } - }); + }); HorizontalPanel hPanel = new HorizontalPanel(); hPanel.add(maneuverLossWidget); hPanel.add(maneuverLossLinesCheckBox); - vPanel.add(hPanel); + vPanel.add(hPanel); } if (maneuver.markPassingTimePoint != null) { vPanel.add( @@ -2228,8 +2356,8 @@ public class RaceMap extends AbstractCompositeComponent impleme DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.markPassingTimePoint) + (maneuver.markPassingSide == null ? "" : " (" + (maneuver.markPassingSide == NauticalSide.PORT - ? stringMessages.portSide() : stringMessages.starboardSide()) - + ")"))); + ? stringMessages.portSide() + : stringMessages.starboardSide()) + ")"))); } return vPanel; } @@ -2239,11 +2367,14 @@ public class RaceMap extends AbstractCompositeComponent impleme final VerticalPanel vPanel = new VerticalPanel(); final Anchor windSourceNameAnchor = new Anchor(WindSourceTypeFormatter.format(windSource, stringMessages)); vPanel.add(createInfoWindowLabelWithWidget(stringMessages.windSource(), windSourceNameAnchor)); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.wind(), Math.round(windDTO.dampenedTrueWindFromDeg) + " " + stringMessages.degreesShort())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.windSpeed(), numberFormatOneDecimal.format(windDTO.dampenedTrueWindSpeedInKnots))); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.wind(), + Math.round(windDTO.dampenedTrueWindFromDeg) + " " + stringMessages.degreesShort())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.windSpeed(), + numberFormatOneDecimal.format(windDTO.dampenedTrueWindSpeedInKnots))); final MillisecondsTimePoint timePoint = new MillisecondsTimePoint(windDTO.measureTimepoint); vPanel.add(createInfoWindowLabelAndValue(stringMessages.time(), timePoint.asDate().toString())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), windDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), + windDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); final Label positionInDecimalDegreesLabel = new Label(windDTO.position.getAsSignedDecimalDegrees()); positionInDecimalDegreesLabel.setWordWrap(false); positionInDecimalDegreesLabel.getElement().getStyle().setFloat(Style.Float.LEFT); @@ -2283,7 +2414,8 @@ public class RaceMap extends AbstractCompositeComponent impleme private Widget getInfoWindowContent(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType lastFix) { final VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.competitor(), competitorDTO.getName())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), competitorSelection.getBoat(competitorDTO).getSailId())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), + competitorSelection.getBoat(competitorDTO).getSailId())); final Integer rank = getRank(competitorDTO); if (rank != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.rank(), String.valueOf(rank))); @@ -2294,8 +2426,10 @@ public class RaceMap extends AbstractCompositeComponent impleme speedWithBearing = new SpeedWithBearingDTO(0, 0); } vPanel.add(createInfoWindowLabelAndValue(stringMessages.speed(), - NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " "+stringMessages.knotsUnit())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), (int) speedWithBearing.bearingInDegrees + " "+stringMessages.degreesShort())); + NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " " + + stringMessages.knotsUnit())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), + (int) speedWithBearing.bearingInDegrees + " " + stringMessages.degreesShort())); if (lastFix.degreesBoatToTheWind != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.degreesBoatToTheWind(), (int) Math.abs(lastFix.degreesBoatToTheWind) + " " + stringMessages.degreesShort())); @@ -2312,11 +2446,13 @@ public class RaceMap extends AbstractCompositeComponent impleme new AsyncCallback>>() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), true /*silentMode */); + errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), + true /* silentMode */); } @Override - public void onSuccess(Map> result) { + public void onSuccess( + Map> result) { lastDouglasPeuckerResult = result; if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); @@ -2328,10 +2464,12 @@ public class RaceMap extends AbstractCompositeComponent impleme } } }); - sailingService.getManeuvers(race, timeRange, new AsyncCallback>>() { + sailingService.getManeuvers(race, timeRange, + new AsyncCallback>>() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), true /*silentMode */); + errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), + true /* silentMode */); } @Override @@ -2354,31 +2492,34 @@ public class RaceMap extends AbstractCompositeComponent impleme /** * @return the {@link CompetitorSelectionProvider#getSelectedCompetitors()} if * {@link RaceMapSettings#isShowOnlySelectedCompetitors() only selected competitors are to be shown}, the - * {@link CompetitorSelectionProvider#getFilteredCompetitors() filtered competitors} otherwise. In both cases, - * if we have {@link RaceCompetitorSet information about the competitors participating} in this map's race, - * the result set is reduced to those, no matter if other regatta participants would otherwise have been in - * the result set + * {@link CompetitorSelectionProvider#getFilteredCompetitors() filtered competitors} otherwise. In both + * cases, if we have {@link RaceCompetitorSet information about the competitors participating} in this map's + * race, the result set is reduced to those, no matter if other regatta participants would otherwise have + * been in the result set */ private Iterable getCompetitorsToShow() { final Set result = new HashSet<>(); Iterable selection = competitorSelection.getSelectedCompetitors(); - final Set raceCompetitorIdsAsString = raceCompetitorSet.getIdsOfCompetitorsParticipatingInRaceAsStrings(); + final Set raceCompetitorIdsAsString = raceCompetitorSet + .getIdsOfCompetitorsParticipatingInRaceAsStrings(); if (!settings.isShowOnlySelectedCompetitors() || Util.isEmpty(selection)) { for (final CompetitorDTO filteredCompetitor : competitorSelection.getFilteredCompetitors()) { - if (raceCompetitorIdsAsString == null || raceCompetitorIdsAsString.contains(filteredCompetitor.getIdAsString())) { + if (raceCompetitorIdsAsString == null + || raceCompetitorIdsAsString.contains(filteredCompetitor.getIdAsString())) { result.add(filteredCompetitor); } } } else { for (final CompetitorDTO selectedCompetitor : selection) { - if (raceCompetitorIdsAsString == null || raceCompetitorIdsAsString.contains(selectedCompetitor.getIdAsString())) { + if (raceCompetitorIdsAsString == null + || raceCompetitorIdsAsString.contains(selectedCompetitor.getIdAsString())) { result.add(selectedCompetitor); } } } return result; } - + protected void removeAllMarkDouglasPeuckerpoints() { if (douglasMarkers != null) { for (Marker marker : douglasMarkers) { @@ -2395,17 +2536,10 @@ public class RaceMap extends AbstractCompositeComponent impleme } maneuverMarkers = null; } - if (maneuverLossLines != null) { - for (Polyline maneuverLossLine : maneuverLossLines) { - maneuverLossLine.setMap((MapWidget) null); - } - maneuverLossLines = null; - maneuverLossLinesCheckBoxValueStore = false; - maneuverLossInfoOverlay.removeFromMap(); - } } - private void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { + private void showMarkDouglasPeuckerPoints( + Map> gpsFixPointMapForCompetitors) { douglasMarkers = new HashSet(); if (map != null && gpsFixPointMapForCompetitors != null) { Set keySet = gpsFixPointMapForCompetitors.keySet(); @@ -2416,7 +2550,7 @@ public class RaceMap extends AbstractCompositeComponent impleme for (GPSFixDTOWithSpeedWindTackAndLegType fix : gpsFix) { LatLng latLng = coordinateSystem.toLatLng(fix.position); MarkerOptions options = MarkerOptions.newInstance(); - options.setTitle(fix.timepoint+": "+fix.position+", "+fix.speedWithBearing.toString()); + options.setTitle(fix.timepoint + ": " + fix.position + ", " + fix.speedWithBearing.toString()); Marker marker = Marker.newInstance(options); marker.setPosition(latLng); douglasMarkers.add(marker); @@ -2460,29 +2594,29 @@ public class RaceMap extends AbstractCompositeComponent impleme maneuverMarkers.add(marker); marker.setMap(map); } - + final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return "Middle Maneuver Angle"; + return ""; } }; final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return "Extrapolated Maneuver Start Position"; + return ""; } }; final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return "Projected extrapolated Maneuver Start Position"; + return ""; } }; final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return "Projected Maneuver End Position"; + return ""; } }; final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { @@ -2492,22 +2626,48 @@ public class RaceMap extends AbstractCompositeComponent impleme } }; - private Set maneuverLossLines; - private boolean maneuverLossLinesCheckBoxValueStore = false; - private SmallTransparentInfoOverlay maneuverLossInfoOverlay; private void visualizeManeuver(ManeuverDTO maneuver, boolean showManeuverVisualization) { if (showManeuverVisualization == false) { - for (Polyline maneuverLossLine : maneuverLossLines) { - maneuverLossLine.setMap((MapWidget) null); + for (Polyline p : maneuverLossLinesMap.get(maneuver.position)) { + p.setMap((MapWidget) null); + } + maneuverLossInfoOverlayMap.get(maneuver.position).removeFromMap(); } - maneuverLossLines = null; - maneuverLossLinesCheckBoxValueStore = false; - maneuverLossInfoOverlay.removeFromMap(); - } else { + else { createManeuverLossLines(maneuver); } + } + + private SmallTransparentInfoOverlay maneuverLossInfoOverlay; + private Set maneuverLossLines; + private Map> maneuverLossLinesMap = new HashMap<>(); + private Map maneuverLossInfoOverlayMap = new HashMap<>(); + + private void removeManeuverLossLines() { + if (!maneuverLossLinesMap.isEmpty()) { + for (Position pos : maneuverLossLinesMap.keySet()) { + for (Polyline maneuverLossLine : maneuverLossLinesMap.get(pos)) { + maneuverLossLine.setMap((MapWidget) null); + ; + } + } + maneuverLossLinesMap.clear(); + } + if (!maneuverLossInfoOverlayMap.isEmpty()) { + for (Map.Entry e : maneuverLossInfoOverlayMap.entrySet()) { + e.getValue().removeFromMap(); + } + maneuverLossInfoOverlayMap.clear(); + } + if (!maneuverLossCheckBoxValueStore.isEmpty()) { + for (Map.Entry e : maneuverLossCheckBoxValueStore.entrySet()) { + e.setValue(false); + } + maneuverLossCheckBoxValueStore.clear(); + } } -/** Visualizes the ManeuverLoss as calculated in {@link ManeuverLoss} + +/** Visualizes the ManeuverLoss as calculated in {@link ManeuverDetectorImpl#getManeuverLoss()} * */ private void createManeuverLossLines(ManeuverDTO maneuver) { @@ -2537,24 +2697,32 @@ public class RaceMap extends AbstractCompositeComponent impleme projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00")); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, maneuverLossLineInfoProvider, color)); - StringBuilder sb = new StringBuilder(); - sb.append(maneuverLossLineInfoProvider.getLineInfo() + ": "); - sb.append(numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); - sb.append('\n'); - sb.append("Maneuver Angle" + ": "); - sb.append(numberFormatOneDecimal.format(maneuver.directionChangeInDegrees) + " " + stringMessages.degreesUnit()); - maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, - RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); - maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, - new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); - maneuverLossInfoOverlay.draw(); for (Polyline maneuverLossLine : maneuverLossLines) { PolylineOptions options = PolylineOptions.newInstance(); options.setStrokeOpacity(0.5); maneuverLossLine.setOptions(options); } + maneuverLossLinesMap.put(maneuver.position, maneuverLossLines); + StringBuilder sb = new StringBuilder(); + sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + + " " + stringMessages.metersUnit() + '\n'); + if (maneuver.type == ManeuverType.TACK) { + sb.append(stringMessages.tackAngle() + ": "); + } else if (maneuver.type == ManeuverType.JIBE) { + sb.append(stringMessages.jibeAngle() + ": "); + } else { + sb.append(stringMessages.maneuverAngle() + ": "); + } + sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.directionChangeInDegrees)) + " " + + stringMessages.degreesUnit()); + maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, + RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); + maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, + new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); + maneuverLossInfoOverlay.draw(); + maneuverLossInfoOverlayMap.put(maneuver.position, maneuverLossInfoOverlay); } - + /** * @param date * the point in time for which to determine the competitor's boat position; approximated by using the fix @@ -2567,37 +2735,45 @@ public class RaceMap extends AbstractCompositeComponent impleme GPSFixDTOWithSpeedWindTackAndLegType result = null; List competitorFixes = fixesAndTails.getFixes(competitorDTO); if (competitorFixes != null && !competitorFixes.isEmpty()) { - int i = Collections.binarySearch(competitorFixes, new GPSFixDTOWithSpeedWindTackAndLegType(date, null, null, (WindDTO) null, null, null, false), + int i = Collections.binarySearch(competitorFixes, + new GPSFixDTOWithSpeedWindTackAndLegType(date, null, null, (WindDTO) null, null, null, false), new Comparator() { - @Override - public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, GPSFixDTOWithSpeedWindTackAndLegType o2) { - return o1.timepoint.compareTo(o2.timepoint); - } - }); - if (i<0) { - i = -i-1; // no perfect match; i is now the insertion point + @Override + public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, + GPSFixDTOWithSpeedWindTackAndLegType o2) { + return o1.timepoint.compareTo(o2.timepoint); + } + }); + if (i < 0) { + i = -i - 1; // no perfect match; i is now the insertion point // if the insertion point is at the end, use last fix if (i >= competitorFixes.size()) { - result = competitorFixes.get(competitorFixes.size()-1); + result = competitorFixes.get(competitorFixes.size() - 1); } else if (i == 0) { // if the insertion point is at the beginning, use first fix result = competitorFixes.get(0); } else { - // competitorFixes must have at least two elements, and i points neither to the end nor the beginning; + // competitorFixes must have at least two elements, and i points neither to the end nor the + // beginning; // get the fix from i and i+1 whose timepoint is closer to date - final GPSFixDTOWithSpeedWindTackAndLegType fixBefore = competitorFixes.get(i-1); + final GPSFixDTOWithSpeedWindTackAndLegType fixBefore = competitorFixes.get(i - 1); final GPSFixDTOWithSpeedWindTackAndLegType fixAfter = competitorFixes.get(i); final GPSFixDTOWithSpeedWindTackAndLegType closer; - if (date.getTime() - fixBefore.timepoint.getTime() < fixAfter.timepoint.getTime() - date.getTime()) { + if (date.getTime() - fixBefore.timepoint.getTime() < fixAfter.timepoint.getTime() + - date.getTime()) { closer = fixBefore; } else { closer = fixAfter; } // now compute a weighted average depending on the time difference to "date" (see also bug 1924) - double factorForAfter = (double) (date.getTime()-fixBefore.timepoint.getTime()) / (double) (fixAfter.timepoint.getTime() - fixBefore.timepoint.getTime()); - double factorForBefore = 1-factorForAfter; - DegreePosition betweenPosition = new DegreePosition(factorForBefore*fixBefore.position.getLatDeg() + factorForAfter*fixAfter.position.getLatDeg(), - factorForBefore*fixBefore.position.getLngDeg() + factorForAfter*fixAfter.position.getLngDeg()); + double factorForAfter = (double) (date.getTime() - fixBefore.timepoint.getTime()) + / (double) (fixAfter.timepoint.getTime() - fixBefore.timepoint.getTime()); + double factorForBefore = 1 - factorForAfter; + DegreePosition betweenPosition = new DegreePosition( + factorForBefore * fixBefore.position.getLatDeg() + + factorForAfter * fixAfter.position.getLatDeg(), + factorForBefore * fixBefore.position.getLngDeg() + + factorForAfter * fixAfter.position.getLngDeg()); final double betweenBearing; if (fixBefore.speedWithBearing == null) { if (fixAfter.speedWithBearing == null) { @@ -2608,16 +2784,22 @@ public class RaceMap extends AbstractCompositeComponent impleme } else if (fixAfter.speedWithBearing == null) { betweenBearing = fixBefore.speedWithBearing.bearingInDegrees; } else { - betweenBearing = new ScalableBearing(new DegreeBearingImpl(fixBefore.speedWithBearing.bearingInDegrees)). - multiply(factorForBefore).add(new ScalableBearing(new DegreeBearingImpl(fixAfter.speedWithBearing.bearingInDegrees)). - multiply(factorForAfter)).divide(1).getDegrees(); + betweenBearing = new ScalableBearing( + new DegreeBearingImpl(fixBefore.speedWithBearing.bearingInDegrees)) + .multiply(factorForBefore) + .add(new ScalableBearing( + new DegreeBearingImpl(fixAfter.speedWithBearing.bearingInDegrees)) + .multiply(factorForAfter)) + .divide(1).getDegrees(); } - SpeedWithBearingDTO betweenSpeed = new SpeedWithBearingDTO( - factorForBefore*(fixBefore.speedWithBearing==null?0:fixBefore.speedWithBearing.speedInKnots) + - factorForAfter*(fixAfter.speedWithBearing==null?0:fixAfter.speedWithBearing.speedInKnots), + SpeedWithBearingDTO betweenSpeed = new SpeedWithBearingDTO(factorForBefore + * (fixBefore.speedWithBearing == null ? 0 : fixBefore.speedWithBearing.speedInKnots) + + factorForAfter + * (fixAfter.speedWithBearing == null ? 0 : fixAfter.speedWithBearing.speedInKnots), betweenBearing); - result = new GPSFixDTOWithSpeedWindTackAndLegType(date, betweenPosition, betweenSpeed, closer.degreesBoatToTheWind, - closer.tack, closer.legType, fixBefore.extrapolated || fixAfter.extrapolated); + result = new GPSFixDTOWithSpeedWindTackAndLegType(date, betweenPosition, betweenSpeed, + closer.degreesBoatToTheWind, closer.tack, closer.legType, + fixBefore.extrapolated || fixAfter.extrapolated); } } else { // perfect match @@ -2670,7 +2852,8 @@ public class RaceMap extends AbstractCompositeComponent impleme for (CompetitorDTO oneOfAllCompetitors : competitorSelection.getAllCompetitors()) { Polyline tail = fixesAndTails.getTail(oneOfAllCompetitors); if (tail != null) { - PolylineOptions newOptions = createTailStyle(oneOfAllCompetitors, displayHighlighted(oneOfAllCompetitors)); + PolylineOptions newOptions = createTailStyle(oneOfAllCompetitors, + displayHighlighted(oneOfAllCompetitors)); tail.setOptions(newOptions); } } @@ -2680,7 +2863,7 @@ public class RaceMap extends AbstractCompositeComponent impleme zoomMapToNewBounds(zoomSettings.getNewBounds(this)); } } - + @Override public void removedFromSelection(CompetitorDTO competitor) { if (isShowAnyHelperLines()) { @@ -2712,7 +2895,7 @@ public class RaceMap extends AbstractCompositeComponent impleme showCompetitorInfoOnMap(timer.getTime(), -1, competitorSelection.getSelectedFilteredCompetitors()); } } - //Trigger auto-zoom if needed + // Trigger auto-zoom if needed RaceMapZoomSettings zoomSettings = settings.getZoomSettings(); if (!zoomSettings.containsZoomType(ZoomTypes.NONE) && zoomSettings.isZoomToSelectedCompetitors()) { zoomMapToNewBounds(zoomSettings.getNewBounds(this)); @@ -2748,7 +2931,7 @@ public class RaceMap extends AbstractCompositeComponent impleme boolean maneuverTypeSelectionChanged = false; boolean requiresRedraw = false; boolean requiresUpdateCoordinateSystem = false; - + for (ManeuverType maneuverType : ManeuverType.values()) { if (newSettings.isShowManeuverType(maneuverType) != settings.isShowManeuverType(maneuverType)) { maneuverTypeSelectionChanged = true; @@ -2758,10 +2941,12 @@ public class RaceMap extends AbstractCompositeComponent impleme if (!(timer.getPlayState() == PlayStates.Playing) && lastManeuverResult != null) { removeAllManeuverMarkers(); showManeuvers(lastManeuverResult); + removeManeuverLossLines(); } } if (newSettings.isShowDouglasPeuckerPoints() != settings.isShowDouglasPeuckerPoints()) { - if (!(timer.getPlayState() == PlayStates.Playing) && lastDouglasPeuckerResult != null && newSettings.isShowDouglasPeuckerPoints()) { + if (!(timer.getPlayState() == PlayStates.Playing) && lastDouglasPeuckerResult != null + && newSettings.isShowDouglasPeuckerPoints()) { removeAllMarkDouglasPeuckerpoints(); showMarkDouglasPeuckerPoints(lastDouglasPeuckerResult); } else if (!newSettings.isShowDouglasPeuckerPoints()) { @@ -2789,7 +2974,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (!newSettings.getHelpLinesSettings().equals(settings.getHelpLinesSettings())) { requiresRedraw = true; } - if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null){ + if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null) { estimatedDurationOverlay.removeFromParent(); } if (newSettings.isShowWindStreamletOverlay() != settings.isShowWindStreamletOverlay()) { @@ -2805,7 +2990,7 @@ public class RaceMap extends AbstractCompositeComponent impleme requiresUpdateCoordinateSystem = true; requiresRedraw = true; } - if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null){ + if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null) { estimatedDurationOverlay.removeFromParent(); } if (newSettings.getStartCountDownFontSizeScaling() != settings.getStartCountDownFontSizeScaling()) { @@ -2816,7 +3001,7 @@ public class RaceMap extends AbstractCompositeComponent impleme requiresRedraw = true; } this.settings = newSettings; - + if (requiresUpdateCoordinateSystem) { updateCoordinateSystemFromSettings(); } @@ -2828,7 +3013,7 @@ public class RaceMap extends AbstractCompositeComponent impleme private void showSimulationOverlay(boolean visible) { simulationOverlay.setVisible(visible); if (visible) { - simulationOverlay.updateLeg(Math.max(lastLegNumber,1), true, -1 /* ensure ui-update */); + simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), true, -1 /* ensure ui-update */); } } @@ -2841,11 +3026,13 @@ public class RaceMap extends AbstractCompositeComponent impleme if (selectedCompetitors == null || !selectedCompetitors.iterator().hasNext()) { competitors = forMap.getCompetitorsToShow(); } else { - competitors = isZoomOnlyToSelectedCompetitors(forMap) ? selectedCompetitors : forMap.getCompetitorsToShow(); + competitors = isZoomOnlyToSelectedCompetitors(forMap) ? selectedCompetitors + : forMap.getCompetitorsToShow(); } for (CompetitorDTO competitor : competitors) { try { - GPSFixDTOWithSpeedWindTackAndLegType competitorFix = forMap.getBoatFix(competitor, forMap.timer.getTime()); + GPSFixDTOWithSpeedWindTackAndLegType competitorFix = forMap.getBoatFix(competitor, + forMap.timer.getTime()); Position competitorPosition = competitorFix != null ? competitorFix.position : null; if (competitorPosition != null) { if (newBounds == null) { @@ -2856,25 +3043,30 @@ public class RaceMap extends AbstractCompositeComponent impleme } } catch (IndexOutOfBoundsException e) { // TODO can't this be predicted and the exception be avoided in the first place? - // Catch this in case the competitor has no GPS fixes at the current time (e.g. in race 'Finale 2' of STG) + // Catch this in case the competitor has no GPS fixes at the current time (e.g. in race 'Finale 2' + // of STG) } } return newBounds; } - + } - + public static class TailsBoundsCalculator extends LatLngBoundsCalculatorForSelected { @Override public LatLngBounds calculateNewBounds(RaceMap racemap) { LatLngBounds newBounds = null; - Iterable competitors = isZoomOnlyToSelectedCompetitors(racemap) ? racemap.competitorSelection.getSelectedCompetitors() : racemap.getCompetitorsToShow(); + Iterable competitors = isZoomOnlyToSelectedCompetitors(racemap) + ? racemap.competitorSelection.getSelectedCompetitors() + : racemap.getCompetitorsToShow(); for (CompetitorDTO competitor : competitors) { Polyline tail = racemap.fixesAndTails.getTail(competitor); LatLngBounds bounds = null; // TODO: Find a replacement for missing Polyline function getBounds() from v2 - // see also http://stackoverflow.com/questions/3284808/getting-the-bounds-of-a-polyine-in-google-maps-api-v3; - // optionally, consider providing a bounds cache with two sorted sets that organize the LatLng objects for O(1) bounds calculation and logarithmic add, ideally O(1) remove + // see also + // http://stackoverflow.com/questions/3284808/getting-the-bounds-of-a-polyine-in-google-maps-api-v3; + // optionally, consider providing a bounds cache with two sorted sets that organize the LatLng objects + // for O(1) bounds calculation and logarithmic add, ideally O(1) remove if (tail != null && tail.getPath().getLength() >= 1) { bounds = BoundsUtil.getAsBounds(tail.getPath().get(0)); for (int i = 1; i < tail.getPath().getLength(); i++) { @@ -2892,7 +3084,7 @@ public class RaceMap extends AbstractCompositeComponent impleme return newBounds; } } - + public static class CourseMarksBoundsCalculator implements LatLngBoundsCalculator { @Override public LatLngBounds calculateNewBounds(RaceMap forMap) { @@ -2949,17 +3141,17 @@ public class RaceMap extends AbstractCompositeComponent impleme map.triggerResize(); zoomMapToNewBounds(settings.getZoomSettings().getNewBounds(RaceMap.this)); } - // Adjust RaceMap headers to avoid overlapping based on the RaceMap width + // Adjust RaceMap headers to avoid overlapping based on the RaceMap width boolean isCompactHeader = this.getOffsetWidth() <= 600; getLeftHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); getRightHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); - + // Adjust combined wind and true north indicator panel indent, based on the RaceMap height if (topLeftControlsWrapperPanel.getParent() != null) { this.adjustLeftControlsIndent(); } } - + private void adjustLeftControlsIndent() { topLeftControlsWrapperPanel.getParent().setStyleName("TopLeftControlsWrapperPanelParentDiv"); String leftControlsIndentStyle = getLeftControlsIndentStyle(); @@ -2972,16 +3164,17 @@ public class RaceMap extends AbstractCompositeComponent impleme public void competitorsListChanged(Iterable competitors) { timeChanged(timer.getTime(), null); } - + @Override public void filteredCompetitorsListChanged(Iterable filteredCompetitors) { timeChanged(timer.getTime(), null); } - + @Override public void filterChanged(FilterSet> oldFilterSet, FilterSet> newFilterSet) { - // nothing to do; if the list of filtered competitors has changed, a separate call to filteredCompetitorsListChanged will occur + // nothing to do; if the list of filtered competitors has changed, a separate call to + // filteredCompetitorsListChanged will occur } @Override @@ -2991,7 +3184,7 @@ public class RaceMap extends AbstractCompositeComponent impleme options.setGeodesic(true); options.setStrokeOpacity(1.0); - switch(displayMode){ + switch (displayMode) { case DEFAULT: options.setStrokeColor(competitorSelection.getColor(competitor, raceIdentifier).getAsHtml()); options.setStrokeWeight(1); @@ -3009,7 +3202,7 @@ public class RaceMap extends AbstractCompositeComponent impleme options.setZindex(RaceMapOverlaysZIndexes.BOATTAILS_ZINDEX); return options; } - + @Override public Polyline createTail(final CompetitorDTO competitor, List points) { final BoatDTO boat = competitorSelection.getBoat(competitor); @@ -3053,7 +3246,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } return result; } - + private Image createSAPLogo() { ImageResource sapLogoResource = resources.sapLogoOverlay(); Image sapLogo = new Image(sapLogoResource); @@ -3081,42 +3274,42 @@ public class RaceMap extends AbstractCompositeComponent impleme private MapOptions getMapOptions(final boolean showMapControls, boolean windUp) { MapOptions mapOptions = MapOptions.newInstance(); - mapOptions.setScrollWheel(true); - mapOptions.setMapTypeControl(showMapControls && !windUp); - mapOptions.setPanControl(showMapControls); - mapOptions.setZoomControl(showMapControls); - mapOptions.setScaleControl(true); - if (windUp) { - mapOptions.setMinZoom(8); - } else { - mapOptions.setMinZoom(0); - } - MapTypeStyle[] mapTypeStyles = new MapTypeStyle[4]; - - // hide all transit lines including ferry lines - mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT); - // hide points of interest - mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI); - // simplify road display - mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD); - // set water color - mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, WATER_COLOR); - - MapTypeControlOptions mapTypeControlOptions = MapTypeControlOptions.newInstance(); - mapTypeControlOptions.setPosition(ControlPosition.BOTTOM_RIGHT); - mapOptions.setMapTypeControlOptions(mapTypeControlOptions); + mapOptions.setScrollWheel(true); + mapOptions.setMapTypeControl(showMapControls && !windUp); + mapOptions.setPanControl(showMapControls); + mapOptions.setZoomControl(showMapControls); + mapOptions.setScaleControl(true); + if (windUp) { + mapOptions.setMinZoom(8); + } else { + mapOptions.setMinZoom(0); + } + MapTypeStyle[] mapTypeStyles = new MapTypeStyle[4]; - mapOptions.setMapTypeStyles(mapTypeStyles); - // no need to try to position the scale control; it always ends up at the right bottom corner - mapOptions.setStreetViewControl(false); - if (showMapControls) { - ZoomControlOptions zoomControlOptions = ZoomControlOptions.newInstance(); - zoomControlOptions.setPosition(ControlPosition.RIGHT_TOP); - mapOptions.setZoomControlOptions(zoomControlOptions); - PanControlOptions panControlOptions = PanControlOptions.newInstance(); - panControlOptions.setPosition(ControlPosition.RIGHT_TOP); - mapOptions.setPanControlOptions(panControlOptions); - } + // hide all transit lines including ferry lines + mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT); + // hide points of interest + mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI); + // simplify road display + mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD); + // set water color + mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, WATER_COLOR); + + MapTypeControlOptions mapTypeControlOptions = MapTypeControlOptions.newInstance(); + mapTypeControlOptions.setPosition(ControlPosition.BOTTOM_RIGHT); + mapOptions.setMapTypeControlOptions(mapTypeControlOptions); + + mapOptions.setMapTypeStyles(mapTypeStyles); + // no need to try to position the scale control; it always ends up at the right bottom corner + mapOptions.setStreetViewControl(false); + if (showMapControls) { + ZoomControlOptions zoomControlOptions = ZoomControlOptions.newInstance(); + zoomControlOptions.setPosition(ControlPosition.RIGHT_TOP); + mapOptions.setZoomControlOptions(zoomControlOptions); + PanControlOptions panControlOptions = PanControlOptions.newInstance(); + panControlOptions.setPosition(ControlPosition.RIGHT_TOP); + mapOptions.setPanControlOptions(panControlOptions); + } return mapOptions; } @@ -3130,11 +3323,11 @@ public class RaceMap extends AbstractCompositeComponent impleme public Map getCourseMarkOverlays() { return courseMarkOverlays; } - + public CoordinateSystem getCoordinateSystem() { return coordinateSystem; } - + public void hideAllHelplines() { if (startLine != null) { startLine.setVisible(false); @@ -3160,7 +3353,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + public void showAllHelplinesToShow() { if (settings.getHelpLinesSettings().isShowAnyHelperLines()) { if (startLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE)) @@ -3169,8 +3362,8 @@ public class RaceMap extends AbstractCompositeComponent impleme finishLine.setVisible(true); if (advantageLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) advantageLine.setVisible(true); - if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null && - settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) { + if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null + && settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) { windwardStartLineMarkToFirstMarkLine.setVisible(true); leewardStartLineMarkToFirstMarkLine.setVisible(true); } @@ -3186,7 +3379,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + public void addCompetitorsForRaceDefinedListener(CompetitorsForRaceDefinedListener listener) { raceCompetitorSet.addCompetitorsForRaceDefinedListener(listener); } @@ -3199,7 +3392,7 @@ public class RaceMap extends AbstractCompositeComponent impleme public String getId() { return raceMapLifecycle.getComponentId(); } - + public RaceMapLifecycle getLifecycle() { return raceMapLifecycle; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java index e0bf8a19772..f717e6272be 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java @@ -12,7 +12,6 @@ public class ManeuverLossDTO implements IsSerializable { public Duration maneuverDuration; public ManeuverLossDTO() { - } public ManeuverLossDTO(Position maneuverStartPosition, Position maneuverEndPosition, @@ -23,7 +22,4 @@ public class ManeuverLossDTO implements IsSerializable { this.middleManeuverAngle = middleManeuverAngle; this.maneuverDuration = maneuverDuration; } - - - } From e328fb1a7c84d56374c44d3ca5456d1271ac515c Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Mon, 28 May 2018 11:17:44 +0200 Subject: [PATCH 11/53] A ManeuverLoss Visualization is now identified by a triplet of competitor, maneuver.timepoint and maneuver.type. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 80 +++++++++++-------- .../gwt/ui/shared/ManeuverLossDTO.java | 2 +- 2 files changed, 46 insertions(+), 36 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index bebd66578ca..f61f8749436 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2298,10 +2298,8 @@ public class RaceMap extends AbstractCompositeComponent createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsSignedDecimalDegrees())); return vPanel; } - - private Map maneuverLossCheckBoxValueStore = new HashMap<>(); - private Widget getInfoWindowContent(ManeuverDTO maneuver) { + private Widget getInfoWindowContent(ManeuverDTO maneuver, CompetitorDTO competitor) { SpeedWithBearingDTO before = maneuver.speedWithBearingBefore; SpeedWithBearingDTO after = maneuver.speedWithBearingAfter; VerticalPanel vPanel = new VerticalPanel(); @@ -2334,15 +2332,17 @@ public class RaceMap extends AbstractCompositeComponent Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); if (maneuverLossCheckBoxValueStore.isEmpty()) { - maneuverLossCheckBoxValueStore.put(maneuver.position, false); + maneuverLossCheckBoxValueStore.put(t, false); } - maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.get(maneuver.position)); + maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.get(t)); maneuverLossLinesCheckBox.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - visualizeManeuver(maneuver, maneuverLossLinesCheckBox.getValue()); - maneuverLossCheckBoxValueStore.put(maneuver.position, maneuverLossLinesCheckBox.getValue()); + visualizeManeuver(maneuver, maneuverLossLinesCheckBox.getValue(), competitor); + maneuverLossCheckBoxValueStore.put(t, maneuverLossLinesCheckBox.getValue()); } }); HorizontalPanel hPanel = new HorizontalPanel(); @@ -2570,14 +2570,14 @@ public class RaceMap extends AbstractCompositeComponent List maneuversForCompetitor = maneuvers.get(competitorDTO); for (ManeuverDTO maneuver : maneuversForCompetitor) { if (settings.isShowManeuverType(maneuver.type)) { - createAndAddMarkerOfManeuver(maneuver); + createAndAddMarkerOfManeuver(maneuver, competitorDTO); } } } } } - private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver) { + private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { LatLng latLng = coordinateSystem.toLatLng(maneuver.position); Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor .get(new Pair(maneuver.type, ManeuverColor.getManeuverColor(maneuver))); @@ -2588,7 +2588,7 @@ public class RaceMap extends AbstractCompositeComponent marker.setTitle(ManeuverTypeFormatter.format(maneuver.type, stringMessages)); marker.addClickHandler(event -> { LatLng where = getCoordinateSystem().toLatLng(maneuver.position); - Widget content = getInfoWindowContent(maneuver); + Widget content = getInfoWindowContent(maneuver, competitor); managedInfoWindow.openAtPosition(content, where); }); maneuverMarkers.add(marker); @@ -2626,51 +2626,59 @@ public class RaceMap extends AbstractCompositeComponent } }; - private void visualizeManeuver(ManeuverDTO maneuver, boolean showManeuverVisualization) { + private void visualizeManeuver(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { if (showManeuverVisualization == false) { - for (Polyline p : maneuverLossLinesMap.get(maneuver.position)) { - p.setMap((MapWidget) null); - } - maneuverLossInfoOverlayMap.get(maneuver.position).removeFromMap(); + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); + for (Polyline p : maneuverLossLinesMap.get(t)) { + p.setMap((MapWidget) null); } - else { - createManeuverLossLines(maneuver); + maneuverLossInfoOverlayMap.get(t).removeFromMap(); + } else { + createManeuverLossLines(maneuver, competitor); } - } - + } + + private Map, Boolean> maneuverLossCheckBoxValueStore = new HashMap<>(); private SmallTransparentInfoOverlay maneuverLossInfoOverlay; private Set maneuverLossLines; - private Map> maneuverLossLinesMap = new HashMap<>(); - private Map maneuverLossInfoOverlayMap = new HashMap<>(); - + private Map, Set> maneuverLossLinesMap = new HashMap<>(); + private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); + + /** + * Removes the Polylines visualizing the ManeuverLoss and the SmallTransparentInfoOverlays attached to the + * Polylines. Also clears the Checkbox that defines whether a maneuverLoss is visualized or not. + */ private void removeManeuverLossLines() { if (!maneuverLossLinesMap.isEmpty()) { - for (Position pos : maneuverLossLinesMap.keySet()) { - for (Polyline maneuverLossLine : maneuverLossLinesMap.get(pos)) { + for (Triple t : maneuverLossLinesMap.keySet()) { + for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { maneuverLossLine.setMap((MapWidget) null); - ; } } maneuverLossLinesMap.clear(); } if (!maneuverLossInfoOverlayMap.isEmpty()) { - for (Map.Entry e : maneuverLossInfoOverlayMap.entrySet()) { + for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap + .entrySet()) { e.getValue().removeFromMap(); } maneuverLossInfoOverlayMap.clear(); } if (!maneuverLossCheckBoxValueStore.isEmpty()) { - for (Map.Entry e : maneuverLossCheckBoxValueStore.entrySet()) { + for (Map.Entry, Boolean> e : maneuverLossCheckBoxValueStore.entrySet()) { e.setValue(false); } maneuverLossCheckBoxValueStore.clear(); } } -/** Visualizes the ManeuverLoss as calculated in {@link ManeuverDetectorImpl#getManeuverLoss()} - * - */ - private void createManeuverLossLines(ManeuverDTO maneuver) { + /** + * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLoss()} and draw + * SmallTransparentInfoOverlays attached to the Polylines visualizing the maneuver. + * + */ + private void createManeuverLossLines(ManeuverDTO maneuver, CompetitorDTO competitor) { maneuverLossLines = new HashSet<>(); Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); @@ -2702,10 +2710,12 @@ public class RaceMap extends AbstractCompositeComponent options.setStrokeOpacity(0.5); maneuverLossLine.setOptions(options); } - maneuverLossLinesMap.put(maneuver.position, maneuverLossLines); + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); + maneuverLossLinesMap.put(t, maneuverLossLines); StringBuilder sb = new StringBuilder(); sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) - + " " + stringMessages.metersUnit() + '\n'); + + stringMessages.metersUnit() + '\n'); if (maneuver.type == ManeuverType.TACK) { sb.append(stringMessages.tackAngle() + ": "); } else if (maneuver.type == ManeuverType.JIBE) { @@ -2713,14 +2723,14 @@ public class RaceMap extends AbstractCompositeComponent } else { sb.append(stringMessages.maneuverAngle() + ": "); } - sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.directionChangeInDegrees)) + " " + sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.directionChangeInDegrees)) + stringMessages.degreesUnit()); maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); maneuverLossInfoOverlay.draw(); - maneuverLossInfoOverlayMap.put(maneuver.position, maneuverLossInfoOverlay); + maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); } /** diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java index f717e6272be..40a4900f410 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java @@ -22,4 +22,4 @@ public class ManeuverLossDTO implements IsSerializable { this.middleManeuverAngle = middleManeuverAngle; this.maneuverDuration = maneuverDuration; } -} +} \ No newline at end of file From 1493ae8be3d9e0452736114b848440b157794145 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Tue, 29 May 2018 18:36:08 +0200 Subject: [PATCH 12/53] bug4572: Trying to implement a visualization for the percentage of target vmg during a maneuver. Prepared some polylines for that. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 102 +++++++++++++++--- 1 file changed, 90 insertions(+), 12 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index f61f8749436..726c639d404 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -9,6 +9,7 @@ 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.Map.Entry; import java.util.Objects; @@ -1116,8 +1117,9 @@ public class RaceMap extends AbstractCompositeComponent removeAllManeuverMarkers(); } if (maneuverLossLines != null) { - removeManeuverLossLines(); + removeManeuverLossLinesAndInfoOverlays(); } + removeManeuverLossTargetVmgLines(); if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); @@ -2345,9 +2347,23 @@ public class RaceMap extends AbstractCompositeComponent maneuverLossCheckBoxValueStore.put(t, maneuverLossLinesCheckBox.getValue()); } }); + CheckBox maneuverLossTargetVMGCheckBox = new CheckBox ("Target VMG"); + if (maneuverLossTargetVMGCheckBoxValueStore.isEmpty()) { + maneuverLossTargetVMGCheckBoxValueStore.put(t, false); + } + maneuverLossTargetVMGCheckBox.setValue(maneuverLossTargetVMGCheckBoxValueStore.get(t)); + maneuverLossTargetVMGCheckBox.addClickHandler(new ClickHandler() { + + @Override + public void onClick(ClickEvent event) { + showManeuverLossTargetVmg(competitor, maneuver, maneuverLossTargetVMGCheckBox.getValue()); + maneuverLossTargetVMGCheckBoxValueStore.put(t, maneuverLossTargetVMGCheckBox.getValue()); + } + }); HorizontalPanel hPanel = new HorizontalPanel(); hPanel.add(maneuverLossWidget); hPanel.add(maneuverLossLinesCheckBox); + hPanel.add(maneuverLossTargetVMGCheckBox); vPanel.add(hPanel); } if (maneuver.markPassingTimePoint != null) { @@ -2630,26 +2646,90 @@ public class RaceMap extends AbstractCompositeComponent if (showManeuverVisualization == false) { Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, maneuver.type); - for (Polyline p : maneuverLossLinesMap.get(t)) { - p.setMap((MapWidget) null); + if (maneuverLossLinesMap.get(t) != null) { + for (Polyline p : maneuverLossLinesMap.get(t)) { + p.setMap((MapWidget) null); + } } maneuverLossInfoOverlayMap.get(t).removeFromMap(); } else { - createManeuverLossLines(maneuver, competitor); + createManeuverLossLinesAndInfoOverlays(maneuver, competitor); } } - + + private void showManeuverLossTargetVmg(CompetitorDTO competitor, ManeuverDTO maneuver, + boolean showManeuverLossTargetVmg) { + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); + if (!showManeuverLossTargetVmg) { + if (maneuverLossTargetVmgMap.get(t) != null) { + for (Polyline p : maneuverLossTargetVmgMap.get(t)) { + p.setMap((MapWidget) null); + } + } + } else { + maneuverLossTargetVmgMap.put(t, createManeuverLossTargetVmgPolyline(competitor, maneuver)); + } + + } + + private Set createManeuverLossTargetVmgPolyline(CompetitorDTO competitor, ManeuverDTO maneuver) { + List timePointList = new ArrayList<>(); + Set result = new HashSet<>(); + int stepInSeconds = 1; + for (long l = maneuver.timePointBefore.getTime() / 1000; l <= maneuver.timePointBefore.getTime() / 1000 + + maneuver.maneuverLoss.maneuverDuration.asSeconds(); l += stepInSeconds) { + timePointList.add(new Date(l * 1000)); + } + PolylineOptions options = PolylineOptions.newInstance(); + options.setGeodesic(true); + options.setStrokeOpacity(1.0); + options.setStrokeWeight(10); + //Color needs to be set individual + options.setStrokeColor("#000000"); + options.setZindex(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX); + options.setMap(map); + ListIterator iter = timePointList.listIterator(); + while (iter.hasNext()) { + iter.next(); + MVCArray pointsAsArray = MVCArray.newInstance(); + pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, iter.previous()).position)); + iter.next(); + pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, iter.next()).position)); + options.setPath(pointsAsArray); + Polyline res = Polyline.newInstance(options); + result.add(res); + } + return result; + } + + private void removeManeuverLossTargetVmgLines() { + if (!maneuverLossTargetVmgMap.isEmpty()) { + for (Triple t : maneuverLossTargetVmgMap.keySet()) { + for (Polyline p : maneuverLossTargetVmgMap.get(t)) { + p.setMap((MapWidget) null); + } + } + maneuverLossTargetVmgMap.clear(); + } + if (!maneuverLossTargetVMGCheckBoxValueStore.isEmpty()) { + maneuverLossTargetVMGCheckBoxValueStore.clear(); + } + } + private Map, Boolean> maneuverLossCheckBoxValueStore = new HashMap<>(); + private Map, Boolean> maneuverLossTargetVMGCheckBoxValueStore = new HashMap<>(); private SmallTransparentInfoOverlay maneuverLossInfoOverlay; private Set maneuverLossLines; private Map, Set> maneuverLossLinesMap = new HashMap<>(); + private Map, Set> maneuverLossTargetVmgMap = new HashMap<>(); private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); /** * Removes the Polylines visualizing the ManeuverLoss and the SmallTransparentInfoOverlays attached to the - * Polylines. Also clears the Checkbox that defines whether a maneuverLoss is visualized or not. + * Polylines. Also clears the CheckboxValueStore for maneuverLoss. */ - private void removeManeuverLossLines() { + private void removeManeuverLossLinesAndInfoOverlays() { if (!maneuverLossLinesMap.isEmpty()) { for (Triple t : maneuverLossLinesMap.keySet()) { for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { @@ -2666,9 +2746,6 @@ public class RaceMap extends AbstractCompositeComponent maneuverLossInfoOverlayMap.clear(); } if (!maneuverLossCheckBoxValueStore.isEmpty()) { - for (Map.Entry, Boolean> e : maneuverLossCheckBoxValueStore.entrySet()) { - e.setValue(false); - } maneuverLossCheckBoxValueStore.clear(); } } @@ -2678,7 +2755,7 @@ public class RaceMap extends AbstractCompositeComponent * SmallTransparentInfoOverlays attached to the Polylines visualizing the maneuver. * */ - private void createManeuverLossLines(ManeuverDTO maneuver, CompetitorDTO competitor) { + private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { maneuverLossLines = new HashSet<>(); Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); @@ -2951,7 +3028,8 @@ public class RaceMap extends AbstractCompositeComponent if (!(timer.getPlayState() == PlayStates.Playing) && lastManeuverResult != null) { removeAllManeuverMarkers(); showManeuvers(lastManeuverResult); - removeManeuverLossLines(); + removeManeuverLossLinesAndInfoOverlays(); + removeManeuverLossTargetVmgLines(); } } if (newSettings.isShowDouglasPeuckerPoints() != settings.isShowDouglasPeuckerPoints()) { From 0c7fe39d0280c76d2c21fdc74a4add14fe9a4b0d Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 15 Jun 2018 17:16:05 +0200 Subject: [PATCH 13/53] bug4572: first try for coloring tails during a maneuver. --- .../sailing/domain/tracking/ManeuverLoss.java | 4 - .../gwt/ui/client/shared/racemap/RaceMap.java | 531 +++++++++++------- 2 files changed, 330 insertions(+), 205 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java index dfe29c83720..3ef2591acec 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java @@ -17,10 +17,6 @@ import com.sap.sse.common.Duration; * Updated ManeuverLoss class to use it to visualize the correct maneuver loss on the race map. To draw the ManeuverLoss * needs the maneuverStartPosition, maneuverEndPosition, middleManeuverAngle, speedWithBearingBefore and * maneuverDuration. The ManeuverLoss is calculated by {@link ManeuverDetectorImpl#getManeuverLoss()}. - * - * - * | maneuverEndPosition -> x--¦ \ | )| / maneuverStartPosition -> x´ - * */ public class ManeuverLoss { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 726c639d404..c5a5a43ece2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -9,13 +9,15 @@ 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.Map.Entry; import java.util.Objects; import java.util.Set; import java.util.function.Consumer; +import com.google.gwt.canvas.client.Canvas; +import com.google.gwt.canvas.dom.client.Context2d; +import com.google.gwt.canvas.dom.client.TextMetrics; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; @@ -2343,7 +2345,7 @@ public class RaceMap extends AbstractCompositeComponent maneuverLossLinesCheckBox.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - visualizeManeuver(maneuver, maneuverLossLinesCheckBox.getValue(), competitor); + visualizeManeuverLoss(maneuver, maneuverLossLinesCheckBox.getValue(), competitor); maneuverLossCheckBoxValueStore.put(t, maneuverLossLinesCheckBox.getValue()); } }); @@ -2611,205 +2613,6 @@ public class RaceMap extends AbstractCompositeComponent marker.setMap(map); } - final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return ""; - } - }; - final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return ""; - } - }; - final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return ""; - } - }; - final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return ""; - } - }; - final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.maneuverLoss(); - } - }; - - private void visualizeManeuver(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { - if (showManeuverVisualization == false) { - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); - if (maneuverLossLinesMap.get(t) != null) { - for (Polyline p : maneuverLossLinesMap.get(t)) { - p.setMap((MapWidget) null); - } - } - maneuverLossInfoOverlayMap.get(t).removeFromMap(); - } else { - createManeuverLossLinesAndInfoOverlays(maneuver, competitor); - } - } - - private void showManeuverLossTargetVmg(CompetitorDTO competitor, ManeuverDTO maneuver, - boolean showManeuverLossTargetVmg) { - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); - if (!showManeuverLossTargetVmg) { - if (maneuverLossTargetVmgMap.get(t) != null) { - for (Polyline p : maneuverLossTargetVmgMap.get(t)) { - p.setMap((MapWidget) null); - } - } - } else { - maneuverLossTargetVmgMap.put(t, createManeuverLossTargetVmgPolyline(competitor, maneuver)); - } - - } - - private Set createManeuverLossTargetVmgPolyline(CompetitorDTO competitor, ManeuverDTO maneuver) { - List timePointList = new ArrayList<>(); - Set result = new HashSet<>(); - int stepInSeconds = 1; - for (long l = maneuver.timePointBefore.getTime() / 1000; l <= maneuver.timePointBefore.getTime() / 1000 - + maneuver.maneuverLoss.maneuverDuration.asSeconds(); l += stepInSeconds) { - timePointList.add(new Date(l * 1000)); - } - PolylineOptions options = PolylineOptions.newInstance(); - options.setGeodesic(true); - options.setStrokeOpacity(1.0); - options.setStrokeWeight(10); - //Color needs to be set individual - options.setStrokeColor("#000000"); - options.setZindex(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX); - options.setMap(map); - ListIterator iter = timePointList.listIterator(); - while (iter.hasNext()) { - iter.next(); - MVCArray pointsAsArray = MVCArray.newInstance(); - pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, iter.previous()).position)); - iter.next(); - pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, iter.next()).position)); - options.setPath(pointsAsArray); - Polyline res = Polyline.newInstance(options); - result.add(res); - } - return result; - } - - private void removeManeuverLossTargetVmgLines() { - if (!maneuverLossTargetVmgMap.isEmpty()) { - for (Triple t : maneuverLossTargetVmgMap.keySet()) { - for (Polyline p : maneuverLossTargetVmgMap.get(t)) { - p.setMap((MapWidget) null); - } - } - maneuverLossTargetVmgMap.clear(); - } - if (!maneuverLossTargetVMGCheckBoxValueStore.isEmpty()) { - maneuverLossTargetVMGCheckBoxValueStore.clear(); - } - } - - private Map, Boolean> maneuverLossCheckBoxValueStore = new HashMap<>(); - private Map, Boolean> maneuverLossTargetVMGCheckBoxValueStore = new HashMap<>(); - private SmallTransparentInfoOverlay maneuverLossInfoOverlay; - private Set maneuverLossLines; - private Map, Set> maneuverLossLinesMap = new HashMap<>(); - private Map, Set> maneuverLossTargetVmgMap = new HashMap<>(); - private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); - - /** - * Removes the Polylines visualizing the ManeuverLoss and the SmallTransparentInfoOverlays attached to the - * Polylines. Also clears the CheckboxValueStore for maneuverLoss. - */ - private void removeManeuverLossLinesAndInfoOverlays() { - if (!maneuverLossLinesMap.isEmpty()) { - for (Triple t : maneuverLossLinesMap.keySet()) { - for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { - maneuverLossLine.setMap((MapWidget) null); - } - } - maneuverLossLinesMap.clear(); - } - if (!maneuverLossInfoOverlayMap.isEmpty()) { - for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap - .entrySet()) { - e.getValue().removeFromMap(); - } - maneuverLossInfoOverlayMap.clear(); - } - if (!maneuverLossCheckBoxValueStore.isEmpty()) { - maneuverLossCheckBoxValueStore.clear(); - } - } - - /** - * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLoss()} and draw - * SmallTransparentInfoOverlays attached to the Polylines visualizing the maneuver. - * - */ - private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { - maneuverLossLines = new HashSet<>(); - Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); - Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); - Distance extrapolationOfManeuverStartPoint = new MeterDistance( - maneuver.maneuverLoss.speedWithBearingBefore.speedInKnots - * maneuver.maneuverLoss.maneuverDuration.asHours() * 1852); - Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.maneuverStartPosition - .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); - Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition - .getIntersection(bearingBefore, maneuver.position, middleManeuverAngle); - Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, - middleManeuverAngleLineInfoProvider, "#ffffff")); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, - extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff")); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, - projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00")); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, - projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00")); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, - projectedManeuverEndPosition, maneuverLossLineInfoProvider, color)); - for (Polyline maneuverLossLine : maneuverLossLines) { - PolylineOptions options = PolylineOptions.newInstance(); - options.setStrokeOpacity(0.5); - maneuverLossLine.setOptions(options); - } - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); - maneuverLossLinesMap.put(t, maneuverLossLines); - StringBuilder sb = new StringBuilder(); - sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) - + stringMessages.metersUnit() + '\n'); - if (maneuver.type == ManeuverType.TACK) { - sb.append(stringMessages.tackAngle() + ": "); - } else if (maneuver.type == ManeuverType.JIBE) { - sb.append(stringMessages.jibeAngle() + ": "); - } else { - sb.append(stringMessages.maneuverAngle() + ": "); - } - sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.directionChangeInDegrees)) - + stringMessages.degreesUnit()); - maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, - RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); - maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, - new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); - maneuverLossInfoOverlay.draw(); - maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); - } - /** * @param date * the point in time for which to determine the competitor's boat position; approximated by using the fix @@ -3484,4 +3287,330 @@ public class RaceMap extends AbstractCompositeComponent public RaceMapLifecycle getLifecycle() { return raceMapLifecycle; } + final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return "Middle Maneuver Angle"; + } + }; + final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return "Extrapolated Maneuver Start Position"; + } + }; + final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return ""; + } + }; + final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return ""; + } + }; + final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.maneuverLoss(); + } + }; + + + + private Map, Boolean> maneuverLossCheckBoxValueStore = new HashMap<>(); + private Map, Boolean> maneuverLossTargetVMGCheckBoxValueStore = new HashMap<>(); +// private SmallTransparentInfoOverlay maneuverLossInfoOverlay; + private Set maneuverLossLines; + private Map, Set> maneuverLossLinesMap = new HashMap<>(); + private Map, Set> maneuverLossTargetVmgMap = new HashMap<>(); + private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); + + private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { + if (showManeuverVisualization == false) { + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); + if (maneuverLossLinesMap.get(t) != null) { + for (Polyline p : maneuverLossLinesMap.get(t)) { + p.setMap((MapWidget) null); + } + } + maneuverLossInfoOverlayMap.get(t).removeFromMap(); + } else { + createManeuverLossLinesAndInfoOverlays(maneuver, competitor); + } + } + + /** + * Removes the Polylines visualizing the ManeuverLoss and the SmallTransparentInfoOverlays attached to the + * Polylines. Also clears the CheckboxValueStore for maneuverLoss. + */ + private void removeManeuverLossLinesAndInfoOverlays() { + if (!maneuverLossLinesMap.isEmpty()) { + for (Triple t : maneuverLossLinesMap.keySet()) { + for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { + maneuverLossLine.setMap((MapWidget) null); + } + } + maneuverLossLinesMap.clear(); + } + if (!maneuverLossInfoOverlayMap.isEmpty()) { + for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap + .entrySet()) { + e.getValue().removeFromMap(); + } + maneuverLossInfoOverlayMap.clear(); + } + if (!maneuverLossCheckBoxValueStore.isEmpty()) { + maneuverLossCheckBoxValueStore.clear(); + } + } + + /** + * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLoss()} and draw + * SmallTransparentInfoOverlays attached to the Polylines visualizing the maneuver. + * + */ + private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { + maneuverLossLines = new HashSet<>(); + Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); + Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); + Distance extrapolationOfManeuverStartPoint = new MeterDistance( + maneuver.maneuverLoss.speedWithBearingBefore.speedInKnots + * maneuver.maneuverLoss.maneuverDuration.asHours() * 1852); + Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.maneuverStartPosition + .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); + Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition + .getIntersection(bearingBefore, maneuver.position, middleManeuverAngle); + Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, + middleManeuverAngleLineInfoProvider, "#ffffff")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, + extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, + projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, + projectedManeuverEndPosition, maneuverLossLineInfoProvider, color)); + for (Polyline maneuverLossLine : maneuverLossLines) { + PolylineOptions options = PolylineOptions.newInstance(); + options.setStrokeOpacity(0.5); + maneuverLossLine.setOptions(options); + } + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); + maneuverLossLinesMap.put(t, maneuverLossLines); + StringBuilder sb = new StringBuilder(); + sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + + stringMessages.metersUnit() + '\n'); + if (maneuver.type == ManeuverType.TACK) { + sb.append(stringMessages.tackAngle() + ": "); + } else if (maneuver.type == ManeuverType.JIBE) { + sb.append(stringMessages.jibeAngle() + ": "); + } else { + sb.append(stringMessages.maneuverAngle() + ": "); + } + sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.directionChangeInDegrees)) + + stringMessages.degreesUnit()); + SmallTransparentInfoOverlay maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, + RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); + maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, + new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); + maneuverLossInfoOverlay.draw(); + maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); + } + + private void showManeuverLossTargetVmg(CompetitorDTO competitor, ManeuverDTO maneuver, + boolean showManeuverLossTargetVmg) { + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); + if (!showManeuverLossTargetVmg) { + if (maneuverLossTargetVmgMap.get(t) != null) { + for (Polyline p : maneuverLossTargetVmgMap.get(t)) { + p.setMap((MapWidget) null); + } + } + } else { + maneuverLossTargetVmgMap.put(t, createManeuverLossTargetVmgPolyline(competitor, maneuver)); + drawLegendVmg(); + } + + } + + + +// private void drawLegendVmg() { +// double x, y; +// x=100; +// y=16; +// double w = 1; +// double h = 20; +// int maxIdx = 300; +// Context2d context2d = streamletOverlay.streamletLegend.getContext2d(); +// context2d.setFillStyle("rgba(0,0,0,.3)"); +// context2d.setLineWidth(1.0); +// context2d.beginPath(); +// context2d.fillRect(x-7.0, y-16.0, w*maxIdx+15.0, 56.0); +// context2d.closePath(); +// context2d.stroke(); +// context2d.setFillStyle("white"); +// String label = stringMessages.windSpeedInKnots(); +// TextMetrics txtmet; +// txtmet = context2d.measureText(label); +// context2d.fillText(label, x + (w*maxIdx - txtmet.getWidth())/2.0, y - 5.0); +// for(int idx=0; idx <= maxIdx; idx++) { +// //double speed = idx * 24.0 / maxIdx; +// double speed = 4.0 + idx * 16.0 / maxIdx; +// context2d.setFillStyle(streamletOverlay.windField.getColor(speed)); +// context2d.beginPath(); +// context2d.fillRect(x + idx*w, y, w, h); +// context2d.closePath(); +// context2d.stroke(); +// if (Math.abs(speed % 2.0) < (12.0/maxIdx)) { +// context2d.setStrokeStyle("white"); +// context2d.setLineWidth(1.0); +// context2d.beginPath(); +// context2d.moveTo(x + idx*w, y + h); +// context2d.lineTo(x + idx*w, y + h + 7.0); +// context2d.closePath(); +// context2d.stroke(); +// context2d.setFillStyle("white"); +// label = ""+Math.round(speed); +// txtmet = context2d.measureText(label); +// context2d.fillText(label, x + idx*w - txtmet.getWidth()/2.0, y + h + 8.0 + 8.0); +// } +// } +// } + + private Set createManeuverLossTargetVmgPolyline(CompetitorDTO competitor, ManeuverDTO maneuver) { + final List timePointList = new ArrayList<>(); + final Set result = new HashSet<>(); + final int stepInSeconds = 1; + for (long l = maneuver.timePointBefore.getTime() / 1000; l <= maneuver.timePointBefore.getTime() / 1000 + + maneuver.maneuverLoss.maneuverDuration.asSeconds(); l += stepInSeconds) { + timePointList.add(new Date(l * 1000)); + } + PolylineOptions options = PolylineOptions.newInstance(); + options.setGeodesic(true); + options.setStrokeOpacity(1.0); + options.setStrokeWeight(10); + options.setZindex(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX); + options.setMap(map); + +// getLastCombinedWindTrackInfoDTO(); +// +// BoatClassDTO boatClass = competitorSelection.getBoat(competitor).getBoatClass(); + + if (timePointList.size() != 0) { + for (int i = 0; i < timePointList.size() - 1; i++) { + + double targetVmgInKnots = 6; + double percent = getBoatFix(competitor, timePointList.get(i)).speedWithBearing.speedInKnots + / targetVmgInKnots; + // Integer h = Math.toIntExact(Math.round(120 * percent)); + options.setStrokeColor("hsl(" + Math.toIntExact(Math.round(120 * percent)) + ", 100%, 50%)"); + + MVCArray pointsAsArray = MVCArray.newInstance(); + pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, timePointList.get(i)).position)); + pointsAsArray + .push(coordinateSystem.toLatLng(getBoatFix(competitor, timePointList.get(i + 1)).position)); + options.setPath(pointsAsArray); + Polyline p = Polyline.newInstance(options); + result.add(p); + } + } + +// ListIterator iter = timePointList.listIterator(); +// if (iter.hasNext()) { +// iter.next(); +// while (iter.hasNext()) { +// MVCArray pointsAsArray = MVCArray.newInstance(); +// pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, iter.previous()).position)); +// iter.next(); +// pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, iter.next()).position)); +// options.setPath(pointsAsArray); +// Polyline res = Polyline.newInstance(options); +// result.add(res); +// } +// } + return result; + } + + private void drawLegendVmg() { + Canvas maneuverLossTargetVmgLegend = Canvas.createIfSupported(); + maneuverLossTargetVmgLegend.addStyleName("MapTargetVmgLegend"); + map.setControls(ControlPosition.LEFT_BOTTOM, maneuverLossTargetVmgLegend); + int canvasWidth = 500; + int canvasHeight = 60; + maneuverLossTargetVmgLegend.getParent().addStyleName("MapTargetVmgLegendParentDiv"); + maneuverLossTargetVmgLegend.setWidth(String.valueOf(canvasWidth)); + maneuverLossTargetVmgLegend.setHeight(String.valueOf(canvasHeight)); + maneuverLossTargetVmgLegend.setCoordinateSpaceWidth(canvasWidth); + maneuverLossTargetVmgLegend.setCoordinateSpaceHeight(canvasHeight); + double x, y; + x=100; + y=16; + double w = 1; + double h = 20; + int maxIdx = 300; + Context2d context2d = maneuverLossTargetVmgLegend.getContext2d(); + context2d.setFillStyle("rgba(0,0,0,.3)"); + context2d.setLineWidth(1.0); + context2d.beginPath(); + context2d.fillRect(x-7.0, y-16.0, w*maxIdx+15.0, 56.0); + context2d.closePath(); + context2d.stroke(); + context2d.setFillStyle("white"); + String label = stringMessages.windSpeedInKnots(); + TextMetrics txtmet; + txtmet = context2d.measureText(label); + context2d.fillText(label, x + (w*maxIdx - txtmet.getWidth())/2.0, y - 5.0); + for(int idx=0; idx <= maxIdx; idx++) { + //double speed = idx * 24.0 / maxIdx; +// double speed = 4.0 + idx * 16.0 / maxIdx; + context2d.setFillStyle("hsl(" + Math.round((idx+1)/maxIdx)*120 + ", 100%, 50%)"); +// context2d.setFillStyle(streamletOverlay.windField.getColor(speed)); + context2d.beginPath(); + context2d.fillRect(x + idx*w, y, w, h); + context2d.closePath(); + context2d.stroke(); +// if (Math.abs(speed % 2.0) < (12.0/maxIdx)) { +// context2d.setStrokeStyle("white"); +// context2d.setLineWidth(1.0); +// context2d.beginPath(); +// context2d.moveTo(x + idx*w, y + h); +// context2d.lineTo(x + idx*w, y + h + 7.0); +// context2d.closePath(); +// context2d.stroke(); +// context2d.setFillStyle("white"); +// label = ""+Math.round(speed); +// txtmet = context2d.measureText(label); +// context2d.fillText(label, x + idx*w - txtmet.getWidth()/2.0, y + h + 8.0 + 8.0); +// } + } + } + + + private void removeManeuverLossTargetVmgLines() { + if (!maneuverLossTargetVmgMap.isEmpty()) { + for (Triple t : maneuverLossTargetVmgMap.keySet()) { + for (Polyline p : maneuverLossTargetVmgMap.get(t)) { + p.setMap((MapWidget) null); + } + } + maneuverLossTargetVmgMap.clear(); + } + if (!maneuverLossTargetVMGCheckBoxValueStore.isEmpty()) { + maneuverLossTargetVMGCheckBoxValueStore.clear(); + } + } + } From a0bd3329e30f0180b5f39b11f57114dfb908b2bd Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 29 Jun 2018 13:57:01 +0200 Subject: [PATCH 14/53] bug4572: removed unneccesary comments. changed strokeWeight of maneuverloss polyline. --- .../sailing/gwt/ui/client/StringMessages.java | 2 + .../gwt/ui/client/StringMessages.properties | 4 +- .../ui/client/StringMessages_de.properties | 4 +- .../gwt/ui/client/shared/racemap/RaceMap.java | 278 +++--------------- 4 files changed, 55 insertions(+), 233 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 6028702e9b2..b59121f6c6b 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2081,4 +2081,6 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String multiUrlChangeExplain(); String lastEvent(String locationOrVenue); String teaserOverallLinkToolTip(); + String middleManeuverAngle(); + String extrapolatedManeuverStartPosition(); } 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 c6bdd26e273..c6e7ed28338 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 @@ -2074,4 +2074,6 @@ multiUrlChangeNewURL=new url multiUrlNoPrefixWarning=No common prefix was found, this usually means that not all selected videos are hosted at the same location currently. Proceed at own risk! multiUrlChangeExplain=This dialog will bulk replace common parts at the start of mediatrack urls. Make sure all urls start with the same prefix! Also please take a look at the new url column, and test the resulting urls before pressing save! lastEvent=Last Event: {0} -teaserOverallLinkToolTip=To see the overall series, click the yellow corner \ No newline at end of file +teaserOverallLinkToolTip=To see the overall series, click the yellow corner +middleManeuverAngle=Middle maneuver angle +extrapolatedManeuverStartPosition=Extrapolated Maneuver Start Position \ 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 1d188c02f7e..1223aceb2e9 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 @@ -2070,4 +2070,6 @@ multiUrlChangeNewURL=Neue Url multiUrlNoPrefixWarning=Es wurde kein gemeinsamer Präfix gefunden, dies deutet darauf hin, dass nicht alle ausgewählten Videos vom gleichen Host kommen. Auf eigene Gefahr fortsetzen! multiUrlChangeExplain=Dieser Dialog ermöglicht es, mehrere URL-Anfänge gleichzeitig auszutauschen. Hierzu müssen diese den gleichen Präfix besitzen. Bitte unbedingt die Spalte mit den neuen URLs beachten und diese testen bevor gespeichert wird! lastEvent=Letztes Event: {0} -teaserOverallLinkToolTip=Um die Serien-Übersicht zu sehen, in die gelbe Ecke klicken \ No newline at end of file +teaserOverallLinkToolTip=Um die Serien-Übersicht zu sehen, in die gelbe Ecke klicken +middleManeuverAngle=Manöver-Winkelhalbierende +extrapolatedManeuverStartPosition=Extrapolierte Manöver Anfangsposition \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index b7c8fe0fd89..6a203704008 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -15,9 +15,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; -import com.google.gwt.canvas.client.Canvas; -import com.google.gwt.canvas.dom.client.Context2d; -import com.google.gwt.canvas.dom.client.TextMetrics; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; @@ -451,6 +448,18 @@ public class RaceMap extends AbstractCompositeComponent /** Callback to set the visibility of the wind chart. */ private final Consumer showWindChartForProvider; private ManagedInfoWindow managedInfoWindow; + + /** The Map where for each competitor and each maneuver is stored if it is visualized or not.*/ + private Map, Boolean> maneuverLossCheckBoxValueStore = new HashMap<>(); + + /** The maneuver loss visualizing polylines.*/ + private Set maneuverLossLines; + + /** The Map where the polylines for the specific maneuver are stored.*/ + private Map, Set> maneuverLossLinesMap = new HashMap<>(); + + /** The Map where the info overlays for a maneuver loss visualization are stored.*/ + private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); private class AdvantageLineUpdater implements QuickRanksListener { @Override @@ -1121,8 +1130,6 @@ public class RaceMap extends AbstractCompositeComponent if (maneuverLossLines != null) { removeManeuverLossLinesAndInfoOverlays(); } - removeManeuverLossTargetVmgLines(); - if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } @@ -1710,14 +1717,14 @@ public class RaceMap extends AbstractCompositeComponent (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider, - "grey"); + "grey", 1); leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* * showLine */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider, - "grey"); + "grey", 1); } } @@ -1788,7 +1795,7 @@ public class RaceMap extends AbstractCompositeComponent || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, startLineLeftPosition, - startLineRightPosition, startLineInfoProvider, "#ffffff"); + startLineRightPosition, startLineInfoProvider, "#ffffff", 1); // draw the finish line final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0); @@ -1807,7 +1814,7 @@ public class RaceMap extends AbstractCompositeComponent finishLineAdvantageText.delete(0, finishLineAdvantageText.length()); } finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, finishLineLeftPosition, - finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR); + finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR, 1); // the control point pairs for which we already decided whether or not // to show a course middle line for; values tell whether to show the line and for which zero-based // start waypoint index to do so; when for an equal control point pair multiple decisions with different @@ -1900,7 +1907,7 @@ public class RaceMap extends AbstractCompositeComponent } }; return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, - lineInfoProvider, "#2268a0"); + lineInfoProvider, "#2268a0", 1); } private interface LineInfoProvider { @@ -1918,7 +1925,7 @@ public class RaceMap extends AbstractCompositeComponent */ private Polyline showOrRemoveOrUpdateLine(Polyline lineToShowOrRemoveOrUpdate, final boolean showLine, final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, - String lineColorRGB) { + String lineColorRGB, int strokeWeight) { if (position1DTO != null && position2DTO != null) { if (showLine) { LatLng courseMiddleLinePoint1 = coordinateSystem.toLatLng(position1DTO); @@ -1929,7 +1936,7 @@ public class RaceMap extends AbstractCompositeComponent options.setClickable(true); options.setGeodesic(true); options.setStrokeColor(lineColorRGB); - options.setStrokeWeight(1); + options.setStrokeWeight(strokeWeight); options.setStrokeOpacity(1.0); pointsAsArray = MVCArray.newInstance(); lineToShowOrRemoveOrUpdate = Polyline.newInstance(options); @@ -2348,23 +2355,23 @@ public class RaceMap extends AbstractCompositeComponent maneuverLossCheckBoxValueStore.put(t, maneuverLossLinesCheckBox.getValue()); } }); - CheckBox maneuverLossTargetVMGCheckBox = new CheckBox ("Target VMG"); - if (maneuverLossTargetVMGCheckBoxValueStore.isEmpty()) { - maneuverLossTargetVMGCheckBoxValueStore.put(t, false); - } - maneuverLossTargetVMGCheckBox.setValue(maneuverLossTargetVMGCheckBoxValueStore.get(t)); - maneuverLossTargetVMGCheckBox.addClickHandler(new ClickHandler() { - - @Override - public void onClick(ClickEvent event) { - showManeuverLossTargetVmg(competitor, maneuver, maneuverLossTargetVMGCheckBox.getValue()); - maneuverLossTargetVMGCheckBoxValueStore.put(t, maneuverLossTargetVMGCheckBox.getValue()); - } - }); +// CheckBox maneuverLossTargetVMGCheckBox = new CheckBox ("Target VMG"); +// if (maneuverLossTargetVMGCheckBoxValueStore.isEmpty()) { +// maneuverLossTargetVMGCheckBoxValueStore.put(t, false); +// } +// maneuverLossTargetVMGCheckBox.setValue(maneuverLossTargetVMGCheckBoxValueStore.get(t)); +// maneuverLossTargetVMGCheckBox.addClickHandler(new ClickHandler() { +// +// @Override +// public void onClick(ClickEvent event) { +// showManeuverLossTargetVmg(competitor, maneuver, maneuverLossTargetVMGCheckBox.getValue()); +// maneuverLossTargetVMGCheckBoxValueStore.put(t, maneuverLossTargetVMGCheckBox.getValue()); +// } +// }); HorizontalPanel hPanel = new HorizontalPanel(); hPanel.add(maneuverLossWidget); hPanel.add(maneuverLossLinesCheckBox); - hPanel.add(maneuverLossTargetVMGCheckBox); +// hPanel.add(maneuverLossTargetVMGCheckBox); vPanel.add(hPanel); } if (maneuver.markPassingTimePoint != null) { @@ -2831,7 +2838,6 @@ public class RaceMap extends AbstractCompositeComponent removeAllManeuverMarkers(); showManeuvers(lastManeuverResult); removeManeuverLossLinesAndInfoOverlays(); - removeManeuverLossTargetVmgLines(); } } if (newSettings.isShowDouglasPeuckerPoints() != settings.isShowDouglasPeuckerPoints()) { @@ -3289,13 +3295,13 @@ public class RaceMap extends AbstractCompositeComponent final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return "Middle Maneuver Angle"; + return stringMessages.middleManeuverAngle(); } }; final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return "Extrapolated Maneuver Start Position"; + return stringMessages.extrapolatedManeuverStartPosition(); } }; final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { @@ -3317,17 +3323,13 @@ public class RaceMap extends AbstractCompositeComponent } }; - - - private Map, Boolean> maneuverLossCheckBoxValueStore = new HashMap<>(); - private Map, Boolean> maneuverLossTargetVMGCheckBoxValueStore = new HashMap<>(); -// private SmallTransparentInfoOverlay maneuverLossInfoOverlay; - private Set maneuverLossLines; - private Map, Set> maneuverLossLinesMap = new HashMap<>(); - private Map, Set> maneuverLossTargetVmgMap = new HashMap<>(); - private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); - - private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { + /** + * Visualizes the Maneuver Loss, creates the corresponding polylines and Info Overlays calling + * {@link #createManeuverLossLinesAndInfoOverlays(ManeuverDTO, CompetitorDTO)}. If called with + * showManeuverVisualization=false removes the polylines and infooverlays, if there are any. + */ + private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, + CompetitorDTO competitor) { if (showManeuverVisualization == false) { Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, maneuver.type); @@ -3390,15 +3392,15 @@ public class RaceMap extends AbstractCompositeComponent String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, - middleManeuverAngleLineInfoProvider, "#ffffff")); + middleManeuverAngleLineInfoProvider, "#ffffff", 1)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, - extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff")); + extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff", 1)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, - projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00")); + projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00", 1)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, - projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00")); + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00", 1)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, - projectedManeuverEndPosition, maneuverLossLineInfoProvider, color)); + projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 3)); for (Polyline maneuverLossLine : maneuverLossLines) { PolylineOptions options = PolylineOptions.newInstance(); options.setStrokeOpacity(0.5); @@ -3426,190 +3428,4 @@ public class RaceMap extends AbstractCompositeComponent maneuverLossInfoOverlay.draw(); maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); } - - private void showManeuverLossTargetVmg(CompetitorDTO competitor, ManeuverDTO maneuver, - boolean showManeuverLossTargetVmg) { - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); - if (!showManeuverLossTargetVmg) { - if (maneuverLossTargetVmgMap.get(t) != null) { - for (Polyline p : maneuverLossTargetVmgMap.get(t)) { - p.setMap((MapWidget) null); - } - } - } else { - maneuverLossTargetVmgMap.put(t, createManeuverLossTargetVmgPolyline(competitor, maneuver)); - drawLegendVmg(); - } - - } - - - -// private void drawLegendVmg() { -// double x, y; -// x=100; -// y=16; -// double w = 1; -// double h = 20; -// int maxIdx = 300; -// Context2d context2d = streamletOverlay.streamletLegend.getContext2d(); -// context2d.setFillStyle("rgba(0,0,0,.3)"); -// context2d.setLineWidth(1.0); -// context2d.beginPath(); -// context2d.fillRect(x-7.0, y-16.0, w*maxIdx+15.0, 56.0); -// context2d.closePath(); -// context2d.stroke(); -// context2d.setFillStyle("white"); -// String label = stringMessages.windSpeedInKnots(); -// TextMetrics txtmet; -// txtmet = context2d.measureText(label); -// context2d.fillText(label, x + (w*maxIdx - txtmet.getWidth())/2.0, y - 5.0); -// for(int idx=0; idx <= maxIdx; idx++) { -// //double speed = idx * 24.0 / maxIdx; -// double speed = 4.0 + idx * 16.0 / maxIdx; -// context2d.setFillStyle(streamletOverlay.windField.getColor(speed)); -// context2d.beginPath(); -// context2d.fillRect(x + idx*w, y, w, h); -// context2d.closePath(); -// context2d.stroke(); -// if (Math.abs(speed % 2.0) < (12.0/maxIdx)) { -// context2d.setStrokeStyle("white"); -// context2d.setLineWidth(1.0); -// context2d.beginPath(); -// context2d.moveTo(x + idx*w, y + h); -// context2d.lineTo(x + idx*w, y + h + 7.0); -// context2d.closePath(); -// context2d.stroke(); -// context2d.setFillStyle("white"); -// label = ""+Math.round(speed); -// txtmet = context2d.measureText(label); -// context2d.fillText(label, x + idx*w - txtmet.getWidth()/2.0, y + h + 8.0 + 8.0); -// } -// } -// } - - private Set createManeuverLossTargetVmgPolyline(CompetitorDTO competitor, ManeuverDTO maneuver) { - final List timePointList = new ArrayList<>(); - final Set result = new HashSet<>(); - final int stepInSeconds = 1; - for (long l = maneuver.timePointBefore.getTime() / 1000; l <= maneuver.timePointBefore.getTime() / 1000 - + maneuver.maneuverLoss.maneuverDuration.asSeconds(); l += stepInSeconds) { - timePointList.add(new Date(l * 1000)); - } - PolylineOptions options = PolylineOptions.newInstance(); - options.setGeodesic(true); - options.setStrokeOpacity(1.0); - options.setStrokeWeight(10); - options.setZindex(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX); - options.setMap(map); - -// getLastCombinedWindTrackInfoDTO(); -// -// BoatClassDTO boatClass = competitorSelection.getBoat(competitor).getBoatClass(); - - if (timePointList.size() != 0) { - for (int i = 0; i < timePointList.size() - 1; i++) { - - double targetVmgInKnots = 6; - double percent = getBoatFix(competitor, timePointList.get(i)).speedWithBearing.speedInKnots - / targetVmgInKnots; - // Integer h = Math.toIntExact(Math.round(120 * percent)); - options.setStrokeColor("hsl(" + Math.toIntExact(Math.round(120 * percent)) + ", 100%, 50%)"); - - MVCArray pointsAsArray = MVCArray.newInstance(); - pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, timePointList.get(i)).position)); - pointsAsArray - .push(coordinateSystem.toLatLng(getBoatFix(competitor, timePointList.get(i + 1)).position)); - options.setPath(pointsAsArray); - Polyline p = Polyline.newInstance(options); - result.add(p); - } - } - -// ListIterator iter = timePointList.listIterator(); -// if (iter.hasNext()) { -// iter.next(); -// while (iter.hasNext()) { -// MVCArray pointsAsArray = MVCArray.newInstance(); -// pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, iter.previous()).position)); -// iter.next(); -// pointsAsArray.push(coordinateSystem.toLatLng(getBoatFix(competitor, iter.next()).position)); -// options.setPath(pointsAsArray); -// Polyline res = Polyline.newInstance(options); -// result.add(res); -// } -// } - return result; - } - - private void drawLegendVmg() { - Canvas maneuverLossTargetVmgLegend = Canvas.createIfSupported(); - maneuverLossTargetVmgLegend.addStyleName("MapTargetVmgLegend"); - map.setControls(ControlPosition.LEFT_BOTTOM, maneuverLossTargetVmgLegend); - int canvasWidth = 500; - int canvasHeight = 60; - maneuverLossTargetVmgLegend.getParent().addStyleName("MapTargetVmgLegendParentDiv"); - maneuverLossTargetVmgLegend.setWidth(String.valueOf(canvasWidth)); - maneuverLossTargetVmgLegend.setHeight(String.valueOf(canvasHeight)); - maneuverLossTargetVmgLegend.setCoordinateSpaceWidth(canvasWidth); - maneuverLossTargetVmgLegend.setCoordinateSpaceHeight(canvasHeight); - double x, y; - x=100; - y=16; - double w = 1; - double h = 20; - int maxIdx = 300; - Context2d context2d = maneuverLossTargetVmgLegend.getContext2d(); - context2d.setFillStyle("rgba(0,0,0,.3)"); - context2d.setLineWidth(1.0); - context2d.beginPath(); - context2d.fillRect(x-7.0, y-16.0, w*maxIdx+15.0, 56.0); - context2d.closePath(); - context2d.stroke(); - context2d.setFillStyle("white"); - String label = stringMessages.windSpeedInKnots(); - TextMetrics txtmet; - txtmet = context2d.measureText(label); - context2d.fillText(label, x + (w*maxIdx - txtmet.getWidth())/2.0, y - 5.0); - for(int idx=0; idx <= maxIdx; idx++) { - //double speed = idx * 24.0 / maxIdx; -// double speed = 4.0 + idx * 16.0 / maxIdx; - context2d.setFillStyle("hsl(" + Math.round((idx+1)/maxIdx)*120 + ", 100%, 50%)"); -// context2d.setFillStyle(streamletOverlay.windField.getColor(speed)); - context2d.beginPath(); - context2d.fillRect(x + idx*w, y, w, h); - context2d.closePath(); - context2d.stroke(); -// if (Math.abs(speed % 2.0) < (12.0/maxIdx)) { -// context2d.setStrokeStyle("white"); -// context2d.setLineWidth(1.0); -// context2d.beginPath(); -// context2d.moveTo(x + idx*w, y + h); -// context2d.lineTo(x + idx*w, y + h + 7.0); -// context2d.closePath(); -// context2d.stroke(); -// context2d.setFillStyle("white"); -// label = ""+Math.round(speed); -// txtmet = context2d.measureText(label); -// context2d.fillText(label, x + idx*w - txtmet.getWidth()/2.0, y + h + 8.0 + 8.0); -// } - } - } - - - private void removeManeuverLossTargetVmgLines() { - if (!maneuverLossTargetVmgMap.isEmpty()) { - for (Triple t : maneuverLossTargetVmgMap.keySet()) { - for (Polyline p : maneuverLossTargetVmgMap.get(t)) { - p.setMap((MapWidget) null); - } - } - maneuverLossTargetVmgMap.clear(); - } - if (!maneuverLossTargetVMGCheckBoxValueStore.isEmpty()) { - maneuverLossTargetVMGCheckBoxValueStore.clear(); - } - } - } From 763a339b237d297b0c65b22a6315e91b4024da0a Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Fri, 29 Jun 2018 14:14:06 +0200 Subject: [PATCH 15/53] in ManeuverLosDTO made fields private and offer getters Change-Id: I41230e71fb2ad2284b92f06a54bcaf8880e55bd5 --- .../gwt/ui/client/shared/racemap/RaceMap.java | 18 +++++------ .../gwt/ui/shared/ManeuverLossDTO.java | 30 +++++++++++++++---- 2 files changed, 34 insertions(+), 14 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 6a203704008..da201ef724a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -3376,28 +3376,28 @@ public class RaceMap extends AbstractCompositeComponent */ private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { maneuverLossLines = new HashSet<>(); - Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); - Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); + Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.getSpeedWithBearingBefore().bearingInDegrees); + Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.getMiddleManeuverAngle()); Distance extrapolationOfManeuverStartPoint = new MeterDistance( - maneuver.maneuverLoss.speedWithBearingBefore.speedInKnots - * maneuver.maneuverLoss.maneuverDuration.asHours() * 1852); - Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.maneuverStartPosition + maneuver.maneuverLoss.getSpeedWithBearingBefore().speedInKnots + * maneuver.maneuverLoss.getManeuverDuration().asHours() * 1852); + Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.getManeuverStartPosition() .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); - Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition + Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.getManeuverStartPosition() .getIntersection(bearingBefore, maneuver.position, middleManeuverAngle); Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( + Position projectedManeuverEndPosition = maneuver.maneuverLoss.getManeuverEndPosition().projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, "#ffffff", 1)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.getManeuverStartPosition(), extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff", 1)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00", 1)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.getManeuverEndPosition(), projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00", 1)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 3)); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java index 40a4900f410..3460cacb72e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java @@ -5,11 +5,11 @@ import com.sap.sailing.domain.common.Position; import com.sap.sse.common.Duration; public class ManeuverLossDTO implements IsSerializable { - public Position maneuverStartPosition; - public Position maneuverEndPosition; - public SpeedWithBearingDTO speedWithBearingBefore; - public Double middleManeuverAngle; - public Duration maneuverDuration; + private Position maneuverStartPosition; + private Position maneuverEndPosition; + private SpeedWithBearingDTO speedWithBearingBefore; + private Double middleManeuverAngle; + private Duration maneuverDuration; public ManeuverLossDTO() { } @@ -22,4 +22,24 @@ public class ManeuverLossDTO implements IsSerializable { this.middleManeuverAngle = middleManeuverAngle; this.maneuverDuration = maneuverDuration; } + + public Position getManeuverStartPosition() { + return maneuverStartPosition; + } + + public Position getManeuverEndPosition() { + return maneuverEndPosition; + } + + public SpeedWithBearingDTO getSpeedWithBearingBefore() { + return speedWithBearingBefore; + } + + public Double getMiddleManeuverAngle() { + return middleManeuverAngle; + } + + public Duration getManeuverDuration() { + return maneuverDuration; + } } \ No newline at end of file From c4515bb6a53d4e0c50857059dd34cd453078da1a Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 29 Jun 2018 15:30:50 +0200 Subject: [PATCH 16/53] bug4572: trying to fix formatting for RaceMap.java --- .../gwt/ui/client/shared/racemap/RaceMap.java | 1717 +++++++---------- 1 file changed, 746 insertions(+), 971 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 6a203704008..ed3b5372108 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -73,7 +73,9 @@ import com.google.gwt.user.client.ui.Label; 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.Bearing; import com.sap.sailing.domain.common.Bounds; +import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.ManeuverType; import com.sap.sailing.domain.common.NauticalSide; import com.sap.sailing.domain.common.Position; @@ -84,6 +86,7 @@ import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.CompetitorWithBoatDTO; import com.sap.sailing.domain.common.dto.LeaderboardDTO; +import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing; @@ -133,9 +136,7 @@ import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapAPIKey; import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapStyleHelper; import com.sap.sailing.gwt.ui.shared.racemap.RaceSimulationOverlay; import com.sap.sailing.gwt.ui.shared.racemap.WindStreamletsRaceboardOverlay; -import com.sap.sse.common.Bearing; import com.sap.sse.common.Color; -import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimeRange; @@ -144,7 +145,6 @@ import com.sap.sse.common.Util.Pair; import com.sap.sse.common.Util.Triple; import com.sap.sse.common.filter.Filter; import com.sap.sse.common.filter.FilterSet; -import com.sap.sse.common.impl.DegreeBearingImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.RGBColor; import com.sap.sse.common.impl.TimeRangeImpl; @@ -161,31 +161,30 @@ import com.sap.sse.gwt.client.shared.components.SettingsDialog; import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; import com.sap.sse.gwt.client.shared.settings.ComponentContext; -public class RaceMap extends AbstractCompositeComponent - implements TimeListener, CompetitorSelectionChangeListener, RaceTimesInfoProviderListener, TailFactory, - RequiresDataInitialization, RequiresResize, QuickRankProvider { +public class RaceMap extends AbstractCompositeComponent implements TimeListener, CompetitorSelectionChangeListener, + RaceTimesInfoProviderListener, TailFactory, RequiresDataInitialization, RequiresResize, QuickRankProvider { private static final String ADVANTAGE_LINE_COLOR = "#ff9900"; // orange private static final String FINISH_LINE_COLOR = "#000000"; private static final Color LOWLIGHTED_TAIL_COLOR = new RGBColor(200, 200, 200); public static final String GET_RACE_MAP_DATA_CATEGORY = "getRaceMapData"; public static final String GET_WIND_DATA_CATEGORY = "getWindData"; private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; - + private static final String COMPACT_HEADER_STYLE = "compactHeader"; public static final Color WATER_COLOR = new RGBColor(0, 67, 125); - + private AbsolutePanel rootPanel = new AbsolutePanel(); - + private MapWidget map; - + /** * Always valid, non-null. Must be used to map all coordinates, headings, bearings, and directions * displayed on the map, including the orientations of any canvases such as boat icons, wind displays etc. that are - * embedded in the map. The coordinate systems facilitates the possibility of transformed displays such as rotated - * and translated versions of the map, implementing the "wind-up" view. + * embedded in the map. The coordinate systems facilitates the possibility of transformed displays such as + * rotated and translated versions of the map, implementing the "wind-up" view. */ private DelegateCoordinateSystem coordinateSystem; - + private FlowPanel headerPanel; private AbsolutePanel panelForLeftHeaderLabels; private AbsolutePanel panelForRightHeaderLabels; @@ -206,18 +205,17 @@ public class RaceMap extends AbstractCompositeComponent private Polyline finishLine; /** - * Polyline for the advantage line (the leading line for the boats, orthogonal to the wind direction; touching the - * leading boat). + * Polyline for the advantage line (the leading line for the boats, orthogonal to the wind direction; touching the leading boat). */ private Polyline advantageLine; - + private AdvantageLineAnimator advantageTimer; - + /** * The windward of two Polylines representing a triangle between startline and first mark. */ private Polyline windwardStartLineMarkToFirstMarkLine; - + /** * The leeward of two Polylines representing a triangle between startline and first mark. */ @@ -226,12 +224,12 @@ public class RaceMap extends AbstractCompositeComponent private class AdvantageLineMouseOverMapHandler implements MouseOverMapHandler { private double trueWindAngle; private Date date; - + public AdvantageLineMouseOverMapHandler(double trueWindAngle, Date date) { this.trueWindAngle = trueWindAngle; this.date = date; } - + public void setTrueWindBearing(double trueWindAngle) { this.trueWindAngle = trueWindAngle; } @@ -242,43 +240,42 @@ public class RaceMap extends AbstractCompositeComponent @Override public void onEvent(MouseOverMapEvent event) { - map.setTitle(stringMessages.advantageLine() + " (from " - + new DegreeBearingImpl(Math.round(trueWindAngle)).reverse().getDegrees() + "deg" - + (date == null ? ")" : ", " + date) + ")"); + map.setTitle(stringMessages.advantageLine()+" (from "+new DegreeBearingImpl(Math.round(trueWindAngle)).reverse().getDegrees()+"deg"+ + (date == null ? ")" : ", "+ date) + ")"); } }; - + private AdvantageLineMouseOverMapHandler advantageLineMouseOverHandler; - + /** * Polylines for the course middle lines; keys are the two control points delimiting the leg for which the - * {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there are - * multiple legs using the same control points in different directions, using a {@link Set} makes this independent - * of the order of the two control points. If no course middle line is currently being shown for a pair of control - * points, the map will not contain a value for this pair. + * {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there + * are multiple legs using the same control points in different directions, using a {@link Set} makes this + * independent of the order of the two control points. If no course middle line is currently being shown for + * a pair of control points, the map will not contain a value for this pair. */ private final Map, Polyline> courseMiddleLines; private final Map courseSidelines; - + /** - * When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the lines - * that show the tooltip text in a little info box linked to the line. When the line is removed by + * When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the + * lines that show the tooltip text in a little info box linked to the line. When the line is removed by * {@link #showOrRemoveOrUpdateLine(Polyline, boolean, Position, Position, LineInfoProvider, String)}, these - * overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is deactivated, - * all these overlays need to go. + * overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is + * deactivated, all these overlays need to go. */ private final Map infoOverlaysForLinesForCourseGeometry; - + /** * Wind data used to display the advantage line. Retrieved by a {@link GetWindInfoAction} execution and used in * {@link #showAdvantageLine(Iterable, Date)}. */ private WindInfoForRaceDTO lastCombinedWindTrackInfoDTO; - + /** - * Manages the cached set of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the boat positions as well as their - * graphical counterpart in the form of {@link Polyline}s. + * Manages the cached set of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the boat positions as well as their graphical counterpart in the + * form of {@link Polyline}s. */ private final FixesAndTails fixesAndTails; @@ -291,7 +288,7 @@ public class RaceMap extends AbstractCompositeComponent * html5 canvases used for competitor info display on the map */ private final CompetitorInfoOverlays competitorInfoOverlays; - + private SmallTransparentInfoOverlay countDownOverlay; /** @@ -304,12 +301,11 @@ public class RaceMap extends AbstractCompositeComponent * html5 canvas used to display course marks including buoy zones */ private final Map courseMarkOverlays; - + private final Map courseMarkClickHandlers; /** - * Maps from the {@link MarkDTO#getIdAsString() mark's ID converted to a string} to the corresponding - * {@link MarkDTO} + * Maps from the {@link MarkDTO#getIdAsString() mark's ID converted to a string} to the corresponding {@link MarkDTO} */ private final Map markDTOs; @@ -328,9 +324,9 @@ public class RaceMap extends AbstractCompositeComponent private Map> lastManeuverResult; private Map> lastDouglasPeuckerResult; - + private final RaceCompetitorSelectionProvider competitorSelection; - + private final RaceCompetitorSet raceCompetitorSet; /** @@ -341,7 +337,7 @@ public class RaceMap extends AbstractCompositeComponent private final Timer timer; private RaceTimesInfoDTO lastRaceTimesInfo; - + /** * 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, @@ -358,83 +354,81 @@ public class RaceMap extends AbstractCompositeComponent */ private int startedProcessingRequestID; - private RaceMapImageManager raceMapImageManager; + private RaceMapImageManager raceMapImageManager; private RaceMapSettings settings; private final RaceMapLifecycle raceMapLifecycle; - + private final StringMessages stringMessages; - + private boolean isMapInitialized; private Date lastTimeChangeBeforeInitialization; - + private int lastLegNumber; /** * The strategy for maintaining and delivering the "quick ranks" information. The provider will be informed about - * quick ranks received from a {@link RaceMapDataDTO#quickRanks} field but may choose to ignore this information, - * e.g., if it can assume that more current information about ranks and leg numbers is available from a - * {@link LeaderboardDTO}. + * quick ranks received from a {@link RaceMapDataDTO#quickRanks} field but may choose to ignore this information, e.g., + * if it can assume that more current information about ranks and leg numbers is available from a {@link LeaderboardDTO}. */ private QuickRanksDTOProvider quickRanksDTOProvider; - + private final CombinedWindPanel combinedWindPanel; private final TrueNorthIndicatorPanel trueNorthIndicatorPanel; private final FlowPanel topLeftControlsWrapperPanel; - + private final AsyncActionsExecutor asyncActionsExecutor; /** - * The map bounds as last received by map callbacks; used to determine whether to suppress the boat animation during - * zoom/pan + * The map bounds as last received by map callbacks; used to determine whether to suppress the boat animation during zoom/pan */ private LatLngBounds currentMapBounds; // bounds to which bounds-changed-handler compares - private int currentZoomLevel; // zoom-level to which bounds-changed-handler compares - - private boolean autoZoomIn = false; // flags auto-zoom-in in progress + private int currentZoomLevel; // zoom-level to which bounds-changed-handler compares + + private boolean autoZoomIn = false; // flags auto-zoom-in in progress private boolean autoZoomOut = false; // flags auto-zoom-out in progress - private int autoZoomLevel; // zoom-level to which auto-zoom-in/-out is zooming - LatLngBounds autoZoomLatLngBounds; // bounds to which auto-zoom-in/-out is panning&zooming - + private int autoZoomLevel; // zoom-level to which auto-zoom-in/-out is zooming + LatLngBounds autoZoomLatLngBounds; // bounds to which auto-zoom-in/-out is panning&zooming + private RaceSimulationOverlay simulationOverlay; private WindStreamletsRaceboardOverlay streamletOverlay; - + private final boolean isSimulationEnabled; - + private static final String GET_POLAR_CATEGORY = "getPolar"; - + /** * Tells about the availability of polar / VPP data for this race. If available, the simulation feature can be * offered to the user. */ private boolean hasPolar; - + private final RegattaAndRaceIdentifier raceIdentifier; - + /** - * When the user requests wind-up display this may happen at a point where no mark positions are known or when no - * wind direction is known yet. In this case, this flag will be set, and when wind information or course mark + * When the user requests wind-up display this may happen at a point where no mark positions are known or when + * no wind direction is known yet. In this case, this flag will be set, and when wind information or course mark * positions are received later, this flag is checked, and if set, a {@link #updateCoordinateSystemFromSettings()} * call is issued to make sure that the user's request for a new coordinate system is honored. */ private boolean requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown; - + /** - * Tells whether currently an auto-zoom is in progress; this is used particularly to keep the smooth CSS boat - * transitions active while auto-zooming whereas stopping them seems the better option for manual zooms. + * Tells whether currently an auto-zoom is in progress; this is used particularly to keep the smooth CSS boat transitions + * active while auto-zooming whereas stopping them seems the better option for manual zooms. */ private boolean autoZoomInProgress; - + /** - * Tells whether currently an orientation change is in progress; this is required handle map events during the - * configuration of the map during an orientation change. + * Tells whether currently an orientation change is in progress; this is required handle map events during the configuration of the map + * during an orientation change. */ private boolean orientationChangeInProgress; - + private final NumberFormat numberFormatOneDecimal = NumberFormat.getFormat("0.0"); private final NumberFormat numberFormatNoDecimal = NumberFormat.getFormat("0"); - + /** * The competitor for which the advantage line is currently showing. Should this competitor's quick rank change, or * should ranks be received while this field is {@code null}, the advantage line @@ -444,56 +438,39 @@ public class RaceMap extends AbstractCompositeComponent protected Label estimatedDurationOverlay; private RaceMapStyle raceMapStyle; private final boolean showHeaderPanel; - + /** Callback to set the visibility of the wind chart. */ private final Consumer showWindChartForProvider; private ManagedInfoWindow managedInfoWindow; - - /** The Map where for each competitor and each maneuver is stored if it is visualized or not.*/ - private Map, Boolean> maneuverLossCheckBoxValueStore = new HashMap<>(); - - /** The maneuver loss visualizing polylines.*/ - private Set maneuverLossLines; - - /** The Map where the polylines for the specific maneuver are stored.*/ - private Map, Set> maneuverLossLinesMap = new HashMap<>(); - - /** The Map where the info overlays for a maneuver loss visualization are stored.*/ - private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); private class AdvantageLineUpdater implements QuickRanksListener { @Override public void rankChanged(String competitorIdAsString, QuickRankDTO oldQuickRank, QuickRankDTO quickRank) { - if (advantageLineCompetitor == null - || (oldQuickRank != null - && advantageLineCompetitor.getIdAsString().equals(oldQuickRank.competitor.getIdAsString())) - || (quickRank != null - && advantageLineCompetitor.getIdAsString().equals(quickRank.competitor.getIdAsString()))) { - showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), - /* timeForPositionTransitionMillis */ -1 /* (no transition) */); + if (advantageLineCompetitor == null || + (oldQuickRank != null && advantageLineCompetitor.getIdAsString().equals(oldQuickRank.competitor.getIdAsString())) || + (quickRank != null && advantageLineCompetitor.getIdAsString().equals(quickRank.competitor.getIdAsString()))) { + showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), /* timeForPositionTransitionMillis */ -1 /* (no transition) */); } } } - + public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, - RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, - AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer timer, - RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, - StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, - boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider) { + RaceMapSettings raceMapSettings, + SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, + ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, + RaceCompetitorSet raceCompetitorSet, StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, + RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider) { this(parent, context, raceMapLifecycle, raceMapSettings, sailingService, asyncActionsExecutor, errorReporter, timer, competitorSelection, raceCompetitorSet, stringMessages, raceIdentifier, raceMapResources, - showHeaderPanel, quickRanksDTOProvider, visible -> { - }); + showHeaderPanel, quickRanksDTOProvider, visible -> {}); } - + public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, - RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, - AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer timer, - RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, - StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, - boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, - Consumer showWindChartForProvider) { + RaceMapSettings raceMapSettings, + SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, + ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, + StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, + RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, Consumer showWindChartForProvider) { super(parent, context); this.showHeaderPanel = showHeaderPanel; this.quickRanksDTOProvider = quickRanksDTOProvider; @@ -536,11 +513,9 @@ public class RaceMap extends AbstractCompositeComponent initializeData(settings.isShowMapControls(), showHeaderPanel); raceMapStyle = raceMapResources.raceMapStyle(); raceMapStyle.ensureInjected(); - combinedWindPanel = new CombinedWindPanel(this, raceMapImageManager, raceMapStyle, stringMessages, - coordinateSystem); + combinedWindPanel = new CombinedWindPanel(this, raceMapImageManager, raceMapStyle, stringMessages, coordinateSystem); combinedWindPanel.setVisible(false); - trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, raceMapStyle, stringMessages, - coordinateSystem); + trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, raceMapStyle, stringMessages, coordinateSystem); trueNorthIndicatorPanel.setVisible(true); topLeftControlsWrapperPanel = new FlowPanel(); topLeftControlsWrapperPanel.add(combinedWindPanel); @@ -551,22 +526,20 @@ public class RaceMap extends AbstractCompositeComponent initWidget(rootPanel); this.setSize("100%", "100%"); } - + public void setQuickRanksDTOProvider(QuickRanksDTOProvider newQuickRanksDTOProvider) { if (this.quickRanksDTOProvider != null) { this.quickRanksDTOProvider.moveListernersTo(newQuickRanksDTOProvider); } this.quickRanksDTOProvider = newQuickRanksDTOProvider; } - /** * The {@link WindDTO#dampenedTrueWindFromDeg} direction if {@link #lastCombinedWindTrackInfoDTO} has a * {@link WindSourceType#COMBINED} source which has at least one fix recorded; null otherwise. */ private Bearing getLastCombinedTrueWindFromDirection() { if (lastCombinedWindTrackInfoDTO != null) { - for (Entry e : lastCombinedWindTrackInfoDTO.windTrackInfoByWindSource - .entrySet()) { + for (Entry e : lastCombinedWindTrackInfoDTO.windTrackInfoByWindSource.entrySet()) { if (e.getKey().getType() == WindSourceType.COMBINED) { final List windFixes = e.getValue().windFixes; if (!windFixes.isEmpty()) { @@ -577,7 +550,7 @@ public class RaceMap extends AbstractCompositeComponent } return null; } - + /** * @return {@code true} if the map was redrawn by the call to this method */ @@ -590,8 +563,7 @@ public class RaceMap extends AbstractCompositeComponent if (centerOfCourse != null) { final Bearing lastCombinedTrueWindFromDirection = getLastCombinedTrueWindFromDirection(); if (lastCombinedTrueWindFromDirection != null) { - // new equator shall point 90deg right of the "from" wind direction to make wind come from top of - // map + // new equator shall point 90deg right of the "from" wind direction to make wind come from top of map coordinateSystem.setCoordinateSystem(new RotateAndTranslateCoordinateSystem(centerOfCourse, lastCombinedTrueWindFromDirection.add(new DegreeBearingImpl(90)))); if (map != null) { @@ -618,19 +590,17 @@ public class RaceMap extends AbstractCompositeComponent } coordinateSystem.setCoordinateSystem(new IdentityCoordinateSystem()); } - if (mapOptions != null) { // if no coordinate system change happened that affects an existing map, don't redraw + if (mapOptions != null) { // if no coordinate system change happened that affects an existing map, don't redraw fixesAndTails.clearTails(); redraw(); redrawn = true; - // zooming and setting options while the event loop is still working doesn't work reliably; defer until - // event loop returns + // zooming and setting options while the event loop is still working doesn't work reliably; defer until event loop returns Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { if (map != null) { map.setOptions(mapOptions); - // ensure zooming to what the settings tell, or defaults if what the settings tell isn't - // possible right now + // ensure zooming to what the settings tell, or defaults if what the settings tell isn't possible right now mapFirstZoomDone = false; trueNorthIndicatorPanel.redraw(); orientationChangeInProgress = false; @@ -650,122 +620,116 @@ public class RaceMap extends AbstractCompositeComponent loadLibraries.add(LoadLibrary.GEOMETRY); Runnable onLoad = new Runnable() { - @Override - public void run() { - MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false); - map = new MapWidget(mapOptions); - rootPanel.add(map, 0, 0); - if (showHeaderPanel) { - Image sapLogo = createSAPLogo(); - rootPanel.add(sapLogo); - } + @Override + public void run() { + MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false); + map = new MapWidget(mapOptions); + rootPanel.add(map, 0, 0); + if (showHeaderPanel) { + Image sapLogo = createSAPLogo(); + rootPanel.add(sapLogo); + } + + map.setControls(ControlPosition.LEFT_TOP, topLeftControlsWrapperPanel); + adjustLeftControlsIndent(); - map.setControls(ControlPosition.LEFT_TOP, topLeftControlsWrapperPanel); - adjustLeftControlsIndent(); + RaceMap.this.raceMapImageManager.loadMapIcons(map); + map.setSize("100%", "100%"); + map.addZoomChangeHandler(new ZoomChangeMapHandler() { + @Override + public void onEvent(ZoomChangeMapEvent event) { + if (!autoZoomIn && !autoZoomOut && !orientationChangeInProgress) { + // stop automatic zoom after a manual zoom event; automatic zoom in zoomMapToNewBounds will restore old settings + final List emptyList = Collections.emptyList(); + RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, settings.getZoomSettings().isZoomToSelectedCompetitors()); + settings = new RaceMapSettings(settings, clearedZoomSettings); + } + // TODO bug489 when in wind-up mode, avoid zooming out too far; perhaps zoom back in if zoomed out too far + } + }); + map.addDragEndHandler(new DragEndMapHandler() { + @Override + public void onEvent(DragEndMapEvent event) { + // stop automatic zoom after a manual drag event + autoZoomIn = false; + autoZoomOut = false; + final List emptyList = Collections.emptyList(); + RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, settings.getZoomSettings().isZoomToSelectedCompetitors()); + settings = new RaceMapSettings(settings, clearedZoomSettings); + } + }); + map.addIdleHandler(new IdleMapHandler() { + @Override + public void onEvent(IdleMapEvent event) { + // the "idle"-event is raised at the end of map-animations + if (autoZoomIn) { + // finalize zoom-in that was started with panTo() in zoomMapToNewBounds() + map.setZoom(autoZoomLevel); + autoZoomIn = false; + } + if (autoZoomOut) { + // finalize zoom-out that was started with setZoom() in zoomMapToNewBounds() + map.panTo(autoZoomLatLngBounds.getCenter()); + autoZoomOut = false; + } + } + }); + map.addBoundsChangeHandler(new BoundsChangeMapHandler() { + @Override + public void onEvent(BoundsChangeMapEvent event) { + int newZoomLevel = map.getZoom(); + if (!isAutoZoomInProgress() && (newZoomLevel != currentZoomLevel)) { + removeTransitions(); + } + if ((streamletOverlay != null) && !map.getBounds().equals(currentMapBounds)) { + streamletOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); + } + if ((simulationOverlay != null) && !map.getBounds().equals(currentMapBounds)) { + simulationOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); + } + currentMapBounds = map.getBounds(); + currentZoomLevel = newZoomLevel; + headerPanel.getElement().getStyle().setWidth(map.getOffsetWidth(), Unit.PX); + } + }); + + // If there was a time change before the API was loaded, reset the time + if (lastTimeChangeBeforeInitialization != null) { + timeChanged(lastTimeChangeBeforeInitialization, null); + lastTimeChangeBeforeInitialization = null; + } + // Initialize streamlet canvas for wind visualization; it shouldn't be doing anything unless it's visible + streamletOverlay = new WindStreamletsRaceboardOverlay(getMap(), /* zIndex */ 0, + timer, raceIdentifier, sailingService, asyncActionsExecutor, stringMessages, coordinateSystem); + streamletOverlay.addToMap(); + if (settings.isShowWindStreamletOverlay()) { + streamletOverlay.setColors(settings.isShowWindStreamletColors()); + streamletOverlay.setVisible(true); + } - RaceMap.this.raceMapImageManager.loadMapIcons(map); - map.setSize("100%", "100%"); - map.addZoomChangeHandler(new ZoomChangeMapHandler() { - @Override - public void onEvent(ZoomChangeMapEvent event) { - if (!autoZoomIn && !autoZoomOut && !orientationChangeInProgress) { - // stop automatic zoom after a manual zoom event; automatic zoom in zoomMapToNewBounds will - // restore old settings - final List emptyList = Collections.emptyList(); - RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, - settings.getZoomSettings().isZoomToSelectedCompetitors()); - settings = new RaceMapSettings(settings, clearedZoomSettings); - } - // TODO bug489 when in wind-up mode, avoid zooming out too far; perhaps zoom back in if zoomed - // out too far - } - }); - map.addDragEndHandler(new DragEndMapHandler() { - @Override - public void onEvent(DragEndMapEvent event) { - // stop automatic zoom after a manual drag event - autoZoomIn = false; - autoZoomOut = false; - final List emptyList = Collections.emptyList(); - RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, - settings.getZoomSettings().isZoomToSelectedCompetitors()); - settings = new RaceMapSettings(settings, clearedZoomSettings); - } - }); - map.addIdleHandler(new IdleMapHandler() { - @Override - public void onEvent(IdleMapEvent event) { - // the "idle"-event is raised at the end of map-animations - if (autoZoomIn) { - // finalize zoom-in that was started with panTo() in zoomMapToNewBounds() - map.setZoom(autoZoomLevel); - autoZoomIn = false; - } - if (autoZoomOut) { - // finalize zoom-out that was started with setZoom() in zoomMapToNewBounds() - map.panTo(autoZoomLatLngBounds.getCenter()); - autoZoomOut = false; - } - } - }); - map.addBoundsChangeHandler(new BoundsChangeMapHandler() { - @Override - public void onEvent(BoundsChangeMapEvent event) { - int newZoomLevel = map.getZoom(); - if (!isAutoZoomInProgress() && (newZoomLevel != currentZoomLevel)) { - removeTransitions(); - } - if ((streamletOverlay != null) && !map.getBounds().equals(currentMapBounds)) { - streamletOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); - } - if ((simulationOverlay != null) && !map.getBounds().equals(currentMapBounds)) { - simulationOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); - } - currentMapBounds = map.getBounds(); - currentZoomLevel = newZoomLevel; - headerPanel.getElement().getStyle().setWidth(map.getOffsetWidth(), Unit.PX); - } - }); - - // If there was a time change before the API was loaded, reset the time - if (lastTimeChangeBeforeInitialization != null) { - timeChanged(lastTimeChangeBeforeInitialization, null); - lastTimeChangeBeforeInitialization = null; - } - // Initialize streamlet canvas for wind visualization; it shouldn't be doing anything unless it's - // visible - streamletOverlay = new WindStreamletsRaceboardOverlay(getMap(), /* zIndex */ 0, timer, raceIdentifier, - sailingService, asyncActionsExecutor, stringMessages, coordinateSystem); - streamletOverlay.addToMap(); - if (settings.isShowWindStreamletOverlay()) { - streamletOverlay.setColors(settings.isShowWindStreamletColors()); - streamletOverlay.setVisible(true); - } - - if (isSimulationEnabled) { - // determine availability of polar diagram - setHasPolar(); - // initialize simulation canvas - simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, - sailingService, stringMessages, asyncActionsExecutor, coordinateSystem); - simulationOverlay.addToMap(); - showSimulationOverlay(settings.isShowSimulationOverlay()); - } - if (showHeaderPanel) { - createHeaderPanel(map); - } - if (showMapControls) { - createSettingsButton(map); - } - // Data has been initialized - RaceMap.this.isMapInitialized = true; - RaceMap.this.redraw(); - trueNorthIndicatorPanel.redraw(); - showAdditionalControls(map); - RaceMap.this.managedInfoWindow = new ManagedInfoWindow(map); - } + if (isSimulationEnabled) { + // determine availability of polar diagram + setHasPolar(); + // initialize simulation canvas + simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, sailingService, stringMessages, asyncActionsExecutor, coordinateSystem); + simulationOverlay.addToMap(); + showSimulationOverlay(settings.isShowSimulationOverlay()); + } + if (showHeaderPanel) { + createHeaderPanel(map); + } + if (showMapControls) { + createSettingsButton(map); + } + // Data has been initialized + RaceMap.this.isMapInitialized = true; + RaceMap.this.redraw(); + trueNorthIndicatorPanel.redraw(); + showAdditionalControls(map); + RaceMap.this.managedInfoWindow = new ManagedInfoWindow(map); + } }; - LoadApi.go(onLoad, loadLibraries, sensor, GoogleMapAPIKey.V3_PARAMS); + LoadApi.go(onLoad, loadLibraries, sensor, "key="+GoogleMapAPIKey.V3_APIKey); } /** @@ -794,13 +758,14 @@ public class RaceMap extends AbstractCompositeComponent } /** - * Creates a header panel where additional information can be displayed by using {@link #getLeftHeaderPanel()} or - * {@link #getRightHeaderPanel()}. + * Creates a header panel where additional information can be displayed by using + * {@link #getLeftHeaderPanel()} or {@link #getRightHeaderPanel()}. * - * This panel is transparent and configured in such a way that it moves other controls down by its height. To - * achieve the goal of not having added widgets transparent this widget consists of two parts: First one is the - * transparent panel and the second one is the panel for the controls. The controls then need to moved onto the - * panel by using CSS. + * This panel is transparent and configured in such a way that it moves other controls + * down by its height. To achieve the goal of not having added widgets transparent + * this widget consists of two parts: First one is the transparent panel and the + * second one is the panel for the controls. The controls then need to moved onto + * the panel by using CSS. */ private void createHeaderPanel(MapWidget map) { // we need a panel that does not have any transparency to have the @@ -820,7 +785,7 @@ public class RaceMap extends AbstractCompositeComponent // controls at RIGHT would not get the correct top setting map.setControls(ControlPosition.TOP_RIGHT, headerPanel); } - + private void createSettingsButton(MapWidget map) { final Component component = this; Button settingsButton = new Button(); @@ -857,11 +822,11 @@ public class RaceMap extends AbstractCompositeComponent public void redraw() { timeChanged(timer.getTime(), null); } - + Map getBoatOverlays() { return Collections.unmodifiableMap(boatOverlays); } - + protected RaceCompetitorSelectionProvider getCompetitorSelection() { return competitorSelection; } @@ -877,36 +842,33 @@ public class RaceMap extends AbstractCompositeComponent public MapWidget getMap() { return map; } - + public RaceSimulationOverlay getSimulationOverlay() { return simulationOverlay; } - + /** * @return the Panel where labels or other controls for the header can be positioned */ public AbsolutePanel getLeftHeaderPanel() { return panelForLeftHeaderLabels; } - + public AbsolutePanel getRightHeaderPanel() { return panelForRightHeaderLabels; } - + @Override - public void raceTimesInfosReceived(Map raceTimesInfos, - long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { - timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, - clientTimeWhenResponseWasReceived); + public void raceTimesInfosReceived(Map raceTimesInfos, long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { + timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived); this.lastRaceTimesInfo = raceTimesInfos.get(raceIdentifier); } /** - * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard - * contents}, null is used as time point. The condition for this is encapsulated in this method so - * others can find out. For example, when a time change is signaled due to local offset / delay adjustments, no - * additional call to {@link #loadCompleteLeaderboard(Date)} would be required as null will be passed - * in any case, not being affected by local time offsets. + * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard contents}, null + * is used as time point. The condition for this is encapsulated in this method so others can find out. For example, when a time change + * is signaled due to local offset / delay adjustments, no additional call to {@link #loadCompleteLeaderboard(Date)} would be required + * as null will be passed in any case, not being affected by local time offsets. */ private boolean useNullAsTimePoint() { return timer.getPlayMode() == PlayModes.Live; @@ -920,66 +882,57 @@ public class RaceMap extends AbstractCompositeComponent final Iterable competitorsToShow = getCompetitorsToShow(); if (race != null) { final long transitionTimeInMillis = calculateTimeForPositionTransitionInMillis(newTime, oldTime); - final com.sap.sse.common.Util.Triple, Map, Map> fromAndToAndOverlap = fixesAndTails - .computeFromAndTo(newTime, competitorsToShow, - settings.getEffectiveTailLengthInMilliseconds()); + final com.sap.sse.common.Util.Triple, Map, Map> fromAndToAndOverlap = + fixesAndTails.computeFromAndTo(newTime, competitorsToShow, settings.getEffectiveTailLengthInMilliseconds()); // Request map data update, possibly in two calls; see method details - callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers( - fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow); + callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers(fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow); // draw the wind into the map, get the combined wind List windSourceTypeNames = new ArrayList(); windSourceTypeNames.add(WindSourceType.EXPEDITION.name()); windSourceTypeNames.add(WindSourceType.WINDFINDER.name()); windSourceTypeNames.add(WindSourceType.COMBINED.name()); - GetWindInfoAction getWindInfoAction = new GetWindInfoAction(sailingService, race, newTime, 1000L, 1, - windSourceTypeNames, + GetWindInfoAction getWindInfoAction = new GetWindInfoAction(sailingService, race, newTime, 1000L, 1, windSourceTypeNames, /* onlyUpToNewestEvent==false means get us any data we can get by a best effort */ false); - asyncActionsExecutor.execute(getWindInfoAction, GET_WIND_DATA_CATEGORY, - new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError( - "Error obtaining wind information: " + caught.getMessage(), - true /* silentMode */); - } + asyncActionsExecutor.execute(getWindInfoAction, GET_WIND_DATA_CATEGORY, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error obtaining wind information: " + caught.getMessage(), true /*silentMode */); + } - @Override - public void onSuccess(WindInfoForRaceDTO windInfo) { - List> windSourcesToShow = new ArrayList>(); - if (windInfo != null) { - lastCombinedWindTrackInfoDTO = windInfo; - showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); - for (WindSource windSource : windInfo.windTrackInfoByWindSource.keySet()) { - WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource - .get(windSource); - switch (windSource.getType()) { - case EXPEDITION: - case WINDFINDER: - // we filter out measured wind sources with very low confidence - if (windTrackInfoDTO.minWindConfidence > 0.0001) { - windSourcesToShow.add( - new com.sap.sse.common.Util.Pair( - windSource, windTrackInfoDTO)); - } - break; - case COMBINED: - showCombinedWindOnMap(windSource, windTrackInfoDTO); - if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { - updateCoordinateSystemFromSettings(); - } - break; - default: - // Which wind sources are requested is defined in a list above this - // action. So we throw here an exception to notice a missing source. - throw new UnsupportedOperationException( - "There is currently no support for the enum value '" - + windSource.getType() + "' in this method."); + @Override + public void onSuccess(WindInfoForRaceDTO windInfo) { + List> windSourcesToShow = new ArrayList>(); + if (windInfo != null) { + lastCombinedWindTrackInfoDTO = windInfo; + showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); + for (WindSource windSource : windInfo.windTrackInfoByWindSource.keySet()) { + WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource.get(windSource); + switch (windSource.getType()) { + case EXPEDITION: + case WINDFINDER: + // we filter out measured wind sources with very low confidence + if (windTrackInfoDTO.minWindConfidence > 0.0001) { + windSourcesToShow.add(new com.sap.sse.common.Util.Pair(windSource, windTrackInfoDTO)); } - } + break; + case COMBINED: + showCombinedWindOnMap(windSource, windTrackInfoDTO); + if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { + updateCoordinateSystemFromSettings(); + } + break; + default: + // Which wind sources are requested is defined in a list above this + // action. So we throw here an exception to notice a missing source. + throw new UnsupportedOperationException( + "There is currently no support for the enum value '" + + windSource.getType() + "' in this method."); } - showWindSensorsOnMap(windSourcesToShow); } - }); + } + showWindSensorsOnMap(windSourcesToShow); + } + }); } } } @@ -988,7 +941,6 @@ public class RaceMap extends AbstractCompositeComponent public WindInfoForRaceDTO getLastCombinedWindTrackInfoDTO() { return lastCombinedWindTrackInfoDTO; } - /** * Requests updates for map data and, when received, updates the map structures accordingly. *

@@ -1009,14 +961,12 @@ public class RaceMap extends AbstractCompositeComponent * current tail length setting the new tail segment does not overlap with the old one, requiring a full load of the * entire tail data for that competitor. For these non-overlapping requests, this method creates a * {@link GetBoatPositionsAction} request loading the entire tail required, but not quick ranks, sidelines and mark - * positions. Updating the results of this call is done in - * {@link #updateBoatPositions(Date, long, Map, Iterable, Map, boolean)}. + * positions. Updating the results of this call is done in {@link #updateBoatPositions(Date, long, Map, Iterable, Map, boolean)}. *

*/ private void callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers( final Triple, Map, Map> fromAndToAndOverlap, - RegattaAndRaceIdentifier race, final Date newTime, final long transitionTimeInMillis, - final Iterable competitorsToShow) { + RegattaAndRaceIdentifier race, final Date newTime, final long transitionTimeInMillis, final Iterable competitorsToShow) { final Map fromTimesForQuickCall = new HashMap<>(); final Map toTimesForQuickCall = new HashMap<>(); final Map fromTimesForNonOverlappingTailsCall = new HashMap<>(); @@ -1027,8 +977,7 @@ public class RaceMap extends AbstractCompositeComponent fromTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); toTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getB().get(e.getKey())); } else { - // no overlap; add competitor to request with a zero-length interval asking only position at newTime, - // not the entire tail + // no overlap; add competitor to request with a zero-length interval asking only position at newTime, not the entire tail fromTimesForQuickCall.put(e.getKey(), newTime); toTimesForQuickCall.put(e.getKey(), newTime); fromTimesForNonOverlappingTailsCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); @@ -1046,56 +995,46 @@ public class RaceMap extends AbstractCompositeComponent // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for // all its entries so that the other response received for GetBoatPositionsAction will consider this an // overlap if it happens after this update. - asyncActionsExecutor.execute( - new GetRaceMapDataAction(sailingService, competitorsByIdAsString, race, - useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, - /* extrapolate */true, - (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null), - raceCompetitorSet - .getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID(), - newTime, settings.isShowEstimatedDuration()), - GET_RACE_MAP_DATA_CATEGORY, getRaceMapDataCallback(newTime, transitionTimeInMillis, - fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter)); - // next, if necessary, do the full thing; the two calls have different action classes, so throttling should not - // drop one for the other + asyncActionsExecutor.execute(new GetRaceMapDataAction(sailingService, competitorsByIdAsString, + race, useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, /* extrapolate */true, + (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null), + raceCompetitorSet.getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID(), newTime, settings.isShowEstimatedDuration()), + GET_RACE_MAP_DATA_CATEGORY, + getRaceMapDataCallback(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter)); + // next, if necessary, do the full thing; the two calls have different action classes, so throttling should not drop one for the other if (!fromTimesForNonOverlappingTailsCall.keySet().isEmpty()) { - asyncActionsExecutor.execute( - new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, - toTimesForNonOverlappingTailsCall, /* extrapolate */ true), - GET_RACE_MAP_DATA_CATEGORY, new MarkedAsyncCallback<>(new AsyncCallback() { + asyncActionsExecutor.execute(new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, toTimesForNonOverlappingTailsCall, + /* extrapolate */ true), GET_RACE_MAP_DATA_CATEGORY, + new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable t) { - errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), - true /* silentMode */); + errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), true /*silentMode */); } - + @Override public void onSuccess(CompactBoatPositionsDTO result) { - // Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to - // updateBoatPositions for those - // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will - // contain true for - // all its entries so that the other response received for GetRaceMapDataAction will - // consider this an + // Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to updateBoatPositions for those + // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for + // all its entries so that the other response received for GetRaceMapDataAction will consider this an // overlap if it happens after this update. - updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), - competitorsToShow, result.getBoatPositionsForCompetitors(competitorsByIdAsString), - /* updateTailsOnly */ true); + updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, + result.getBoatPositionsForCompetitors(competitorsByIdAsString), /* updateTailsOnly */ true); } })); } } - private AsyncCallback getRaceMapDataCallback(final Date newTime, final long transitionTimeInMillis, + private AsyncCallback getRaceMapDataCallback( + final Date newTime, + final long transitionTimeInMillis, final Map hasTailOverlapForCompetitor, final Iterable competitorsToShow, final int requestID) { return new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining racemap data: " + caught.getMessage(), - true /* silentMode */); + errorReporter.reportError("Error obtaining racemap data: " + caught.getMessage(), true /*silentMode */); } - + @Override public void onSuccess(RaceMapDataDTO raceMapDataDTO) { if (map != null && raceMapDataDTO != null) { @@ -1104,32 +1043,27 @@ public class RaceMap extends AbstractCompositeComponent startedProcessingRequestID = requestID; if (raceMapDataDTO.raceCompetitorIdsAsStrings != null) { try { - raceCompetitorSet - .setIdsAsStringsOfCompetitorsInRace(raceMapDataDTO.raceCompetitorIdsAsStrings); + raceCompetitorSet.setIdsAsStringsOfCompetitorsInRace(raceMapDataDTO.raceCompetitorIdsAsStrings); } catch (Exception e) { - GWT.log("Error trying to update competitor set for race " + raceIdentifier.getRaceName() - + " in regatta " + raceIdentifier.getRegattaName(), e); + GWT.log("Error trying to update competitor set for race "+raceIdentifier.getRaceName()+ + " in regatta "+raceIdentifier.getRegattaName(), e); } } quickRanksDTOProvider.quickRanksReceivedFromServer(raceMapDataDTO.quickRanks); if (isSimulationEnabled && settings.isShowSimulationOverlay()) { lastLegNumber = raceMapDataDTO.coursePositions.currentLegNumber; - simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, - raceMapDataDTO.simulationResultVersion); + simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion); } // Do boat specific actions Map> boatData = raceMapDataDTO.boatPositions; - updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, - competitorsToShow, boatData, /* updateTailsOnly */ false); + updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, competitorsToShow, boatData, /* updateTailsOnly */ false); if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); } if (maneuverMarkers != null) { removeAllManeuverMarkers(); } - if (maneuverLossLines != null) { - removeManeuverLossLinesAndInfoOverlays(); - } + if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } @@ -1138,30 +1072,29 @@ public class RaceMap extends AbstractCompositeComponent showCourseSidelinesOnMap(raceMapDataDTO.courseSidelines); showStartAndFinishAndCourseMiddleLines(raceMapDataDTO.coursePositions); showStartLineToFirstMarkTriangle(raceMapDataDTO.coursePositions); - + // Rezoom the map LatLngBounds zoomToBounds = null; - if (!settings.getZoomSettings().containsZoomType(ZoomTypes.NONE)) { // Auto zoom if setting is - // not manual + if (!settings.getZoomSettings().containsZoomType(ZoomTypes.NONE)) { // Auto zoom if setting is not manual zoomToBounds = settings.getZoomSettings().getNewBounds(RaceMap.this); if (zoomToBounds == null && !mapFirstZoomDone) { - zoomToBounds = getDefaultZoomBounds(); // the user-specified zoom couldn't find what it - // was looking for; try defaults once + zoomToBounds = getDefaultZoomBounds(); // the user-specified zoom couldn't find what it was looking for; try defaults once } } else if (!mapFirstZoomDone) { // Zoom once to the marks if marks exist zoomToBounds = new CourseMarksBoundsCalculator().calculateNewBounds(RaceMap.this); if (zoomToBounds == null) { - zoomToBounds = getDefaultZoomBounds(); // use default zoom, e.g., + zoomToBounds = getDefaultZoomBounds(); // use default zoom, e.g., } /* - * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that the map was - * just zoomed to the bounds of the marks, it was not a zoom or pan triggered by the user. - * As a consequence the mapZoomedOrPannedSinceLastRaceSelection option has to reset again. + * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that + * the map was just zoomed to the bounds of the marks, it was not a zoom or pan + * triggered by the user. As a consequence the + * mapZoomedOrPannedSinceLastRaceSelection option has to reset again. */ } zoomMapToNewBounds(zoomToBounds); mapFirstZoomDone = true; - + updateEstimatedDuration(raceMapDataDTO.estimatedDuration); } } else { @@ -1178,13 +1111,13 @@ public class RaceMap extends AbstractCompositeComponent if (estimatedDurationOverlay == null) { estimatedDurationOverlay = new Label(""); estimatedDurationOverlay.setStyleName(raceMapStyle.estimatedDuration()); - if (showHeaderPanel) { + if(showHeaderPanel) { estimatedDurationOverlay.addStyleName(raceMapStyle.estimatedDurationWithHeader()); } map.setControls(ControlPosition.TOP_CENTER, estimatedDurationOverlay); } - estimatedDurationOverlay.setText(stringMessages.estimatedDuration() + " " - + DateAndTimeFormatterUtil.formatElapsedTime(estimatedDuration.asMillis())); + estimatedDurationOverlay.setText(stringMessages.estimatedDuration() + + " " + DateAndTimeFormatterUtil.formatElapsedTime(estimatedDuration.asMillis())); } @@ -1192,18 +1125,17 @@ public class RaceMap extends AbstractCompositeComponent * @param hasTailOverlapForCompetitor * if for a competitor whose fixes are provided in fixesForCompetitors this holds * false, any fixes previously stored for that competitor are removed, and the tail is - * deleted from the map (see {@link #removeTail(CompetitorWithBoatDTO)}); the new fixes are then added to - * the {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). - * If this map holds true, {@link #mergeFixes(CompetitorWithBoatDTO, List, long)} is used to - * merge the new fixes from fixesForCompetitors into the {@link #fixes} collection, and the - * tail is left unchanged. NOTE: When a non-overlapping set of fixes is updated - * (false), this map's record for the competitor is UPDATED to true - * after the tail deletion and {@link #fixes} replacement has taken place. This helps in cases where this - * update is only one of two into which an original request was split (one quick update of the tail's - * head and another one for the longer tail itself), such that the second request that uses the - * same map will be considered having an overlap now, not leading to a replacement of the - * previous update originating from the same request. See also - * {@link FixesAndTails#updateFixes(Map, Map, TailFactory, long)}. + * deleted from the map (see {@link #removeTail(CompetitorWithBoatDTO)}); the new fixes are then added to the + * {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). If + * this map holds true, {@link #mergeFixes(CompetitorWithBoatDTO, List, long)} is used to merge the + * new fixes from fixesForCompetitors into the {@link #fixes} collection, and the tail is + * left unchanged. NOTE: When a non-overlapping set of fixes is updated (false), this + * map's record for the competitor is UPDATED to true after the tail deletion and + * {@link #fixes} replacement has taken place. This helps in cases where this update is only one of two + * into which an original request was split (one quick update of the tail's head and another one for the + * longer tail itself), such that the second request that uses the same map will be considered + * having an overlap now, not leading to a replacement of the previous update originating from the same + * request. See also {@link FixesAndTails#updateFixes(Map, Map, TailFactory, long)}. * @param updateTailsOnly * if true, only the tails are updated according to boatData and * hasTailOverlapForCompetitor, but the advantage line is not updated, and neither are the @@ -1214,15 +1146,13 @@ public class RaceMap extends AbstractCompositeComponent */ private void updateBoatPositions(final Date newTime, final long transitionTimeInMillis, final Map hasTailOverlapForCompetitor, - final Iterable competitorsToShow, - Map> boatData, boolean updateTailsOnly) { + final Iterable competitorsToShow, Map> boatData, boolean updateTailsOnly) { fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis); showBoatsOnMap(newTime, transitionTimeInMillis, /* re-calculate; it could have changed since the asynchronous request was made: */ getCompetitorsToShow(), updateTailsOnly); if (!updateTailsOnly) { - showCompetitorInfoOnMap(newTime, transitionTimeInMillis, - competitorSelection.getSelectedFilteredCompetitors()); + showCompetitorInfoOnMap(newTime, transitionTimeInMillis, competitorSelection.getSelectedFilteredCompetitors()); // even though the wind data is retrieved by a separate call, re-draw the advantage line because it needs to // adjust to new boat positions showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); @@ -1230,13 +1160,13 @@ public class RaceMap extends AbstractCompositeComponent } private void showCourseSidelinesOnMap(List sidelinesDTOs) { - if (map != null && sidelinesDTOs != null) { + if (map != null && sidelinesDTOs != null ) { Map toRemoveSidelines = new HashMap(courseSidelines); for (SidelineDTO sidelineDTO : sidelinesDTOs) { if (sidelineDTO.getMarks().size() == 2) { // right now we only support sidelines with 2 marks Polygon sideline = courseSidelines.get(sidelineDTO); LatLng[] sidelinePoints = new LatLng[sidelineDTO.getMarks().size()]; - int i = 0; + int i=0; for (MarkDTO sidelineMark : sidelineDTO.getMarks()) { sidelinePoints[i] = coordinateSystem.toLatLng(sidelineMark.position); i++; @@ -1249,7 +1179,7 @@ public class RaceMap extends AbstractCompositeComponent options.setStrokeOpacity(1.0); options.setFillColor(null); options.setFillOpacity(1.0); - + sideline = Polygon.newInstance(options); MVCArray pointsAsArray = MVCArray.newInstance(sidelinePoints); sideline.setPath(pointsAsArray); @@ -1283,29 +1213,25 @@ public class RaceMap extends AbstractCompositeComponent } } } - + private void showCourseMarksOnMap(CoursePositionsDTO courseDTO, long transitionTimeInMillis) { if (map != null && courseDTO != null) { WaypointDTO endWaypointForCurrentLegNumber = null; - if (courseDTO.currentLegNumber > 0 && courseDTO.currentLegNumber <= courseDTO.totalLegsCount) { + if(courseDTO.currentLegNumber > 0 && courseDTO.currentLegNumber <= courseDTO.totalLegsCount) { endWaypointForCurrentLegNumber = courseDTO.getEndWaypointForLegNumber(courseDTO.currentLegNumber); } - - Map toRemoveCourseMarks = new HashMap( - courseMarkOverlays); + + Map toRemoveCourseMarks = new HashMap(courseMarkOverlays); if (courseDTO.marks != null) { for (MarkDTO markDTO : courseDTO.marks) { boolean isSelected = false; - if (endWaypointForCurrentLegNumber != null - && Util.contains(endWaypointForCurrentLegNumber.controlPoint.getMarks(), markDTO)) { + if (endWaypointForCurrentLegNumber != null && Util.contains(endWaypointForCurrentLegNumber.controlPoint.getMarks(), markDTO)) { isSelected = true; } CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTO.getIdAsString()); if (courseMarkOverlay == null) { - courseMarkOverlay = new CourseMarkOverlay(map, RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, - markDTO, coordinateSystem, courseDTO); - courseMarkOverlay - .setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); + courseMarkOverlay = new CourseMarkOverlay(map, RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, markDTO, coordinateSystem, courseDTO); + courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); courseMarkOverlay.setBuoyZoneRadius(settings.getBuoyZoneRadius()); courseMarkOverlay.setSelected(isSelected); courseMarkOverlays.put(markDTO.getIdAsString(), courseMarkOverlay); @@ -1314,8 +1240,7 @@ public class RaceMap extends AbstractCompositeComponent courseMarkOverlay.addToMap(); } else { courseMarkOverlay.setMarkPosition(markDTO.position, transitionTimeInMillis); - courseMarkOverlay - .setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); + courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); courseMarkOverlay.setBuoyZoneRadius(settings.getBuoyZoneRadius()); courseMarkOverlay.setSelected(isSelected); courseMarkOverlay.setCourse(courseDTO); @@ -1332,10 +1257,10 @@ public class RaceMap extends AbstractCompositeComponent } } } - + /** - * Based on the mark positions in {@link #courseMarkOverlays}' values this method determines the center of gravity - * of these marks' {@link CourseMarkOverlay#getPosition() positions}. + * Based on the mark positions in {@link #courseMarkOverlays}' values this method determines the center of gravity of these marks' + * {@link CourseMarkOverlay#getPosition() positions}. */ private Position getCenterOfCourse() { ScalablePosition center = null; @@ -1359,18 +1284,16 @@ public class RaceMap extends AbstractCompositeComponent } } - private void showWindSensorsOnMap( - List> windSensorsList) { + private void showWindSensorsOnMap(List> windSensorsList) { if (map != null) { Set toRemoveWindSources = new HashSet(windSensorOverlays.keySet()); for (com.sap.sse.common.Util.Pair windSourcePair : windSensorsList) { - WindSource windSource = windSourcePair.getA(); + WindSource windSource = windSourcePair.getA(); WindTrackInfoDTO windTrackInfoDTO = windSourcePair.getB(); WindSensorOverlay windSensorOverlay = windSensorOverlays.get(windSource); if (windSensorOverlay == null) { - windSensorOverlay = createWindSensorOverlay(RaceMapOverlaysZIndexes.WINDSENSOR_ZINDEX, windSource, - windTrackInfoDTO); + windSensorOverlay = createWindSensorOverlay(RaceMapOverlaysZIndexes.WINDSENSOR_ZINDEX, windSource, windTrackInfoDTO); windSensorOverlays.put(windSource, windSensorOverlay); windSensorOverlay.addToMap(); } else { @@ -1388,13 +1311,11 @@ public class RaceMap extends AbstractCompositeComponent } } - private void showCompetitorInfoOnMap(final Date newTime, final long timeForPositionTransitionMillis, - final Iterable competitorsToShow) { + private void showCompetitorInfoOnMap(final Date newTime, final long timeForPositionTransitionMillis, final Iterable competitorsToShow) { if (map != null) { if (settings.isShowSelectedCompetitorsInfo()) { Set toRemoveCompetorInfoOverlaysIdsAsStrings = new HashSet<>(); - Util.addAll(competitorInfoOverlays.getCompetitorIdsAsStrings(), - toRemoveCompetorInfoOverlaysIdsAsStrings); + Util.addAll(competitorInfoOverlays.getCompetitorIdsAsStrings(), toRemoveCompetorInfoOverlaysIdsAsStrings); for (CompetitorDTO competitorDTO : competitorsToShow) { if (fixesAndTails.hasFixesFor(competitorDTO)) { GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = getBoatFix(competitorDTO, newTime); @@ -1402,13 +1323,11 @@ public class RaceMap extends AbstractCompositeComponent CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(competitorDTO); final Integer rank = getRank(competitorDTO); if (competitorInfoOverlay == null) { - competitorInfoOverlay = competitorInfoOverlays.createCompetitorInfoOverlay( - RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO, lastBoatFix, rank, - timeForPositionTransitionMillis); + competitorInfoOverlay = competitorInfoOverlays.createCompetitorInfoOverlay(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO, + lastBoatFix, rank, timeForPositionTransitionMillis); competitorInfoOverlay.addToMap(); } else { - competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix, - timeForPositionTransitionMillis); + competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix, timeForPositionTransitionMillis); } toRemoveCompetorInfoOverlaysIdsAsStrings.remove(competitorDTO.getIdAsString()); } @@ -1454,7 +1373,7 @@ public class RaceMap extends AbstractCompositeComponent } return timeForPositionTransitionMillisSmoothed; } - + /** * @param updateTailsOnly * if false, tails of competitors not in competitorsToShow are removed from the @@ -1480,17 +1399,14 @@ public class RaceMap extends AbstractCompositeComponent if (!fixesAndTails.hasTail(competitorDTO)) { fixesAndTails.createTailAndUpdateIndices(competitorDTO, tailsFromTime, tailsToTime, this); } else { - fixesAndTails.updateTail(competitorDTO, tailsFromTime, tailsToTime, - (int) (timeForPositionTransitionMillis == -1 ? -1 - : timeForPositionTransitionMillis / 2)); + fixesAndTails.updateTail(competitorDTO, tailsFromTime, tailsToTime, (int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2)); if (!updateTailsOnly) { competitorDTOsOfUnusedTails.remove(competitorDTO); } PolylineOptions newOptions = createTailStyle(competitorDTO, displayHighlighted(competitorDTO)); fixesAndTails.getTail(competitorDTO).setOptions(newOptions); } - boolean usedExistingBoatCanvas = updateBoatCanvasForCompetitor(competitorDTO, newTime, - timeForPositionTransitionMillis); + boolean usedExistingBoatCanvas = updateBoatCanvasForCompetitor(competitorDTO, newTime, timeForPositionTransitionMillis); if (usedExistingBoatCanvas && !updateTailsOnly) { competitorDTOsOfUnusedBoatCanvases.remove(competitorDTO); } @@ -1515,34 +1431,32 @@ public class RaceMap extends AbstractCompositeComponent * necessary if greater distances are allowed. */ public LatLng calculatePositionAlongRhumbline(LatLng position, double bearingDeg, Distance distance) { - double distanceRad = distance.getCentralAngleRad(); + double distanceRad = distance.getCentralAngleRad(); double lat1 = position.getLatitude() / 180. * Math.PI; double lon1 = position.getLongitude() / 180. * Math.PI; double bearingRad = bearingDeg / 180. * Math.PI; - double lat2 = Math.asin( - Math.sin(lat1) * Math.cos(distanceRad) + Math.cos(lat1) * Math.sin(distanceRad) * Math.cos(bearingRad)); - double lon2 = lon1 + Math.atan2(Math.sin(bearingRad) * Math.sin(distanceRad) * Math.cos(lat1), - Math.cos(distanceRad) - Math.sin(lat1) * Math.sin(lat2)); - lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180..+180� + double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distanceRad) + + Math.cos(lat1) * Math.sin(distanceRad) * Math.cos(bearingRad)); + double lon2 = lon1 + Math.atan2(Math.sin(bearingRad)*Math.sin(distanceRad)*Math.cos(lat1), + Math.cos(distanceRad)-Math.sin(lat1)*Math.sin(lat2)); + lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalize to -180..+180� // position is already in LatLng space, so no mapping through coordinateSystem is required here - return LatLng.newInstance(lat2 / Math.PI * 180., lon2 / Math.PI * 180.); + return LatLng.newInstance(lat2 / Math.PI * 180., lon2 / Math.PI * 180.); } - + /** - * Returns a pair whose first component is the leg number (one-based) of the competitor returned as the second - * component. The competitor returned currently has the best ranking in the quick ranks provided by the - * {@link #quickRanksDTOProvider}. + * Returns a pair whose first component is the leg number (one-based) of the competitor returned as the second component. + * The competitor returned currently has the best ranking in the quick ranks provided by the {@link #quickRanksDTOProvider}. */ private com.sap.sse.common.Util.Pair getBestVisibleCompetitorWithOneBasedLegNumber( Iterable competitorsToShow) { CompetitorDTO leadingCompetitorDTO = null; int legOfLeaderCompetitor = -1; int bestOneBasedRank = Integer.MAX_VALUE; - for (QuickRankDTO competitorFromBestToWorstAndOneBasedLegNumber : quickRanksDTOProvider.getQuickRanks() - .values()) { - if (Util.contains(competitorsToShow, competitorFromBestToWorstAndOneBasedLegNumber.competitor) - && competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased != 0 - && competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank < bestOneBasedRank) { + for (QuickRankDTO competitorFromBestToWorstAndOneBasedLegNumber : quickRanksDTOProvider.getQuickRanks().values()) { + if (Util.contains(competitorsToShow, competitorFromBestToWorstAndOneBasedLegNumber.competitor) && + competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased != 0 && + competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank < bestOneBasedRank) { leadingCompetitorDTO = competitorFromBestToWorstAndOneBasedLegNumber.competitor; legOfLeaderCompetitor = competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased; bestOneBasedRank = competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank; @@ -1551,22 +1465,18 @@ public class RaceMap extends AbstractCompositeComponent } } } - return leadingCompetitorDTO == null ? null - : new com.sap.sse.common.Util.Pair(legOfLeaderCompetitor, leadingCompetitorDTO); + return leadingCompetitorDTO == null ? null : + new com.sap.sse.common.Util.Pair(legOfLeaderCompetitor, leadingCompetitorDTO); } - final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with - // the visible area of the map; bug 616 - - private void showAdvantageLine(Iterable competitorsToShow, Date date, - long timeForPositionTransitionMillis) { + final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with the visible area of the map; bug 616 + private void showAdvantageLine(Iterable competitorsToShow, Date date, long timeForPositionTransitionMillis) { if (map != null && lastRaceTimesInfo != null && !quickRanksDTOProvider.getQuickRanks().isEmpty() && lastCombinedWindTrackInfoDTO != null) { boolean drawAdvantageLine = false; if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) { // find competitor with highest rank - com.sap.sse.common.Util.Pair visibleLeaderInfo = getBestVisibleCompetitorWithOneBasedLegNumber( - competitorsToShow); + com.sap.sse.common.Util.Pair visibleLeaderInfo = getBestVisibleCompetitorWithOneBasedLegNumber(competitorsToShow); // the boat fix may be null; may mean that no positions were loaded yet for the leading visible boat; // don't show anything GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = null; @@ -1574,7 +1484,8 @@ public class RaceMap extends AbstractCompositeComponent boolean isLegTypeKnown = false; WindTrackInfoDTO windDataForLegMiddle = null; LegInfoDTO legInfoDTO = null; - if (visibleLeaderInfo != null && visibleLeaderInfo.getA() > 0 + if (visibleLeaderInfo != null + && visibleLeaderInfo.getA() > 0 && visibleLeaderInfo.getA() <= lastRaceTimesInfo.getLegInfos().size() // get wind at middle of leg for leading visible competitor && (windDataForLegMiddle = lastCombinedWindTrackInfoDTO @@ -1587,8 +1498,7 @@ public class RaceMap extends AbstractCompositeComponent } lastBoatFix = getBoatFix(visibleLeaderInfo.getB(), date); } - if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null - && lastBoatFix.speedWithBearing != null) { + if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null && lastBoatFix.speedWithBearing != null) { BoatDTO boat = competitorSelection.getBoat(visibleLeaderInfo.getB()); Distance distanceFromBoatPositionInKm = boat.getBoatClass().getHullLength(); // one hull length // implement and use Position.translateRhumb() @@ -1614,7 +1524,7 @@ public class RaceMap extends AbstractCompositeComponent if (rotatedBearingDeg2 < 0.0) { rotatedBearingDeg2 += 360.0; } - break; + break; } case REACHING: { rotatedBearingDeg1 = legInfoDTO.legBearingInDegrees + 90.0; @@ -1625,7 +1535,7 @@ public class RaceMap extends AbstractCompositeComponent if (rotatedBearingDeg2 < 0.0) { rotatedBearingDeg2 += 360.0; } - break; + break; } } MVCArray nextPath = MVCArray.newInstance(); @@ -1640,7 +1550,7 @@ public class RaceMap extends AbstractCompositeComponent options.setStrokeColor(ADVANTAGE_LINE_COLOR); options.setStrokeWeight(1); options.setStrokeOpacity(0.5); - + advantageLine = Polyline.newInstance(options); advantageTimer = new AdvantageLineAnimator(advantageLine); MVCArray pointsAsArray = MVCArray.newInstance(); @@ -1649,7 +1559,7 @@ public class RaceMap extends AbstractCompositeComponent advantageLine.setPath(pointsAsArray); advantageLine.setMap(map); Hoverline advantageHoverline = new Hoverline(advantageLine, options, this); - + advantageLineMouseOverHandler = new AdvantageLineMouseOverMapHandler( bearingOfCombinedWindInDeg, new Date(windFix.measureTimepoint)); advantageLine.addMouseOverHandler(advantageLineMouseOverHandler); @@ -1662,8 +1572,7 @@ public class RaceMap extends AbstractCompositeComponent } else { nextPath.push(advantageLinePos1); nextPath.push(advantageLinePos2); - advantageTimer.setNextPositionAndTransitionMillis(nextPath, - timeForPositionTransitionMillis); + advantageTimer.setNextPositionAndTransitionMillis(nextPath, timeForPositionTransitionMillis); if (advantageLineMouseOverHandler != null) { advantageLineMouseOverHandler.setTrueWindBearing(bearingOfCombinedWindInDeg); advantageLineMouseOverHandler.setDate(new Date(windFix.measureTimepoint)); @@ -1683,11 +1592,11 @@ public class RaceMap extends AbstractCompositeComponent } } } - + private final StringBuilder windwardStartLineMarkToFirstMarkLineText = new StringBuilder(); private final StringBuilder leewardStartLineMarkToFirstMarkLineText = new StringBuilder(); - - private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO) { + + private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO){ final List startMarkPositions = courseDTO.getStartMarkPositions(); if (startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1) { final Position windwardStartLinePosition = startMarkPositions.get(0); @@ -1695,10 +1604,12 @@ public class RaceMap extends AbstractCompositeComponent final Position firstMarkPosition = courseDTO.waypointPositions.get(1); windwardStartLineMarkToFirstMarkLineText.replace(0, windwardStartLineMarkToFirstMarkLineText.length(), stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(windwardStartLinePosition.getDistance(firstMarkPosition).getMeters()))); + .format(windwardStartLinePosition.getDistance(firstMarkPosition) + .getMeters()))); leewardStartLineMarkToFirstMarkLineText.replace(0, leewardStartLineMarkToFirstMarkLineText.length(), stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(leewardStartLinePosition.getDistance(firstMarkPosition).getMeters()))); + .format(leewardStartLinePosition.getDistance(firstMarkPosition) + .getMeters()))); final LineInfoProvider windwardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { @@ -1711,20 +1622,16 @@ public class RaceMap extends AbstractCompositeComponent return leewardStartLineMarkToFirstMarkLineText.toString(); } }; - windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* - * showLine - */ + windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* showLine */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider, - "grey", 1); - leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* - * showLine - */ + "grey"); + leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider, - "grey", 1); + "grey"); } } @@ -1733,135 +1640,112 @@ public class RaceMap extends AbstractCompositeComponent final LineInfoProvider startLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.startLine() + startLineAdvantageText; + return stringMessages.startLine()+startLineAdvantageText; } }; final LineInfoProvider finishLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.finishLine() + finishLineAdvantageText; + return stringMessages.finishLine()+finishLineAdvantageText; } }; private void showStartAndFinishAndCourseMiddleLines(final CoursePositionsDTO courseDTO) { - if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null - && !courseDTO.course.waypoints.isEmpty()) { + if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null && + !courseDTO.course.waypoints.isEmpty()) { // draw the start line final WaypointDTO startWaypoint = courseDTO.course.waypoints.get(0); updateCountdownCanvas(startWaypoint); - final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 - : courseDTO.getStartMarkPositions().size(); - final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 - : courseDTO.getFinishMarkPositions().size(); - final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null - : courseDTO.getStartMarkPositions().get(0); - final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null - : courseDTO.getStartMarkPositions().get(1); + final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 : courseDTO.getStartMarkPositions().size(); + final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 : courseDTO.getFinishMarkPositions().size(); + final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null : courseDTO.getStartMarkPositions().get(0); + final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null : courseDTO.getStartMarkPositions().get(1); if (courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { - startLineAdvantageText.replace(0, startLineAdvantageText.length(), - " " + stringMessages.lineAngleToWindAndAdvantage( - numberFormatOneDecimal.format(courseDTO.startLineLengthInMeters), - numberFormatOneDecimal.format(Math.abs( - courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), - courseDTO.startLineAdvantageousSide.name().charAt(0) - + courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), - numberFormatOneDecimal.format(courseDTO.startLineAdvantageInMeters))); + startLineAdvantageText.replace(0, startLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( + numberFormatOneDecimal.format(courseDTO.startLineLengthInMeters), + numberFormatOneDecimal.format(Math.abs(courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), + courseDTO.startLineAdvantageousSide.name().charAt(0)+courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), + numberFormatOneDecimal.format(courseDTO.startLineAdvantageInMeters))); } else { startLineAdvantageText.delete(0, startLineAdvantageText.length()); } - final boolean showStartLineBasedOnCurrentLeg = numberOfStartWaypointMarks == 2 - && courseDTO.currentLegNumber <= 1; - final boolean showFinishLineBasedOnCurrentLeg = numberOfFinishWaypointMarks == 2 - && courseDTO.currentLegNumber == courseDTO.totalLegsCount; + final boolean showStartLineBasedOnCurrentLeg = numberOfStartWaypointMarks == 2 && courseDTO.currentLegNumber <= 1; + final boolean showFinishLineBasedOnCurrentLeg = numberOfFinishWaypointMarks == 2 && courseDTO.currentLegNumber == courseDTO.totalLegsCount; // show the line when STARTLINE is selected and the current leg is around the start leg, - // or when COURSEGEOMETRY is selected and the finish line isn't equal and wouldn't be shown at the same time - // based on the current leg. - // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will not be - // displayed if + // or when COURSEGEOMETRY is selected and the finish line isn't equal and wouldn't be shown at the same time based on the current leg. + // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will not be displayed if // based on the race progress the finish line is to be preferred, so only the finish line will be shown. - final boolean reallyShowStartLine = (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) - && showStartLineBasedOnCurrentLeg) - || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) - && (!showFinishLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO))); + final boolean reallyShowStartLine = + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) && showStartLineBasedOnCurrentLeg) || + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && + (!showFinishLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO))); // show the line when FINISHLINE is selected and the current leg is the last leg, // or when COURSEGEOMETRY is selected and the start line isn't equal or the current leg is the last leg. - // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will be - // displayed unless + // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will be displayed unless // the finish line should take precedence based on race progress. - final boolean reallyShowFinishLine = showFinishLineBasedOnCurrentLeg - && (!showStartLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO)) - && (settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) - && showFinishLineBasedOnCurrentLeg) - || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) - && (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); - startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, startLineLeftPosition, - startLineRightPosition, startLineInfoProvider, "#ffffff", 1); + final boolean reallyShowFinishLine = showFinishLineBasedOnCurrentLeg && + (!showStartLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO)) && + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) && showFinishLineBasedOnCurrentLeg) || + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && + (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); + startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, + startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff"); // draw the finish line - final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null - : courseDTO.getFinishMarkPositions().get(0); - final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null - : courseDTO.getFinishMarkPositions().get(1); + final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0); + final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null : courseDTO.getFinishMarkPositions().get(1); if (courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { - finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), - " " + stringMessages.lineAngleToWindAndAdvantage( - numberFormatOneDecimal.format(courseDTO.finishLineLengthInMeters), - numberFormatOneDecimal.format(Math.abs( - courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), - courseDTO.finishLineAdvantageousSide.name().charAt(0) - + courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), - numberFormatOneDecimal.format(courseDTO.finishLineAdvantageInMeters))); + finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( + numberFormatOneDecimal.format(courseDTO.finishLineLengthInMeters), + numberFormatOneDecimal.format(Math.abs(courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), + courseDTO.finishLineAdvantageousSide.name().charAt(0)+courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), + numberFormatOneDecimal.format(courseDTO.finishLineAdvantageInMeters))); } else { finishLineAdvantageText.delete(0, finishLineAdvantageText.length()); } - finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, finishLineLeftPosition, - finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR, 1); + finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, + finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR); // the control point pairs for which we already decided whether or not // to show a course middle line for; values tell whether to show the line and for which zero-based // start waypoint index to do so; when for an equal control point pair multiple decisions with different // outcome are made, a decision to show the line overrules the decision to not show it (OR-semantics) final Map, Pair> keysAlreadyHandled = new HashMap<>(); - for (int zeroBasedIndexOfStartWaypoint = 0; zeroBasedIndexOfStartWaypoint < courseDTO.waypointPositions - .size() - 1; zeroBasedIndexOfStartWaypoint++) { + for (int zeroBasedIndexOfStartWaypoint = 0; zeroBasedIndexOfStartWaypoint key = getCourseMiddleLinesKey(courseDTO, zeroBasedIndexOfStartWaypoint); - boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() - || settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) - || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE) - && courseDTO.currentLegNumber > 0 - && courseDTO.currentLegNumber - 1 == zeroBasedIndexOfStartWaypoint); + boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() || + settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) || + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE) + && courseDTO.currentLegNumber > 0 + && courseDTO.currentLegNumber-1 == zeroBasedIndexOfStartWaypoint); keysAlreadyHandled.put(key, new Pair<>(showCourseMiddleLine, zeroBasedIndexOfStartWaypoint)); } Set> keysToConsider = new HashSet<>(keysAlreadyHandled.keySet()); keysToConsider.addAll(courseMiddleLines.keySet()); for (final Set key : keysToConsider) { - final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) - ? keysAlreadyHandled.get(key).getB() - : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter + final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) ? + keysAlreadyHandled.get(key).getB() : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter final Pair showLineAndZeroBasedIndexOfStartWaypoint = keysAlreadyHandled.get(key); - final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null - && showLineAndZeroBasedIndexOfStartWaypoint.getA(); - courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), - zeroBasedIndexOfStartWaypoint, showCourseMiddleLine)); + final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null && showLineAndZeroBasedIndexOfStartWaypoint.getA(); + courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), zeroBasedIndexOfStartWaypoint, showCourseMiddleLine)); } } } private boolean startLineEqualsFinishLine(CoursePositionsDTO courseDTO) { final List waypoints; - return courseDTO != null && courseDTO.course != null && (waypoints = courseDTO.course.waypoints) != null - && waypoints.get(0).controlPoint.equals(waypoints.get(waypoints.size() - 1).controlPoint); + return courseDTO != null && courseDTO.course != null && + (waypoints = courseDTO.course.waypoints) != null && + waypoints.get(0).controlPoint.equals(waypoints.get(waypoints.size()-1).controlPoint); } /** - * Given a zero-based index into courseDTO's {@link RaceCourseDTO#waypoints waypoints list} that - * denotes the start waypoint of the leg in question, returns a key that can be used for the - * {@link #courseMiddleLines} map, consisting of a set that holds the two {@link ControlPointDTO}s representing the - * start and finish control point of that leg. + * Given a zero-based index into courseDTO's {@link RaceCourseDTO#waypoints waypoints list} that denotes the start + * waypoint of the leg in question, returns a key that can be used for the {@link #courseMiddleLines} map, consisting of a set + * that holds the two {@link ControlPointDTO}s representing the start and finish control point of that leg. */ private Set getCourseMiddleLinesKey(final CoursePositionsDTO courseDTO, final int zeroBasedIndexOfStartWaypoint) { ControlPointDTO startControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint).controlPoint; - ControlPointDTO endControlPoint = courseDTO.course.waypoints - .get(zeroBasedIndexOfStartWaypoint + 1).controlPoint; + ControlPointDTO endControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint+1).controlPoint; final Set key = new HashSet<>(); key.add(startControlPoint); key.add(endControlPoint); @@ -1877,28 +1761,26 @@ public class RaceMap extends AbstractCompositeComponent * @return null if the line is not shown; the polyline object representing the line being displayed * otherwise */ - private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, - Polyline lineToShowOrRemoveOrUpdate, final int zeroBasedIndexOfStartWaypoint, final boolean showLine) { + private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, Polyline lineToShowOrRemoveOrUpdate, + final int zeroBasedIndexOfStartWaypoint, final boolean showLine) { final Position position1DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint); - final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint + 1); + final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint+1); final LineInfoProvider lineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { final StringBuilder sb = new StringBuilder(); sb.append(stringMessages.courseMiddleLine()); sb.append('\n'); - sb.append(numberFormatNoDecimal.format(Math.abs(position1DTO.getDistance(position2DTO).getMeters())) - + stringMessages.metersUnit()); + sb.append(numberFormatNoDecimal.format( + Math.abs(position1DTO.getDistance(position2DTO).getMeters()))+stringMessages.metersUnit()); if (lastCombinedWindTrackInfoDTO != null) { - final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO - .getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint); - if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null - && !windTrackAtLegMiddle.windFixes.isEmpty()) { + final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO.getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint); + if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null && !windTrackAtLegMiddle.windFixes.isEmpty()) { WindDTO windAtLegMiddle = windTrackAtLegMiddle.windFixes.get(0); final double legBearingDeg = position1DTO.getBearingGreatCircle(position2DTO).getDegrees(); - final String diff = numberFormatOneDecimal - .format(Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg - legBearingDeg), - Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg - legBearingDeg))); + final String diff = numberFormatOneDecimal.format( + Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg-legBearingDeg), + Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg-legBearingDeg))); sb.append(", "); sb.append(stringMessages.degreesToWind(diff)); } @@ -1906,14 +1788,13 @@ public class RaceMap extends AbstractCompositeComponent return sb.toString(); } }; - return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, - lineInfoProvider, "#2268a0", 1); + return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, lineInfoProvider, "#2268a0"); } private interface LineInfoProvider { String getLineInfo(); } - + /** * @param showLine * tells whether or not to show the line; if the lineToShowOrRemoveOrUpdate references a @@ -1924,8 +1805,7 @@ public class RaceMap extends AbstractCompositeComponent * otherwise */ private Polyline showOrRemoveOrUpdateLine(Polyline lineToShowOrRemoveOrUpdate, final boolean showLine, - final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, - String lineColorRGB, int strokeWeight) { + final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, String lineColorRGB) { if (position1DTO != null && position2DTO != null) { if (showLine) { LatLng courseMiddleLinePoint1 = coordinateSystem.toLatLng(position1DTO); @@ -1936,14 +1816,13 @@ public class RaceMap extends AbstractCompositeComponent options.setClickable(true); options.setGeodesic(true); options.setStrokeColor(lineColorRGB); - options.setStrokeWeight(strokeWeight); + options.setStrokeWeight(1); options.setStrokeOpacity(1.0); pointsAsArray = MVCArray.newInstance(); lineToShowOrRemoveOrUpdate = Polyline.newInstance(options); lineToShowOrRemoveOrUpdate.setPath(pointsAsArray); lineToShowOrRemoveOrUpdate.setMap(map); - Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, - this); + Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, this); lineToShowOrRemoveOrUpdate.addMouseOverHandler(new MouseOverMapHandler() { @Override public void onEvent(MouseOverMapEvent event) { @@ -1961,8 +1840,7 @@ public class RaceMap extends AbstractCompositeComponent pointsAsArray.removeAt(1); pointsAsArray.removeAt(0); } - adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, - lineInfoProvider); + adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, lineInfoProvider); pointsAsArray.insertAt(0, courseMiddleLinePoint1); pointsAsArray.insertAt(1, courseMiddleLinePoint2); } else { @@ -1977,8 +1855,7 @@ public class RaceMap extends AbstractCompositeComponent } private void adjustInfoOverlayForRemovedLine(Polyline lineToShowOrRemoveOrUpdate) { - SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry - .remove(lineToShowOrRemoveOrUpdate); + SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); if (infoOverlay != null) { infoOverlay.removeFromMap(); } @@ -1989,10 +1866,9 @@ public class RaceMap extends AbstractCompositeComponent SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.get(lineToShowOrRemoveOrUpdate); if (getSettings().getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) { if (infoOverlay == null) { - infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, - lineInfoProvider.getLineInfo(), coordinateSystem); + infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, lineInfoProvider.getLineInfo(), coordinateSystem); infoOverlaysForLinesForCourseGeometry.put(lineToShowOrRemoveOrUpdate, infoOverlay); - infoOverlay.addToMap(); + infoOverlay.addToMap(); } else { infoOverlay.setInfoText(lineInfoProvider.getLineInfo()); } @@ -2001,29 +1877,28 @@ public class RaceMap extends AbstractCompositeComponent infoOverlay.draw(); } else { if (infoOverlay != null) { - infoOverlay.removeFromMap(); - infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); + infoOverlay.removeFromMap(); + infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); } } } - + /** - * If, according to {@link #lastRaceTimesInfo} and {@link #timer} the race is still in the pre-start phase, show a - * {@link SmallTransparentInfoOverlay} at the start line that shows the count down. + * If, according to {@link #lastRaceTimesInfo} and {@link #timer} the race is + * still in the pre-start phase, show a {@link SmallTransparentInfoOverlay} at the + * start line that shows the count down. */ private void updateCountdownCanvas(WaypointDTO startWaypoint) { - if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null - || Util.isEmpty(startWaypoint.controlPoint.getMarks()) || lastRaceTimesInfo == null - || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { + if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null || Util.isEmpty(startWaypoint.controlPoint.getMarks()) + || lastRaceTimesInfo == null || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { if (countDownOverlay != null) { countDownOverlay.removeFromMap(); countDownOverlay = null; } } else { long timeToStartInMs = lastRaceTimesInfo.startOfRace.getTime() - timer.getTime().getTime(); - String countDownText = timeToStartInMs > 1000 - ? stringMessages.timeToStart(DateAndTimeFormatterUtil.formatElapsedTime(timeToStartInMs)) - : stringMessages.start(); + String countDownText = timeToStartInMs > 1000 ? stringMessages.timeToStart(DateAndTimeFormatterUtil + .formatElapsedTime(timeToStartInMs)) : stringMessages.start(); if (countDownOverlay == null) { countDownOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, countDownText, coordinateSystem, getSettings().getStartCountDownFontSizeScaling()); @@ -2031,8 +1906,7 @@ public class RaceMap extends AbstractCompositeComponent } else { countDownOverlay.setInfoText(countDownText); } - countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, - /* transition time */ -1); + countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, /* transition time */ -1); countDownOverlay.draw(); } } @@ -2051,8 +1925,7 @@ public class RaceMap extends AbstractCompositeComponent int MAX_ZOOM = 20; // maximum zoom-level that should be automatically selected double LOG2 = Math.log(2.0); double deltaLng = bounds.getNorthEast().getLongitude() - bounds.getSouthWest().getLongitude(); - double deltaLat = getMercatorLatitude(bounds.getNorthEast().getLatitude()) - - getMercatorLatitude(bounds.getSouthWest().getLatitude()); + double deltaLat = getMercatorLatitude(bounds.getNorthEast().getLatitude()) - getMercatorLatitude(bounds.getSouthWest().getLatitude()); if ((deltaLng == 0) && (deltaLat == 0)) { return MAX_ZOOM; } @@ -2060,28 +1933,27 @@ public class RaceMap extends AbstractCompositeComponent deltaLng += 360; } int zoomLng = (int) Math.floor(Math.log(map.getDiv().getClientWidth() * 360 / deltaLng / GLOBE_PXSIZE) / LOG2); - int zoomLat = (int) Math - .floor(Math.log(map.getDiv().getClientHeight() * 2 * Math.PI / deltaLat / GLOBE_PXSIZE) / LOG2); + int zoomLat = (int) Math.floor(Math.log(map.getDiv().getClientHeight() * 2 * Math.PI / deltaLat / GLOBE_PXSIZE) / LOG2); return Math.min(Math.min(zoomLat, zoomLng), MAX_ZOOM); } - + private void zoomMapToNewBounds(LatLngBounds newBounds) { if (newBounds != null) { LatLngBounds currentMapBounds; - if (map.getBounds() == null || !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds) + if (map.getBounds() == null + || !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds) || graticuleAreaRatio(currentMapBounds, newBounds) > 10) { // only change bounds if the new bounds don't fit into the current map zoom - Iterable oldZoomTypesToConsiderSettings = settings.getZoomSettings() - .getTypesToConsiderOnZoom(); + Iterable oldZoomTypesToConsiderSettings = settings.getZoomSettings().getTypesToConsiderOnZoom(); setAutoZoomInProgress(true); autoZoomLatLngBounds = newBounds; - int newZoomLevel = getZoomLevel(autoZoomLatLngBounds); + int newZoomLevel = getZoomLevel(autoZoomLatLngBounds); if (newZoomLevel != map.getZoom()) { // distinguish between zoom-in and zoom-out, because the sequence of panTo() and setZoom() // appears different on the screen due to map-animations // following sequences keep the selected boats allways visible: - // zoom-in : 1. panTo(), 2. setZoom() - // zoom-out: 1. setZoom(), 2. panTo() + // zoom-in : 1. panTo(), 2. setZoom() + // zoom-out: 1. setZoom(), 2. panTo() autoZoomIn = newZoomLevel > map.getZoom(); autoZoomOut = !autoZoomIn; autoZoomLevel = newZoomLevel; @@ -2094,14 +1966,13 @@ public class RaceMap extends AbstractCompositeComponent } else { map.panTo(autoZoomLatLngBounds.getCenter()); } - RaceMapZoomSettings restoredZoomSettings = new RaceMapZoomSettings(oldZoomTypesToConsiderSettings, - settings.getZoomSettings().isZoomToSelectedCompetitors()); + RaceMapZoomSettings restoredZoomSettings = new RaceMapZoomSettings(oldZoomTypesToConsiderSettings, settings.getZoomSettings().isZoomToSelectedCompetitors()); settings = new RaceMapSettings(settings, restoredZoomSettings); setAutoZoomInProgress(false); } } } - + private double graticuleAreaRatio(LatLngBounds containing, LatLngBounds contained) { assert BoundsUtil.contains(containing, contained); double containingAreaRatio = getGraticuleArea(containing) / getGraticuleArea(contained); @@ -2109,29 +1980,26 @@ public class RaceMap extends AbstractCompositeComponent } /** - * A much simplified "area" calculation for a {@link Bounds} object, multiplying the differences in latitude and - * longitude degrees. The result therefore is in the order of magnitude of 60*60 square nautical miles. + * A much simplified "area" calculation for a {@link Bounds} object, multiplying the differences in latitude and longitude degrees. + * The result therefore is in the order of magnitude of 60*60 square nautical miles. */ private double getGraticuleArea(LatLngBounds bounds) { - return ((BoundsUtil.isCrossesDateLine(bounds) ? bounds.getNorthEast().getLongitude() + 360 - : bounds.getNorthEast().getLongitude()) - bounds.getSouthWest().getLongitude()) - * (bounds.getNorthEast().getLatitude() - bounds.getSouthWest().getLatitude()); + return ((BoundsUtil.isCrossesDateLine(bounds) ? bounds.getNorthEast().getLongitude()+360 : bounds.getNorthEast().getLongitude())-bounds.getSouthWest().getLongitude()) * + (bounds.getNorthEast().getLatitude() - bounds.getSouthWest().getLatitude()); } private void setAutoZoomInProgress(boolean autoZoomInProgress) { this.autoZoomInProgress = autoZoomInProgress; } - + boolean isAutoZoomInProgress() { return autoZoomInProgress; } - + /** - * @param timeForPositionTransitionMillis - * use -1 to not animate the position transition, e.g., during map zoom or non-play + * @param timeForPositionTransitionMillis use -1 to not animate the position transition, e.g., during map zoom or non-play */ - private boolean updateBoatCanvasForCompetitor(CompetitorDTO competitorDTO, Date date, - long timeForPositionTransitionMillis) { + private boolean updateBoatCanvasForCompetitor(CompetitorDTO competitorDTO, Date date, long timeForPositionTransitionMillis) { boolean hasTimeJumped = timeForPositionTransitionMillis > 3 * timer.getRefreshInterval(); if (hasTimeJumped) { timeForPositionTransitionMillis = -1; @@ -2141,8 +2009,7 @@ public class RaceMap extends AbstractCompositeComponent if (lastBoatFix != null) { BoatOverlay boatOverlay = boatOverlays.get(competitorDTO); if (boatOverlay == null) { - boatOverlay = createBoatOverlay(RaceMapOverlaysZIndexes.BOATS_ZINDEX, competitorDTO, - displayHighlighted(competitorDTO)); + boatOverlay = createBoatOverlay(RaceMapOverlaysZIndexes.BOATS_ZINDEX, competitorDTO, displayHighlighted(competitorDTO)); boatOverlays.put(competitorDTO, boatOverlay); boatOverlay.setDisplayMode(displayHighlighted(competitorDTO)); boatOverlay.setBoatFix(lastBoatFix, timeForPositionTransitionMillis); @@ -2154,62 +2021,66 @@ public class RaceMap extends AbstractCompositeComponent boatOverlay.draw(); } } + return usedExistingCanvas; } private DisplayMode displayHighlighted(CompetitorDTO competitorDTO) { boolean competitorisSelected = competitorSelection.isSelected(competitorDTO); - if (!settings.isShowOnlySelectedCompetitors()) { - if (competitorisSelected) { + if(!settings.isShowOnlySelectedCompetitors()){ + if(competitorisSelected){ return DisplayMode.SELECTED; - } else { - if (isSomeOtherCompetitorSelected()) { + } + else{ + if(isSomeOtherCompetitorSelected()){ return DisplayMode.NOT_SELECTED; - } else { + } + else{ return DisplayMode.DEFAULT; } } - } else { + } + else{ return competitorSelection.isSelected(competitorDTO) ? DisplayMode.SELECTED : DisplayMode.DEFAULT; } - + } - - private boolean isSomeOtherCompetitorSelected() { + + private boolean isSomeOtherCompetitorSelected(){ return Util.size(competitorSelection.getSelectedCompetitors()) > 0; } - + private class CourseMarkInfoWindowClickHandler implements ClickMapHandler { private final MarkDTO markDTO; private final CourseMarkOverlay courseMarkOverlay; - + public CourseMarkInfoWindowClickHandler(MarkDTO markDTO, CourseMarkOverlay courseMarkOverlay) { this.markDTO = markDTO; this.courseMarkOverlay = courseMarkOverlay; } - + @Override public void onEvent(ClickMapEvent event) { LatLng latlng = courseMarkOverlay.getMarkLatLngPosition(); showMarkInfoWindow(markDTO, latlng); } } - + private void registerCourseMarkInfoWindowClickHandler(final String markDTOIdAsString) { final CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTOIdAsString); - courseMarkClickHandlers.put(markDTOIdAsString, courseMarkOverlay.addClickHandler( - new CourseMarkInfoWindowClickHandler(markDTOs.get(markDTOIdAsString), courseMarkOverlay))); + courseMarkClickHandlers.put(markDTOIdAsString, + courseMarkOverlay.addClickHandler(new CourseMarkInfoWindowClickHandler(markDTOs.get(markDTOIdAsString), courseMarkOverlay))); } - + public void registerAllCourseMarkInfoWindowClickHandlers() { for (String markDTOIdAsString : markDTOs.keySet()) { registerCourseMarkInfoWindowClickHandler(markDTOIdAsString); } } - + public void unregisterAllCourseMarkInfoWindowClickHandlers() { Iterator> iterator = courseMarkClickHandlers.entrySet().iterator(); - while (iterator.hasNext()) { + while(iterator.hasNext()) { Entry handler = iterator.next(); handler.getValue().removeHandler(); iterator.remove(); @@ -2218,8 +2089,7 @@ public class RaceMap extends AbstractCompositeComponent private BoatOverlay createBoatOverlay(int zIndex, final CompetitorDTO competitorDTO, DisplayMode displayMode) { final BoatDTO boatOfCompetitor = competitorSelection.getBoat(competitorDTO); - final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, boatOfCompetitor, - competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); + final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, boatOfCompetitor, competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); boatCanvas.setDisplayMode(displayMode); boatCanvas.addClickHandler(event -> { GPSFixDTOWithSpeedWindTackAndLegType latestFixForCompetitor = getBoatFix(competitorDTO, timer.getTime()); @@ -2244,10 +2114,8 @@ public class RaceMap extends AbstractCompositeComponent return boatCanvas; } - protected WindSensorOverlay createWindSensorOverlay(int zIndex, final WindSource windSource, - final WindTrackInfoDTO windTrackInfoDTO) { - final WindSensorOverlay windSensorOverlay = new WindSensorOverlay(map, zIndex, raceMapImageManager, - stringMessages, coordinateSystem); + protected WindSensorOverlay createWindSensorOverlay(int zIndex, final WindSource windSource, final WindTrackInfoDTO windTrackInfoDTO) { + final WindSensorOverlay windSensorOverlay = new WindSensorOverlay(map, zIndex, raceMapImageManager, stringMessages, coordinateSystem); windSensorOverlay.setWindInfo(windTrackInfoDTO, windSource); windSensorOverlay.addClickHandler(new ClickMapHandler() { @Override @@ -2298,18 +2166,16 @@ public class RaceMap extends AbstractCompositeComponent flowPanel.add(value); return flowPanel; } - + private Widget getInfoWindowContent(MarkDTO markDTO) { VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.mark(), markDTO.getName())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), - markDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); - vPanel.add( - createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsSignedDecimalDegrees())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsSignedDecimalDegrees())); return vPanel; } - - private Widget getInfoWindowContent(ManeuverDTO maneuver, CompetitorDTO competitor) { + + private Widget getInfoWindowContent(ManeuverDTO maneuver) { SpeedWithBearingDTO before = maneuver.speedWithBearingBefore; SpeedWithBearingDTO after = maneuver.speedWithBearingAfter; VerticalPanel vPanel = new VerticalPanel(); @@ -2341,38 +2207,20 @@ public class RaceMap extends AbstractCompositeComponent if (maneuver.maneuverLoss != null) { Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); - CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); - if (maneuverLossCheckBoxValueStore.isEmpty()) { - maneuverLossCheckBoxValueStore.put(t, false); - } - maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.get(t)); + + CheckBox maneuverLossLinesCheckBox = new CheckBox("Show"); + maneuverLossLinesCheckBox.setValue(maneuverLossLinesCheckBoxValueStore); maneuverLossLinesCheckBox.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - visualizeManeuverLoss(maneuver, maneuverLossLinesCheckBox.getValue(), competitor); - maneuverLossCheckBoxValueStore.put(t, maneuverLossLinesCheckBox.getValue()); + visualizeManeuver(maneuver, maneuverLossLinesCheckBox.getValue()); + maneuverLossLinesCheckBoxValueStore = maneuverLossLinesCheckBox.getValue(); } - }); -// CheckBox maneuverLossTargetVMGCheckBox = new CheckBox ("Target VMG"); -// if (maneuverLossTargetVMGCheckBoxValueStore.isEmpty()) { -// maneuverLossTargetVMGCheckBoxValueStore.put(t, false); -// } -// maneuverLossTargetVMGCheckBox.setValue(maneuverLossTargetVMGCheckBoxValueStore.get(t)); -// maneuverLossTargetVMGCheckBox.addClickHandler(new ClickHandler() { -// -// @Override -// public void onClick(ClickEvent event) { -// showManeuverLossTargetVmg(competitor, maneuver, maneuverLossTargetVMGCheckBox.getValue()); -// maneuverLossTargetVMGCheckBoxValueStore.put(t, maneuverLossTargetVMGCheckBox.getValue()); -// } -// }); + }); HorizontalPanel hPanel = new HorizontalPanel(); hPanel.add(maneuverLossWidget); hPanel.add(maneuverLossLinesCheckBox); -// hPanel.add(maneuverLossTargetVMGCheckBox); - vPanel.add(hPanel); + vPanel.add(hPanel); } if (maneuver.markPassingTimePoint != null) { vPanel.add( @@ -2380,8 +2228,8 @@ public class RaceMap extends AbstractCompositeComponent DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.markPassingTimePoint) + (maneuver.markPassingSide == null ? "" : " (" + (maneuver.markPassingSide == NauticalSide.PORT - ? stringMessages.portSide() - : stringMessages.starboardSide()) + ")"))); + ? stringMessages.portSide() : stringMessages.starboardSide()) + + ")"))); } return vPanel; } @@ -2391,14 +2239,11 @@ public class RaceMap extends AbstractCompositeComponent final VerticalPanel vPanel = new VerticalPanel(); final Anchor windSourceNameAnchor = new Anchor(WindSourceTypeFormatter.format(windSource, stringMessages)); vPanel.add(createInfoWindowLabelWithWidget(stringMessages.windSource(), windSourceNameAnchor)); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.wind(), - Math.round(windDTO.dampenedTrueWindFromDeg) + " " + stringMessages.degreesShort())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.windSpeed(), - numberFormatOneDecimal.format(windDTO.dampenedTrueWindSpeedInKnots))); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.wind(), Math.round(windDTO.dampenedTrueWindFromDeg) + " " + stringMessages.degreesShort())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.windSpeed(), numberFormatOneDecimal.format(windDTO.dampenedTrueWindSpeedInKnots))); final MillisecondsTimePoint timePoint = new MillisecondsTimePoint(windDTO.measureTimepoint); vPanel.add(createInfoWindowLabelAndValue(stringMessages.time(), timePoint.asDate().toString())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), - windDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), windDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); final Label positionInDecimalDegreesLabel = new Label(windDTO.position.getAsSignedDecimalDegrees()); positionInDecimalDegreesLabel.setWordWrap(false); positionInDecimalDegreesLabel.getElement().getStyle().setFloat(Style.Float.LEFT); @@ -2438,8 +2283,7 @@ public class RaceMap extends AbstractCompositeComponent private Widget getInfoWindowContent(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType lastFix) { final VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.competitor(), competitorDTO.getName())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), - competitorSelection.getBoat(competitorDTO).getSailId())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), competitorSelection.getBoat(competitorDTO).getSailId())); final Integer rank = getRank(competitorDTO); if (rank != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.rank(), String.valueOf(rank))); @@ -2450,10 +2294,8 @@ public class RaceMap extends AbstractCompositeComponent speedWithBearing = new SpeedWithBearingDTO(0, 0); } vPanel.add(createInfoWindowLabelAndValue(stringMessages.speed(), - NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " " - + stringMessages.knotsUnit())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), - (int) speedWithBearing.bearingInDegrees + " " + stringMessages.degreesShort())); + NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " "+stringMessages.knotsUnit())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), (int) speedWithBearing.bearingInDegrees + " "+stringMessages.degreesShort())); if (lastFix.degreesBoatToTheWind != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.degreesBoatToTheWind(), (int) Math.abs(lastFix.degreesBoatToTheWind) + " " + stringMessages.degreesShort())); @@ -2470,13 +2312,11 @@ public class RaceMap extends AbstractCompositeComponent new AsyncCallback>>() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), - true /* silentMode */); + errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), true /*silentMode */); } @Override - public void onSuccess( - Map> result) { + public void onSuccess(Map> result) { lastDouglasPeuckerResult = result; if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); @@ -2488,12 +2328,10 @@ public class RaceMap extends AbstractCompositeComponent } } }); - sailingService.getManeuvers(race, timeRange, - new AsyncCallback>>() { + sailingService.getManeuvers(race, timeRange, new AsyncCallback>>() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), - true /* silentMode */); + errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), true /*silentMode */); } @Override @@ -2516,34 +2354,31 @@ public class RaceMap extends AbstractCompositeComponent /** * @return the {@link CompetitorSelectionProvider#getSelectedCompetitors()} if * {@link RaceMapSettings#isShowOnlySelectedCompetitors() only selected competitors are to be shown}, the - * {@link CompetitorSelectionProvider#getFilteredCompetitors() filtered competitors} otherwise. In both - * cases, if we have {@link RaceCompetitorSet information about the competitors participating} in this map's - * race, the result set is reduced to those, no matter if other regatta participants would otherwise have - * been in the result set + * {@link CompetitorSelectionProvider#getFilteredCompetitors() filtered competitors} otherwise. In both cases, + * if we have {@link RaceCompetitorSet information about the competitors participating} in this map's race, + * the result set is reduced to those, no matter if other regatta participants would otherwise have been in + * the result set */ private Iterable getCompetitorsToShow() { final Set result = new HashSet<>(); Iterable selection = competitorSelection.getSelectedCompetitors(); - final Set raceCompetitorIdsAsString = raceCompetitorSet - .getIdsOfCompetitorsParticipatingInRaceAsStrings(); + final Set raceCompetitorIdsAsString = raceCompetitorSet.getIdsOfCompetitorsParticipatingInRaceAsStrings(); if (!settings.isShowOnlySelectedCompetitors() || Util.isEmpty(selection)) { for (final CompetitorDTO filteredCompetitor : competitorSelection.getFilteredCompetitors()) { - if (raceCompetitorIdsAsString == null - || raceCompetitorIdsAsString.contains(filteredCompetitor.getIdAsString())) { + if (raceCompetitorIdsAsString == null || raceCompetitorIdsAsString.contains(filteredCompetitor.getIdAsString())) { result.add(filteredCompetitor); } } } else { for (final CompetitorDTO selectedCompetitor : selection) { - if (raceCompetitorIdsAsString == null - || raceCompetitorIdsAsString.contains(selectedCompetitor.getIdAsString())) { + if (raceCompetitorIdsAsString == null || raceCompetitorIdsAsString.contains(selectedCompetitor.getIdAsString())) { result.add(selectedCompetitor); } } } return result; } - + protected void removeAllMarkDouglasPeuckerpoints() { if (douglasMarkers != null) { for (Marker marker : douglasMarkers) { @@ -2560,10 +2395,17 @@ public class RaceMap extends AbstractCompositeComponent } maneuverMarkers = null; } + if (maneuverLossLines != null) { + for (Polyline maneuverLossLine : maneuverLossLines) { + maneuverLossLine.setMap((MapWidget) null); + } + maneuverLossLines = null; + maneuverLossLinesCheckBoxValueStore = false; + maneuverLossInfoOverlay.removeFromMap(); + } } - private void showMarkDouglasPeuckerPoints( - Map> gpsFixPointMapForCompetitors) { + private void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { douglasMarkers = new HashSet(); if (map != null && gpsFixPointMapForCompetitors != null) { Set keySet = gpsFixPointMapForCompetitors.keySet(); @@ -2574,7 +2416,7 @@ public class RaceMap extends AbstractCompositeComponent for (GPSFixDTOWithSpeedWindTackAndLegType fix : gpsFix) { LatLng latLng = coordinateSystem.toLatLng(fix.position); MarkerOptions options = MarkerOptions.newInstance(); - options.setTitle(fix.timepoint + ": " + fix.position + ", " + fix.speedWithBearing.toString()); + options.setTitle(fix.timepoint+": "+fix.position+", "+fix.speedWithBearing.toString()); Marker marker = Marker.newInstance(options); marker.setPosition(latLng); douglasMarkers.add(marker); @@ -2594,14 +2436,14 @@ public class RaceMap extends AbstractCompositeComponent List maneuversForCompetitor = maneuvers.get(competitorDTO); for (ManeuverDTO maneuver : maneuversForCompetitor) { if (settings.isShowManeuverType(maneuver.type)) { - createAndAddMarkerOfManeuver(maneuver, competitorDTO); + createAndAddMarkerOfManeuver(maneuver); } } } } } - private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { + private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver) { LatLng latLng = coordinateSystem.toLatLng(maneuver.position); Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor .get(new Pair(maneuver.type, ManeuverColor.getManeuverColor(maneuver))); @@ -2612,13 +2454,107 @@ public class RaceMap extends AbstractCompositeComponent marker.setTitle(ManeuverTypeFormatter.format(maneuver.type, stringMessages)); marker.addClickHandler(event -> { LatLng where = getCoordinateSystem().toLatLng(maneuver.position); - Widget content = getInfoWindowContent(maneuver, competitor); + Widget content = getInfoWindowContent(maneuver); managedInfoWindow.openAtPosition(content, where); }); maneuverMarkers.add(marker); marker.setMap(map); } + + final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return "Middle Maneuver Angle"; + } + }; + final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return "Extrapolated Maneuver Start Position"; + } + }; + final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return "Projected extrapolated Maneuver Start Position"; + } + }; + final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return "Projected Maneuver End Position"; + } + }; + final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.maneuverLoss(); + } + }; + private Set maneuverLossLines; + private boolean maneuverLossLinesCheckBoxValueStore = false; + private SmallTransparentInfoOverlay maneuverLossInfoOverlay; + private void visualizeManeuver(ManeuverDTO maneuver, boolean showManeuverVisualization) { + if (showManeuverVisualization == false) { + for (Polyline maneuverLossLine : maneuverLossLines) { + maneuverLossLine.setMap((MapWidget) null); + } + maneuverLossLines = null; + maneuverLossLinesCheckBoxValueStore = false; + maneuverLossInfoOverlay.removeFromMap(); + } else { + createManeuverLossLines(maneuver); + } + } +/** Visualizes the ManeuverLoss as calculated in {@link ManeuverLoss} + * + */ + private void createManeuverLossLines(ManeuverDTO maneuver) { + maneuverLossLines = new HashSet<>(); + Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); + Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); + Distance extrapolationOfManeuverStartPoint = new MeterDistance( + maneuver.maneuverLoss.speedWithBearingBefore.speedInKnots + * maneuver.maneuverLoss.maneuverDuration.asHours() * 1852); + Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.maneuverStartPosition + .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); + Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition + .getIntersection(bearingBefore, maneuver.position, middleManeuverAngle); + Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, + middleManeuverAngleLineInfoProvider, "#ffffff")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, + extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, + projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00")); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, + projectedManeuverEndPosition, maneuverLossLineInfoProvider, color)); + StringBuilder sb = new StringBuilder(); + sb.append(maneuverLossLineInfoProvider.getLineInfo() + ": "); + sb.append(numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); + sb.append('\n'); + sb.append("Maneuver Angle" + ": "); + sb.append(numberFormatOneDecimal.format(maneuver.directionChangeInDegrees) + " " + stringMessages.degreesUnit()); + maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, + RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); + maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, + new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); + maneuverLossInfoOverlay.draw(); + for (Polyline maneuverLossLine : maneuverLossLines) { + PolylineOptions options = PolylineOptions.newInstance(); + options.setStrokeOpacity(0.5); + maneuverLossLine.setOptions(options); + } + } + /** * @param date * the point in time for which to determine the competitor's boat position; approximated by using the fix @@ -2631,45 +2567,37 @@ public class RaceMap extends AbstractCompositeComponent GPSFixDTOWithSpeedWindTackAndLegType result = null; List competitorFixes = fixesAndTails.getFixes(competitorDTO); if (competitorFixes != null && !competitorFixes.isEmpty()) { - int i = Collections.binarySearch(competitorFixes, - new GPSFixDTOWithSpeedWindTackAndLegType(date, null, null, (WindDTO) null, null, null, false), + int i = Collections.binarySearch(competitorFixes, new GPSFixDTOWithSpeedWindTackAndLegType(date, null, null, (WindDTO) null, null, null, false), new Comparator() { - @Override - public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, - GPSFixDTOWithSpeedWindTackAndLegType o2) { - return o1.timepoint.compareTo(o2.timepoint); - } - }); - if (i < 0) { - i = -i - 1; // no perfect match; i is now the insertion point + @Override + public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, GPSFixDTOWithSpeedWindTackAndLegType o2) { + return o1.timepoint.compareTo(o2.timepoint); + } + }); + if (i<0) { + i = -i-1; // no perfect match; i is now the insertion point // if the insertion point is at the end, use last fix if (i >= competitorFixes.size()) { - result = competitorFixes.get(competitorFixes.size() - 1); + result = competitorFixes.get(competitorFixes.size()-1); } else if (i == 0) { // if the insertion point is at the beginning, use first fix result = competitorFixes.get(0); } else { - // competitorFixes must have at least two elements, and i points neither to the end nor the - // beginning; + // competitorFixes must have at least two elements, and i points neither to the end nor the beginning; // get the fix from i and i+1 whose timepoint is closer to date - final GPSFixDTOWithSpeedWindTackAndLegType fixBefore = competitorFixes.get(i - 1); + final GPSFixDTOWithSpeedWindTackAndLegType fixBefore = competitorFixes.get(i-1); final GPSFixDTOWithSpeedWindTackAndLegType fixAfter = competitorFixes.get(i); final GPSFixDTOWithSpeedWindTackAndLegType closer; - if (date.getTime() - fixBefore.timepoint.getTime() < fixAfter.timepoint.getTime() - - date.getTime()) { + if (date.getTime() - fixBefore.timepoint.getTime() < fixAfter.timepoint.getTime() - date.getTime()) { closer = fixBefore; } else { closer = fixAfter; } // now compute a weighted average depending on the time difference to "date" (see also bug 1924) - double factorForAfter = (double) (date.getTime() - fixBefore.timepoint.getTime()) - / (double) (fixAfter.timepoint.getTime() - fixBefore.timepoint.getTime()); - double factorForBefore = 1 - factorForAfter; - DegreePosition betweenPosition = new DegreePosition( - factorForBefore * fixBefore.position.getLatDeg() - + factorForAfter * fixAfter.position.getLatDeg(), - factorForBefore * fixBefore.position.getLngDeg() - + factorForAfter * fixAfter.position.getLngDeg()); + double factorForAfter = (double) (date.getTime()-fixBefore.timepoint.getTime()) / (double) (fixAfter.timepoint.getTime() - fixBefore.timepoint.getTime()); + double factorForBefore = 1-factorForAfter; + DegreePosition betweenPosition = new DegreePosition(factorForBefore*fixBefore.position.getLatDeg() + factorForAfter*fixAfter.position.getLatDeg(), + factorForBefore*fixBefore.position.getLngDeg() + factorForAfter*fixAfter.position.getLngDeg()); final double betweenBearing; if (fixBefore.speedWithBearing == null) { if (fixAfter.speedWithBearing == null) { @@ -2680,22 +2608,16 @@ public class RaceMap extends AbstractCompositeComponent } else if (fixAfter.speedWithBearing == null) { betweenBearing = fixBefore.speedWithBearing.bearingInDegrees; } else { - betweenBearing = new ScalableBearing( - new DegreeBearingImpl(fixBefore.speedWithBearing.bearingInDegrees)) - .multiply(factorForBefore) - .add(new ScalableBearing( - new DegreeBearingImpl(fixAfter.speedWithBearing.bearingInDegrees)) - .multiply(factorForAfter)) - .divide(1).getDegrees(); + betweenBearing = new ScalableBearing(new DegreeBearingImpl(fixBefore.speedWithBearing.bearingInDegrees)). + multiply(factorForBefore).add(new ScalableBearing(new DegreeBearingImpl(fixAfter.speedWithBearing.bearingInDegrees)). + multiply(factorForAfter)).divide(1).getDegrees(); } - SpeedWithBearingDTO betweenSpeed = new SpeedWithBearingDTO(factorForBefore - * (fixBefore.speedWithBearing == null ? 0 : fixBefore.speedWithBearing.speedInKnots) - + factorForAfter - * (fixAfter.speedWithBearing == null ? 0 : fixAfter.speedWithBearing.speedInKnots), + SpeedWithBearingDTO betweenSpeed = new SpeedWithBearingDTO( + factorForBefore*(fixBefore.speedWithBearing==null?0:fixBefore.speedWithBearing.speedInKnots) + + factorForAfter*(fixAfter.speedWithBearing==null?0:fixAfter.speedWithBearing.speedInKnots), betweenBearing); - result = new GPSFixDTOWithSpeedWindTackAndLegType(date, betweenPosition, betweenSpeed, - closer.degreesBoatToTheWind, closer.tack, closer.legType, - fixBefore.extrapolated || fixAfter.extrapolated); + result = new GPSFixDTOWithSpeedWindTackAndLegType(date, betweenPosition, betweenSpeed, closer.degreesBoatToTheWind, + closer.tack, closer.legType, fixBefore.extrapolated || fixAfter.extrapolated); } } else { // perfect match @@ -2748,8 +2670,7 @@ public class RaceMap extends AbstractCompositeComponent for (CompetitorDTO oneOfAllCompetitors : competitorSelection.getAllCompetitors()) { Polyline tail = fixesAndTails.getTail(oneOfAllCompetitors); if (tail != null) { - PolylineOptions newOptions = createTailStyle(oneOfAllCompetitors, - displayHighlighted(oneOfAllCompetitors)); + PolylineOptions newOptions = createTailStyle(oneOfAllCompetitors, displayHighlighted(oneOfAllCompetitors)); tail.setOptions(newOptions); } } @@ -2759,7 +2680,7 @@ public class RaceMap extends AbstractCompositeComponent zoomMapToNewBounds(zoomSettings.getNewBounds(this)); } } - + @Override public void removedFromSelection(CompetitorDTO competitor) { if (isShowAnyHelperLines()) { @@ -2791,7 +2712,7 @@ public class RaceMap extends AbstractCompositeComponent showCompetitorInfoOnMap(timer.getTime(), -1, competitorSelection.getSelectedFilteredCompetitors()); } } - // Trigger auto-zoom if needed + //Trigger auto-zoom if needed RaceMapZoomSettings zoomSettings = settings.getZoomSettings(); if (!zoomSettings.containsZoomType(ZoomTypes.NONE) && zoomSettings.isZoomToSelectedCompetitors()) { zoomMapToNewBounds(zoomSettings.getNewBounds(this)); @@ -2827,7 +2748,7 @@ public class RaceMap extends AbstractCompositeComponent boolean maneuverTypeSelectionChanged = false; boolean requiresRedraw = false; boolean requiresUpdateCoordinateSystem = false; - + for (ManeuverType maneuverType : ManeuverType.values()) { if (newSettings.isShowManeuverType(maneuverType) != settings.isShowManeuverType(maneuverType)) { maneuverTypeSelectionChanged = true; @@ -2837,12 +2758,10 @@ public class RaceMap extends AbstractCompositeComponent if (!(timer.getPlayState() == PlayStates.Playing) && lastManeuverResult != null) { removeAllManeuverMarkers(); showManeuvers(lastManeuverResult); - removeManeuverLossLinesAndInfoOverlays(); } } if (newSettings.isShowDouglasPeuckerPoints() != settings.isShowDouglasPeuckerPoints()) { - if (!(timer.getPlayState() == PlayStates.Playing) && lastDouglasPeuckerResult != null - && newSettings.isShowDouglasPeuckerPoints()) { + if (!(timer.getPlayState() == PlayStates.Playing) && lastDouglasPeuckerResult != null && newSettings.isShowDouglasPeuckerPoints()) { removeAllMarkDouglasPeuckerpoints(); showMarkDouglasPeuckerPoints(lastDouglasPeuckerResult); } else if (!newSettings.isShowDouglasPeuckerPoints()) { @@ -2870,7 +2789,7 @@ public class RaceMap extends AbstractCompositeComponent if (!newSettings.getHelpLinesSettings().equals(settings.getHelpLinesSettings())) { requiresRedraw = true; } - if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null) { + if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null){ estimatedDurationOverlay.removeFromParent(); } if (newSettings.isShowWindStreamletOverlay() != settings.isShowWindStreamletOverlay()) { @@ -2886,7 +2805,7 @@ public class RaceMap extends AbstractCompositeComponent requiresUpdateCoordinateSystem = true; requiresRedraw = true; } - if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null) { + if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null){ estimatedDurationOverlay.removeFromParent(); } if (newSettings.getStartCountDownFontSizeScaling() != settings.getStartCountDownFontSizeScaling()) { @@ -2897,7 +2816,7 @@ public class RaceMap extends AbstractCompositeComponent requiresRedraw = true; } this.settings = newSettings; - + if (requiresUpdateCoordinateSystem) { updateCoordinateSystemFromSettings(); } @@ -2909,7 +2828,7 @@ public class RaceMap extends AbstractCompositeComponent private void showSimulationOverlay(boolean visible) { simulationOverlay.setVisible(visible); if (visible) { - simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), true, -1 /* ensure ui-update */); + simulationOverlay.updateLeg(Math.max(lastLegNumber,1), true, -1 /* ensure ui-update */); } } @@ -2922,13 +2841,11 @@ public class RaceMap extends AbstractCompositeComponent if (selectedCompetitors == null || !selectedCompetitors.iterator().hasNext()) { competitors = forMap.getCompetitorsToShow(); } else { - competitors = isZoomOnlyToSelectedCompetitors(forMap) ? selectedCompetitors - : forMap.getCompetitorsToShow(); + competitors = isZoomOnlyToSelectedCompetitors(forMap) ? selectedCompetitors : forMap.getCompetitorsToShow(); } for (CompetitorDTO competitor : competitors) { try { - GPSFixDTOWithSpeedWindTackAndLegType competitorFix = forMap.getBoatFix(competitor, - forMap.timer.getTime()); + GPSFixDTOWithSpeedWindTackAndLegType competitorFix = forMap.getBoatFix(competitor, forMap.timer.getTime()); Position competitorPosition = competitorFix != null ? competitorFix.position : null; if (competitorPosition != null) { if (newBounds == null) { @@ -2939,30 +2856,25 @@ public class RaceMap extends AbstractCompositeComponent } } catch (IndexOutOfBoundsException e) { // TODO can't this be predicted and the exception be avoided in the first place? - // Catch this in case the competitor has no GPS fixes at the current time (e.g. in race 'Finale 2' - // of STG) + // Catch this in case the competitor has no GPS fixes at the current time (e.g. in race 'Finale 2' of STG) } } return newBounds; } - + } - + public static class TailsBoundsCalculator extends LatLngBoundsCalculatorForSelected { @Override public LatLngBounds calculateNewBounds(RaceMap racemap) { LatLngBounds newBounds = null; - Iterable competitors = isZoomOnlyToSelectedCompetitors(racemap) - ? racemap.competitorSelection.getSelectedCompetitors() - : racemap.getCompetitorsToShow(); + Iterable competitors = isZoomOnlyToSelectedCompetitors(racemap) ? racemap.competitorSelection.getSelectedCompetitors() : racemap.getCompetitorsToShow(); for (CompetitorDTO competitor : competitors) { Polyline tail = racemap.fixesAndTails.getTail(competitor); LatLngBounds bounds = null; // TODO: Find a replacement for missing Polyline function getBounds() from v2 - // see also - // http://stackoverflow.com/questions/3284808/getting-the-bounds-of-a-polyine-in-google-maps-api-v3; - // optionally, consider providing a bounds cache with two sorted sets that organize the LatLng objects - // for O(1) bounds calculation and logarithmic add, ideally O(1) remove + // see also http://stackoverflow.com/questions/3284808/getting-the-bounds-of-a-polyine-in-google-maps-api-v3; + // optionally, consider providing a bounds cache with two sorted sets that organize the LatLng objects for O(1) bounds calculation and logarithmic add, ideally O(1) remove if (tail != null && tail.getPath().getLength() >= 1) { bounds = BoundsUtil.getAsBounds(tail.getPath().get(0)); for (int i = 1; i < tail.getPath().getLength(); i++) { @@ -2980,7 +2892,7 @@ public class RaceMap extends AbstractCompositeComponent return newBounds; } } - + public static class CourseMarksBoundsCalculator implements LatLngBoundsCalculator { @Override public LatLngBounds calculateNewBounds(RaceMap forMap) { @@ -3037,17 +2949,17 @@ public class RaceMap extends AbstractCompositeComponent map.triggerResize(); zoomMapToNewBounds(settings.getZoomSettings().getNewBounds(RaceMap.this)); } - // Adjust RaceMap headers to avoid overlapping based on the RaceMap width + // Adjust RaceMap headers to avoid overlapping based on the RaceMap width boolean isCompactHeader = this.getOffsetWidth() <= 600; getLeftHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); getRightHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); - + // Adjust combined wind and true north indicator panel indent, based on the RaceMap height if (topLeftControlsWrapperPanel.getParent() != null) { this.adjustLeftControlsIndent(); } } - + private void adjustLeftControlsIndent() { topLeftControlsWrapperPanel.getParent().setStyleName("TopLeftControlsWrapperPanelParentDiv"); String leftControlsIndentStyle = getLeftControlsIndentStyle(); @@ -3060,17 +2972,16 @@ public class RaceMap extends AbstractCompositeComponent public void competitorsListChanged(Iterable competitors) { timeChanged(timer.getTime(), null); } - + @Override public void filteredCompetitorsListChanged(Iterable filteredCompetitors) { timeChanged(timer.getTime(), null); } - + @Override public void filterChanged(FilterSet> oldFilterSet, FilterSet> newFilterSet) { - // nothing to do; if the list of filtered competitors has changed, a separate call to - // filteredCompetitorsListChanged will occur + // nothing to do; if the list of filtered competitors has changed, a separate call to filteredCompetitorsListChanged will occur } @Override @@ -3080,7 +2991,7 @@ public class RaceMap extends AbstractCompositeComponent options.setGeodesic(true); options.setStrokeOpacity(1.0); - switch (displayMode) { + switch(displayMode){ case DEFAULT: options.setStrokeColor(competitorSelection.getColor(competitor, raceIdentifier).getAsHtml()); options.setStrokeWeight(1); @@ -3098,7 +3009,7 @@ public class RaceMap extends AbstractCompositeComponent options.setZindex(RaceMapOverlaysZIndexes.BOATTAILS_ZINDEX); return options; } - + @Override public Polyline createTail(final CompetitorDTO competitor, List points) { final BoatDTO boat = competitorSelection.getBoat(competitor); @@ -3142,7 +3053,7 @@ public class RaceMap extends AbstractCompositeComponent } return result; } - + private Image createSAPLogo() { ImageResource sapLogoResource = resources.sapLogoOverlay(); Image sapLogo = new Image(sapLogoResource); @@ -3170,42 +3081,42 @@ public class RaceMap extends AbstractCompositeComponent private MapOptions getMapOptions(final boolean showMapControls, boolean windUp) { MapOptions mapOptions = MapOptions.newInstance(); - mapOptions.setScrollWheel(true); - mapOptions.setMapTypeControl(showMapControls && !windUp); - mapOptions.setPanControl(showMapControls); - mapOptions.setZoomControl(showMapControls); - mapOptions.setScaleControl(true); - if (windUp) { - mapOptions.setMinZoom(8); - } else { - mapOptions.setMinZoom(0); - } - MapTypeStyle[] mapTypeStyles = new MapTypeStyle[4]; + mapOptions.setScrollWheel(true); + mapOptions.setMapTypeControl(showMapControls && !windUp); + mapOptions.setPanControl(showMapControls); + mapOptions.setZoomControl(showMapControls); + mapOptions.setScaleControl(true); + if (windUp) { + mapOptions.setMinZoom(8); + } else { + mapOptions.setMinZoom(0); + } + MapTypeStyle[] mapTypeStyles = new MapTypeStyle[4]; + + // hide all transit lines including ferry lines + mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT); + // hide points of interest + mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI); + // simplify road display + mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD); + // set water color + mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, WATER_COLOR); + + MapTypeControlOptions mapTypeControlOptions = MapTypeControlOptions.newInstance(); + mapTypeControlOptions.setPosition(ControlPosition.BOTTOM_RIGHT); + mapOptions.setMapTypeControlOptions(mapTypeControlOptions); - // hide all transit lines including ferry lines - mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT); - // hide points of interest - mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI); - // simplify road display - mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD); - // set water color - mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, WATER_COLOR); - - MapTypeControlOptions mapTypeControlOptions = MapTypeControlOptions.newInstance(); - mapTypeControlOptions.setPosition(ControlPosition.BOTTOM_RIGHT); - mapOptions.setMapTypeControlOptions(mapTypeControlOptions); - - mapOptions.setMapTypeStyles(mapTypeStyles); - // no need to try to position the scale control; it always ends up at the right bottom corner - mapOptions.setStreetViewControl(false); - if (showMapControls) { - ZoomControlOptions zoomControlOptions = ZoomControlOptions.newInstance(); - zoomControlOptions.setPosition(ControlPosition.RIGHT_TOP); - mapOptions.setZoomControlOptions(zoomControlOptions); - PanControlOptions panControlOptions = PanControlOptions.newInstance(); - panControlOptions.setPosition(ControlPosition.RIGHT_TOP); - mapOptions.setPanControlOptions(panControlOptions); - } + mapOptions.setMapTypeStyles(mapTypeStyles); + // no need to try to position the scale control; it always ends up at the right bottom corner + mapOptions.setStreetViewControl(false); + if (showMapControls) { + ZoomControlOptions zoomControlOptions = ZoomControlOptions.newInstance(); + zoomControlOptions.setPosition(ControlPosition.RIGHT_TOP); + mapOptions.setZoomControlOptions(zoomControlOptions); + PanControlOptions panControlOptions = PanControlOptions.newInstance(); + panControlOptions.setPosition(ControlPosition.RIGHT_TOP); + mapOptions.setPanControlOptions(panControlOptions); + } return mapOptions; } @@ -3219,11 +3130,11 @@ public class RaceMap extends AbstractCompositeComponent public Map getCourseMarkOverlays() { return courseMarkOverlays; } - + public CoordinateSystem getCoordinateSystem() { return coordinateSystem; } - + public void hideAllHelplines() { if (startLine != null) { startLine.setVisible(false); @@ -3249,7 +3160,7 @@ public class RaceMap extends AbstractCompositeComponent } } } - + public void showAllHelplinesToShow() { if (settings.getHelpLinesSettings().isShowAnyHelperLines()) { if (startLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE)) @@ -3258,8 +3169,8 @@ public class RaceMap extends AbstractCompositeComponent finishLine.setVisible(true); if (advantageLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) advantageLine.setVisible(true); - if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null - && settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) { + if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null && + settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) { windwardStartLineMarkToFirstMarkLine.setVisible(true); leewardStartLineMarkToFirstMarkLine.setVisible(true); } @@ -3275,7 +3186,7 @@ public class RaceMap extends AbstractCompositeComponent } } } - + public void addCompetitorsForRaceDefinedListener(CompetitorsForRaceDefinedListener listener) { raceCompetitorSet.addCompetitorsForRaceDefinedListener(listener); } @@ -3288,144 +3199,8 @@ public class RaceMap extends AbstractCompositeComponent public String getId() { return raceMapLifecycle.getComponentId(); } - + public RaceMapLifecycle getLifecycle() { return raceMapLifecycle; } - final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.middleManeuverAngle(); - } - }; - final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.extrapolatedManeuverStartPosition(); - } - }; - final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return ""; - } - }; - final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return ""; - } - }; - final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.maneuverLoss(); - } - }; - - /** - * Visualizes the Maneuver Loss, creates the corresponding polylines and Info Overlays calling - * {@link #createManeuverLossLinesAndInfoOverlays(ManeuverDTO, CompetitorDTO)}. If called with - * showManeuverVisualization=false removes the polylines and infooverlays, if there are any. - */ - private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, - CompetitorDTO competitor) { - if (showManeuverVisualization == false) { - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); - if (maneuverLossLinesMap.get(t) != null) { - for (Polyline p : maneuverLossLinesMap.get(t)) { - p.setMap((MapWidget) null); - } - } - maneuverLossInfoOverlayMap.get(t).removeFromMap(); - } else { - createManeuverLossLinesAndInfoOverlays(maneuver, competitor); - } - } - - /** - * Removes the Polylines visualizing the ManeuverLoss and the SmallTransparentInfoOverlays attached to the - * Polylines. Also clears the CheckboxValueStore for maneuverLoss. - */ - private void removeManeuverLossLinesAndInfoOverlays() { - if (!maneuverLossLinesMap.isEmpty()) { - for (Triple t : maneuverLossLinesMap.keySet()) { - for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { - maneuverLossLine.setMap((MapWidget) null); - } - } - maneuverLossLinesMap.clear(); - } - if (!maneuverLossInfoOverlayMap.isEmpty()) { - for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap - .entrySet()) { - e.getValue().removeFromMap(); - } - maneuverLossInfoOverlayMap.clear(); - } - if (!maneuverLossCheckBoxValueStore.isEmpty()) { - maneuverLossCheckBoxValueStore.clear(); - } - } - - /** - * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLoss()} and draw - * SmallTransparentInfoOverlays attached to the Polylines visualizing the maneuver. - * - */ - private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { - maneuverLossLines = new HashSet<>(); - Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); - Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); - Distance extrapolationOfManeuverStartPoint = new MeterDistance( - maneuver.maneuverLoss.speedWithBearingBefore.speedInKnots - * maneuver.maneuverLoss.maneuverDuration.asHours() * 1852); - Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.maneuverStartPosition - .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); - Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition - .getIntersection(bearingBefore, maneuver.position, middleManeuverAngle); - Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, - middleManeuverAngleLineInfoProvider, "#ffffff", 1)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, - extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff", 1)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, - projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00", 1)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, - projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00", 1)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, - projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 3)); - for (Polyline maneuverLossLine : maneuverLossLines) { - PolylineOptions options = PolylineOptions.newInstance(); - options.setStrokeOpacity(0.5); - maneuverLossLine.setOptions(options); - } - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); - maneuverLossLinesMap.put(t, maneuverLossLines); - StringBuilder sb = new StringBuilder(); - sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) - + stringMessages.metersUnit() + '\n'); - if (maneuver.type == ManeuverType.TACK) { - sb.append(stringMessages.tackAngle() + ": "); - } else if (maneuver.type == ManeuverType.JIBE) { - sb.append(stringMessages.jibeAngle() + ": "); - } else { - sb.append(stringMessages.maneuverAngle() + ": "); - } - sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.directionChangeInDegrees)) - + stringMessages.degreesUnit()); - SmallTransparentInfoOverlay maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, - RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); - maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, - new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); - maneuverLossInfoOverlay.draw(); - maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); - } } From 5da94651d4b715c59a3c32e1b42f6516f2114835 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 29 Jun 2018 15:54:04 +0200 Subject: [PATCH 17/53] Revert "Merge branch 'bug4572' of ssh://sapsailing.com/home/trac/git into bug4572" This reverts commit a70168e8f19312d493c044032a25cd45a4d69ae7, reversing changes made to c4515bb6a53d4e0c50857059dd34cd453078da1a. --- .../gwt/ui/shared/ManeuverLossDTO.java | 30 ++++--------------- 1 file changed, 5 insertions(+), 25 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java index 3460cacb72e..40a4900f410 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java @@ -5,11 +5,11 @@ import com.sap.sailing.domain.common.Position; import com.sap.sse.common.Duration; public class ManeuverLossDTO implements IsSerializable { - private Position maneuverStartPosition; - private Position maneuverEndPosition; - private SpeedWithBearingDTO speedWithBearingBefore; - private Double middleManeuverAngle; - private Duration maneuverDuration; + public Position maneuverStartPosition; + public Position maneuverEndPosition; + public SpeedWithBearingDTO speedWithBearingBefore; + public Double middleManeuverAngle; + public Duration maneuverDuration; public ManeuverLossDTO() { } @@ -22,24 +22,4 @@ public class ManeuverLossDTO implements IsSerializable { this.middleManeuverAngle = middleManeuverAngle; this.maneuverDuration = maneuverDuration; } - - public Position getManeuverStartPosition() { - return maneuverStartPosition; - } - - public Position getManeuverEndPosition() { - return maneuverEndPosition; - } - - public SpeedWithBearingDTO getSpeedWithBearingBefore() { - return speedWithBearingBefore; - } - - public Double getMiddleManeuverAngle() { - return middleManeuverAngle; - } - - public Duration getManeuverDuration() { - return maneuverDuration; - } } \ No newline at end of file From 7a46bbb227a9903a6874b04c8678403cc9bd5797 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 29 Jun 2018 16:05:54 +0200 Subject: [PATCH 18/53] bug4572: Get RaceMap.java from master --- .../gwt/ui/client/shared/racemap/RaceMap.java | 146 ++---------------- 1 file changed, 13 insertions(+), 133 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index ed3b5372108..ef4ef34addd 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -65,17 +65,14 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FlowPanel; -import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.HorizontalPanel; +import com.google.gwt.user.client.ui.Image; import com.google.gwt.user.client.ui.Label; 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.Bearing; import com.sap.sailing.domain.common.Bounds; -import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.ManeuverType; import com.sap.sailing.domain.common.NauticalSide; import com.sap.sailing.domain.common.Position; @@ -86,7 +83,6 @@ import com.sap.sailing.domain.common.dto.BoatDTO; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.CompetitorWithBoatDTO; import com.sap.sailing.domain.common.dto.LeaderboardDTO; -import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing; @@ -136,7 +132,9 @@ import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapAPIKey; import com.sap.sailing.gwt.ui.shared.racemap.GoogleMapStyleHelper; import com.sap.sailing.gwt.ui.shared.racemap.RaceSimulationOverlay; import com.sap.sailing.gwt.ui.shared.racemap.WindStreamletsRaceboardOverlay; +import com.sap.sse.common.Bearing; import com.sap.sse.common.Color; +import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimeRange; @@ -145,6 +143,7 @@ import com.sap.sse.common.Util.Pair; import com.sap.sse.common.Util.Triple; import com.sap.sse.common.filter.Filter; import com.sap.sse.common.filter.FilterSet; +import com.sap.sse.common.impl.DegreeBearingImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.RGBColor; import com.sap.sse.common.impl.TimeRangeImpl; @@ -729,7 +728,7 @@ public class RaceMap extends AbstractCompositeComponent impleme RaceMap.this.managedInfoWindow = new ManagedInfoWindow(map); } }; - LoadApi.go(onLoad, loadLibraries, sensor, "key="+GoogleMapAPIKey.V3_APIKey); + LoadApi.go(onLoad, loadLibraries, sensor, GoogleMapAPIKey.V3_PARAMS); } /** @@ -2021,21 +2020,18 @@ public class RaceMap extends AbstractCompositeComponent impleme boatOverlay.draw(); } } - return usedExistingCanvas; } private DisplayMode displayHighlighted(CompetitorDTO competitorDTO) { boolean competitorisSelected = competitorSelection.isSelected(competitorDTO); - if(!settings.isShowOnlySelectedCompetitors()){ - if(competitorisSelected){ + if (!settings.isShowOnlySelectedCompetitors()) { + if (competitorisSelected) { return DisplayMode.SELECTED; - } - else{ - if(isSomeOtherCompetitorSelected()){ + } else { + if (isSomeOtherCompetitorSelected()) { return DisplayMode.NOT_SELECTED; - } - else{ + } else { return DisplayMode.DEFAULT; } } @@ -2204,23 +2200,9 @@ public class RaceMap extends AbstractCompositeComponent impleme NumberFormat.getDecimalFormat().format(maneuver.lowestSpeedInKnots) + " " + stringMessages.knotsUnit())); } - if (maneuver.maneuverLoss != null) { - Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), - numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); - - CheckBox maneuverLossLinesCheckBox = new CheckBox("Show"); - maneuverLossLinesCheckBox.setValue(maneuverLossLinesCheckBoxValueStore); - maneuverLossLinesCheckBox.addClickHandler(new ClickHandler() { - @Override - public void onClick(ClickEvent event) { - visualizeManeuver(maneuver, maneuverLossLinesCheckBox.getValue()); - maneuverLossLinesCheckBoxValueStore = maneuverLossLinesCheckBox.getValue(); - } - }); - HorizontalPanel hPanel = new HorizontalPanel(); - hPanel.add(maneuverLossWidget); - hPanel.add(maneuverLossLinesCheckBox); - vPanel.add(hPanel); + if (maneuver.maneuverLossInMeters != null) { + vPanel.add(createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit())); } if (maneuver.markPassingTimePoint != null) { vPanel.add( @@ -2395,14 +2377,6 @@ public class RaceMap extends AbstractCompositeComponent impleme } maneuverMarkers = null; } - if (maneuverLossLines != null) { - for (Polyline maneuverLossLine : maneuverLossLines) { - maneuverLossLine.setMap((MapWidget) null); - } - maneuverLossLines = null; - maneuverLossLinesCheckBoxValueStore = false; - maneuverLossInfoOverlay.removeFromMap(); - } } private void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { @@ -2460,101 +2434,7 @@ public class RaceMap extends AbstractCompositeComponent impleme maneuverMarkers.add(marker); marker.setMap(map); } - - final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return "Middle Maneuver Angle"; - } - }; - final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return "Extrapolated Maneuver Start Position"; - } - }; - final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return "Projected extrapolated Maneuver Start Position"; - } - }; - final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return "Projected Maneuver End Position"; - } - }; - final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.maneuverLoss(); - } - }; - private Set maneuverLossLines; - private boolean maneuverLossLinesCheckBoxValueStore = false; - private SmallTransparentInfoOverlay maneuverLossInfoOverlay; - private void visualizeManeuver(ManeuverDTO maneuver, boolean showManeuverVisualization) { - if (showManeuverVisualization == false) { - for (Polyline maneuverLossLine : maneuverLossLines) { - maneuverLossLine.setMap((MapWidget) null); - } - maneuverLossLines = null; - maneuverLossLinesCheckBoxValueStore = false; - maneuverLossInfoOverlay.removeFromMap(); - } else { - createManeuverLossLines(maneuver); - } - } -/** Visualizes the ManeuverLoss as calculated in {@link ManeuverLoss} - * - */ - private void createManeuverLossLines(ManeuverDTO maneuver) { - maneuverLossLines = new HashSet<>(); - Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); - Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); - Distance extrapolationOfManeuverStartPoint = new MeterDistance( - maneuver.maneuverLoss.speedWithBearingBefore.speedInKnots - * maneuver.maneuverLoss.maneuverDuration.asHours() * 1852); - Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.maneuverStartPosition - .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); - Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition - .getIntersection(bearingBefore, maneuver.position, middleManeuverAngle); - Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, - middleManeuverAngleLineInfoProvider, "#ffffff")); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, - extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff")); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, - projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00")); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, - projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00")); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, - projectedManeuverEndPosition, maneuverLossLineInfoProvider, color)); - StringBuilder sb = new StringBuilder(); - sb.append(maneuverLossLineInfoProvider.getLineInfo() + ": "); - sb.append(numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); - sb.append('\n'); - sb.append("Maneuver Angle" + ": "); - sb.append(numberFormatOneDecimal.format(maneuver.directionChangeInDegrees) + " " + stringMessages.degreesUnit()); - maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, - RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); - maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, - new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); - maneuverLossInfoOverlay.draw(); - for (Polyline maneuverLossLine : maneuverLossLines) { - PolylineOptions options = PolylineOptions.newInstance(); - options.setStrokeOpacity(0.5); - maneuverLossLine.setOptions(options); - } - } - /** * @param date * the point in time for which to determine the competitor's boat position; approximated by using the fix From 90a77527b55c9c56bcbd2ea8ffd39b7818627ec3 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Fri, 29 Jun 2018 17:16:40 +0200 Subject: [PATCH 19/53] bug4572: trying to fix reformatting of RaceMap.java part2 --- .../gwt/ui/client/shared/racemap/RaceMap.java | 199 ++++++++++++++++-- 1 file changed, 184 insertions(+), 15 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index ef4ef34addd..09330298f5a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -65,6 +65,7 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Button; +import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Image; @@ -441,6 +442,15 @@ public class RaceMap extends AbstractCompositeComponent impleme /** Callback to set the visibility of the wind chart. */ private final Consumer showWindChartForProvider; private ManagedInfoWindow managedInfoWindow; + + /** The Map where for each competitor and each maneuver is stored if it is visualized or not.*/ + private Map, Boolean> maneuverLossCheckBoxValueStore = new HashMap<>(); + + /** The Map where the polylines for the specific maneuver are stored.*/ + private Map, Set> maneuverLossLinesMap = new HashMap<>(); + + /** The Map where the info overlays for a maneuver loss visualization are stored.*/ + private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); private class AdvantageLineUpdater implements QuickRanksListener { @Override @@ -1062,6 +1072,9 @@ public class RaceMap extends AbstractCompositeComponent impleme if (maneuverMarkers != null) { removeAllManeuverMarkers(); } + if (!maneuverLossLinesMap.isEmpty()) { + removeManeuverLossLinesAndInfoOverlays(); + } if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); @@ -1625,12 +1638,12 @@ public class RaceMap extends AbstractCompositeComponent impleme (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider, - "grey"); + "grey", 1, 1.0); leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider, - "grey"); + "grey", 1, 1.0); } } @@ -1688,7 +1701,7 @@ public class RaceMap extends AbstractCompositeComponent impleme (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, - startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff"); + startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff", 1, 1.0); // draw the finish line final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0); final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null : courseDTO.getFinishMarkPositions().get(1); @@ -1702,7 +1715,7 @@ public class RaceMap extends AbstractCompositeComponent impleme finishLineAdvantageText.delete(0, finishLineAdvantageText.length()); } finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, - finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR); + finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR, 1, 1.0); // the control point pairs for which we already decided whether or not // to show a course middle line for; values tell whether to show the line and for which zero-based // start waypoint index to do so; when for an equal control point pair multiple decisions with different @@ -1787,7 +1800,7 @@ public class RaceMap extends AbstractCompositeComponent impleme return sb.toString(); } }; - return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, lineInfoProvider, "#2268a0"); + return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, lineInfoProvider, "#2268a0", 1, 1.0); } private interface LineInfoProvider { @@ -1804,7 +1817,8 @@ public class RaceMap extends AbstractCompositeComponent impleme * otherwise */ private Polyline showOrRemoveOrUpdateLine(Polyline lineToShowOrRemoveOrUpdate, final boolean showLine, - final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, String lineColorRGB) { + final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, + String lineColorRGB, int strokeWeight, double strokeOpacity) { if (position1DTO != null && position2DTO != null) { if (showLine) { LatLng courseMiddleLinePoint1 = coordinateSystem.toLatLng(position1DTO); @@ -1815,8 +1829,8 @@ public class RaceMap extends AbstractCompositeComponent impleme options.setClickable(true); options.setGeodesic(true); options.setStrokeColor(lineColorRGB); - options.setStrokeWeight(1); - options.setStrokeOpacity(1.0); + options.setStrokeWeight(strokeWeight); + options.setStrokeOpacity(strokeOpacity); pointsAsArray = MVCArray.newInstance(); lineToShowOrRemoveOrUpdate = Polyline.newInstance(options); lineToShowOrRemoveOrUpdate.setPath(pointsAsArray); @@ -2171,7 +2185,7 @@ public class RaceMap extends AbstractCompositeComponent impleme return vPanel; } - private Widget getInfoWindowContent(ManeuverDTO maneuver) { + private Widget getInfoWindowContent(ManeuverDTO maneuver, CompetitorDTO competitor) { SpeedWithBearingDTO before = maneuver.speedWithBearingBefore; SpeedWithBearingDTO after = maneuver.speedWithBearingAfter; VerticalPanel vPanel = new VerticalPanel(); @@ -2200,9 +2214,27 @@ public class RaceMap extends AbstractCompositeComponent impleme NumberFormat.getDecimalFormat().format(maneuver.lowestSpeedInKnots) + " " + stringMessages.knotsUnit())); } - if (maneuver.maneuverLossInMeters != null) { - vPanel.add(createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), - numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit())); + if (maneuver.maneuverLoss != null) { + Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); + CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); + if (maneuverLossCheckBoxValueStore.isEmpty()) { + maneuverLossCheckBoxValueStore.put(t, false); + } + maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.get(t)); + maneuverLossLinesCheckBox.addClickHandler(new ClickHandler() { + @Override + public void onClick(ClickEvent event) { + visualizeManeuverLoss(maneuver, maneuverLossLinesCheckBox.getValue(), competitor); + maneuverLossCheckBoxValueStore.put(t, maneuverLossLinesCheckBox.getValue()); + } + }); + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(maneuverLossWidget); + hPanel.add(maneuverLossLinesCheckBox); + vPanel.add(hPanel); } if (maneuver.markPassingTimePoint != null) { vPanel.add( @@ -2410,14 +2442,14 @@ public class RaceMap extends AbstractCompositeComponent impleme List maneuversForCompetitor = maneuvers.get(competitorDTO); for (ManeuverDTO maneuver : maneuversForCompetitor) { if (settings.isShowManeuverType(maneuver.type)) { - createAndAddMarkerOfManeuver(maneuver); + createAndAddMarkerOfManeuver(maneuver, competitorDTO); } } } } } - private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver) { + private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { LatLng latLng = coordinateSystem.toLatLng(maneuver.position); Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor .get(new Pair(maneuver.type, ManeuverColor.getManeuverColor(maneuver))); @@ -2428,7 +2460,7 @@ public class RaceMap extends AbstractCompositeComponent impleme marker.setTitle(ManeuverTypeFormatter.format(maneuver.type, stringMessages)); marker.addClickHandler(event -> { LatLng where = getCoordinateSystem().toLatLng(maneuver.position); - Widget content = getInfoWindowContent(maneuver); + Widget content = getInfoWindowContent(maneuver, competitor); managedInfoWindow.openAtPosition(content, where); }); maneuverMarkers.add(marker); @@ -2638,6 +2670,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (!(timer.getPlayState() == PlayStates.Playing) && lastManeuverResult != null) { removeAllManeuverMarkers(); showManeuvers(lastManeuverResult); + removeManeuverLossLinesAndInfoOverlays(); } } if (newSettings.isShowDouglasPeuckerPoints() != settings.isShowDouglasPeuckerPoints()) { @@ -3083,4 +3116,140 @@ public class RaceMap extends AbstractCompositeComponent impleme public RaceMapLifecycle getLifecycle() { return raceMapLifecycle; } + final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.middleManeuverAngle(); + } + }; + final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.extrapolatedManeuverStartPosition(); + } + }; + final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return ""; + } + }; + final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return ""; + } + }; + final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.maneuverLoss(); + } + }; + + /** + * Visualizes the Maneuver Loss, creates the corresponding polylines and Info Overlays calling + * {@link #createManeuverLossLinesAndInfoOverlays(ManeuverDTO, CompetitorDTO)}. If called with + * showManeuverVisualization=false removes the polylines and infooverlays, if there are any. + */ + private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, + CompetitorDTO competitor) { + if (showManeuverVisualization == false) { + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); + if (maneuverLossLinesMap.get(t) != null) { + for (Polyline p : maneuverLossLinesMap.get(t)) { + p.setMap((MapWidget) null); + } + } + maneuverLossInfoOverlayMap.get(t).removeFromMap(); + } else { + createManeuverLossLinesAndInfoOverlays(maneuver, competitor); + } + } + + /** + * Removes the Polylines visualizing the ManeuverLoss and the SmallTransparentInfoOverlays attached to the + * Polylines. Also clears the CheckboxValueStore for maneuverLoss. + */ + private void removeManeuverLossLinesAndInfoOverlays() { + if (!maneuverLossLinesMap.isEmpty()) { + for (Triple t : maneuverLossLinesMap.keySet()) { + for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { + maneuverLossLine.setMap((MapWidget) null); + } + } + maneuverLossLinesMap.clear(); + } + if (!maneuverLossInfoOverlayMap.isEmpty()) { + for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap + .entrySet()) { + e.getValue().removeFromMap(); + } + maneuverLossInfoOverlayMap.clear(); + } + if (!maneuverLossCheckBoxValueStore.isEmpty()) { + maneuverLossCheckBoxValueStore.clear(); + } + } + + /** + * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLoss()} and draw + * SmallTransparentInfoOverlays attached to the Polylines visualizing the maneuver. + * + */ + private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { + Set maneuverLossLines = new HashSet<>(); + Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.speedWithBearingBefore.bearingInDegrees); + Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.middleManeuverAngle); + Distance extrapolationOfManeuverStartPoint = new MeterDistance( + maneuver.maneuverLoss.speedWithBearingBefore.speedInKnots + * maneuver.maneuverLoss.maneuverDuration.asHours() * 1852); + Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.maneuverStartPosition + .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); + Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.maneuverStartPosition + .getIntersection(bearingBefore, maneuver.position, middleManeuverAngle); + Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + Position projectedManeuverEndPosition = maneuver.maneuverLoss.maneuverEndPosition.projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, + middleManeuverAngleLineInfoProvider, "#ffffff", 1, 0.5)); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverStartPosition, + extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff", 1, 0.5)); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, + projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00", 1, 0.3)); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.maneuverEndPosition, + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00", 1, 0.3)); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, + projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 2, 1.0)); +// for (Polyline maneuverLossLine : maneuverLossLines) { +// PolylineOptions options = PolylineOptions.newInstance(); +// options.setStrokeOpacity(0.5); +// maneuverLossLine.setOptions(options); +// } + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, + maneuver.type); + maneuverLossLinesMap.put(t, maneuverLossLines); + StringBuilder sb = new StringBuilder(); + sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + + stringMessages.metersUnit() + '\n'); + if (maneuver.type == ManeuverType.TACK) { + sb.append(stringMessages.tackAngle() + ": "); + } else if (maneuver.type == ManeuverType.JIBE) { + sb.append(stringMessages.jibeAngle() + ": "); + } else { + sb.append(stringMessages.maneuverAngle() + ": "); + } + sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.directionChangeInDegrees)) + + stringMessages.degreesUnit()); + SmallTransparentInfoOverlay maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, + RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); + maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, + new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); + maneuverLossInfoOverlay.draw(); + maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); + } } From 97656c4c61f2254be7b775f9213222942fa96dbe Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Sat, 30 Jun 2018 18:26:41 +0200 Subject: [PATCH 20/53] encapsulated fields in DTO classes Change-Id: I484ec07aa56a74ae7bc746e65965fcfba49664ac --- .../client/shared/racemap/ManeuverColor.java | 2 +- .../gwt/ui/client/shared/racemap/RaceMap.java | 84 ++++++------- .../racemap/maneuver/ManeuverTableData.java | 22 ++-- .../racemap/maneuver/ManeuverTablePanel.java | 4 +- .../sailing/gwt/ui/shared/ManeuverDTO.java | 118 +++++++++++++----- 5 files changed, 145 insertions(+), 85 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverColor.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverColor.java index bade7d63edf..c55129e5de6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverColor.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverColor.java @@ -6,7 +6,7 @@ public enum ManeuverColor { RED, GREEN; public static ManeuverColor getManeuverColor(ManeuverDTO maneuver) { - return maneuver.directionChangeInDegrees < 0 ? RED : GREEN; + return maneuver.getDirectionChangeInDegrees() < 0 ? RED : GREEN; } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index da201ef724a..f61ee3987f2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2310,15 +2310,15 @@ public class RaceMap extends AbstractCompositeComponent } private Widget getInfoWindowContent(ManeuverDTO maneuver, CompetitorDTO competitor) { - SpeedWithBearingDTO before = maneuver.speedWithBearingBefore; - SpeedWithBearingDTO after = maneuver.speedWithBearingAfter; + SpeedWithBearingDTO before = maneuver.getSpeedWithBearingBefore(); + SpeedWithBearingDTO after = maneuver.getSpeedWithBearingAfter(); VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.maneuverType(), - ManeuverTypeFormatter.format(maneuver.type, stringMessages))); + ManeuverTypeFormatter.format(maneuver.getType(), stringMessages))); vPanel.add(createInfoWindowLabelAndValue(stringMessages.time(), - DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.timePoint))); + DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getTimePoint()))); vPanel.add(createInfoWindowLabelAndValue(stringMessages.directionChange(), - ((int) Math.round(maneuver.directionChangeInDegrees)) + " " + stringMessages.degreesShort() + " (" + ((int) Math.round(maneuver.getDirectionChangeInDegrees())) + " " + stringMessages.degreesShort() + " (" + ((int) Math.round(before.bearingInDegrees)) + " " + stringMessages.degreesShort() + " -> " + ((int) Math.round(after.bearingInDegrees)) + " " + stringMessages.degreesShort() + ")")); vPanel.add(createInfoWindowLabelAndValue(stringMessages.speedChange(), @@ -2328,22 +2328,22 @@ public class RaceMap extends AbstractCompositeComponent + " -> " + NumberFormat.getDecimalFormat().format(after.speedInKnots) + " " + stringMessages.knotsUnit() + ")")); vPanel.add(createInfoWindowLabelAndValue(stringMessages.maxTurningRate(), - NumberFormat.getDecimalFormat().format(maneuver.maxTurningRateInDegreesPerSecond) + " " + NumberFormat.getDecimalFormat().format(maneuver.getMaxTurningRateInDegreesPerSecond()) + " " + stringMessages.degreesPerSecondUnit())); vPanel.add(createInfoWindowLabelAndValue(stringMessages.avgTurningRate(), - NumberFormat.getDecimalFormat().format(maneuver.avgTurningRateInDegreesPerSecond) + " " + NumberFormat.getDecimalFormat().format(maneuver.getAvgTurningRateInDegreesPerSecond()) + " " + stringMessages.degreesPerSecondUnit())); - if (maneuver.type != ManeuverType.BEAR_AWAY && maneuver.type != ManeuverType.HEAD_UP) { + if (maneuver.getType() != ManeuverType.BEAR_AWAY && maneuver.getType() != ManeuverType.HEAD_UP) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.lowestSpeed(), - NumberFormat.getDecimalFormat().format(maneuver.lowestSpeedInKnots) + " " + NumberFormat.getDecimalFormat().format(maneuver.getLowestSpeedInKnots()) + " " + stringMessages.knotsUnit())); } - if (maneuver.maneuverLoss != null) { + if (maneuver.getManeuverLoss() != null) { Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), - numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + " " + stringMessages.metersUnit()); + numberFormatOneDecimal.format(maneuver.getManeuverLossInMeters()) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), + maneuver.getType()); if (maneuverLossCheckBoxValueStore.isEmpty()) { maneuverLossCheckBoxValueStore.put(t, false); } @@ -2374,12 +2374,12 @@ public class RaceMap extends AbstractCompositeComponent // hPanel.add(maneuverLossTargetVMGCheckBox); vPanel.add(hPanel); } - if (maneuver.markPassingTimePoint != null) { + if (maneuver.getMarkPassingTimePoint() != null) { vPanel.add( createInfoWindowLabelAndValue(stringMessages.markPassing(), - DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.markPassingTimePoint) - + (maneuver.markPassingSide == null ? "" - : " (" + (maneuver.markPassingSide == NauticalSide.PORT + DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getMarkPassingTimePoint()) + + (maneuver.getMarkPassingSide() == null ? "" + : " (" + (maneuver.getMarkPassingSide() == NauticalSide.PORT ? stringMessages.portSide() : stringMessages.starboardSide()) + ")"))); } @@ -2593,7 +2593,7 @@ public class RaceMap extends AbstractCompositeComponent CompetitorDTO competitorDTO = iter.next(); List maneuversForCompetitor = maneuvers.get(competitorDTO); for (ManeuverDTO maneuver : maneuversForCompetitor) { - if (settings.isShowManeuverType(maneuver.type)) { + if (settings.isShowManeuverType(maneuver.getType())) { createAndAddMarkerOfManeuver(maneuver, competitorDTO); } } @@ -2602,16 +2602,16 @@ public class RaceMap extends AbstractCompositeComponent } private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { - LatLng latLng = coordinateSystem.toLatLng(maneuver.position); + LatLng latLng = coordinateSystem.toLatLng(maneuver.getPosition()); Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor - .get(new Pair(maneuver.type, ManeuverColor.getManeuverColor(maneuver))); + .get(new Pair(maneuver.getType(), ManeuverColor.getManeuverColor(maneuver))); MarkerOptions options = MarkerOptions.newInstance(); options.setIcon(maneuverMarker.getIcon_MarkerImage()); Marker marker = Marker.newInstance(options); marker.setPosition(latLng); - marker.setTitle(ManeuverTypeFormatter.format(maneuver.type, stringMessages)); + marker.setTitle(ManeuverTypeFormatter.format(maneuver.getType(), stringMessages)); marker.addClickHandler(event -> { - LatLng where = getCoordinateSystem().toLatLng(maneuver.position); + LatLng where = getCoordinateSystem().toLatLng(maneuver.getPosition()); Widget content = getInfoWindowContent(maneuver, competitor); managedInfoWindow.openAtPosition(content, where); }); @@ -3331,8 +3331,8 @@ public class RaceMap extends AbstractCompositeComponent private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { if (showManeuverVisualization == false) { - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), + maneuver.getType()); if (maneuverLossLinesMap.get(t) != null) { for (Polyline p : maneuverLossLinesMap.get(t)) { p.setMap((MapWidget) null); @@ -3376,28 +3376,28 @@ public class RaceMap extends AbstractCompositeComponent */ private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { maneuverLossLines = new HashSet<>(); - Bearing bearingBefore = new DegreeBearingImpl(maneuver.maneuverLoss.getSpeedWithBearingBefore().bearingInDegrees); - Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.maneuverLoss.getMiddleManeuverAngle()); + Bearing bearingBefore = new DegreeBearingImpl(maneuver.getManeuverLoss().getSpeedWithBearingBefore().bearingInDegrees); + Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.getManeuverLoss().getMiddleManeuverAngle()); Distance extrapolationOfManeuverStartPoint = new MeterDistance( - maneuver.maneuverLoss.getSpeedWithBearingBefore().speedInKnots - * maneuver.maneuverLoss.getManeuverDuration().asHours() * 1852); - Position extrapolatedManeuverStartPosition = maneuver.maneuverLoss.getManeuverStartPosition() + maneuver.getManeuverLoss().getSpeedWithBearingBefore().speedInKnots + * maneuver.getManeuverLoss().getManeuverDuration().asHours() * 1852); + Position extrapolatedManeuverStartPosition = maneuver.getManeuverLoss().getManeuverStartPosition() .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); - Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.maneuverLoss.getManeuverStartPosition() - .getIntersection(bearingBefore, maneuver.position, middleManeuverAngle); + Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.getManeuverLoss().getManeuverStartPosition() + .getIntersection(bearingBefore, maneuver.getPosition(), middleManeuverAngle); Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - Position projectedManeuverEndPosition = maneuver.maneuverLoss.getManeuverEndPosition().projectToLineThrough( + Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - String color = maneuver.maneuverLossInMeters > 0 ? color = "#FF0000" : "#00FF00"; + String color = maneuver.getManeuverLossInMeters() > 0 ? color = "#FF0000" : "#00FF00"; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, "#ffffff", 1)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.getManeuverStartPosition(), + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverStartPosition(), extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff", 1)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00", 1)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.maneuverLoss.getManeuverEndPosition(), + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverEndPosition(), projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00", 1)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 3)); @@ -3406,25 +3406,25 @@ public class RaceMap extends AbstractCompositeComponent options.setStrokeOpacity(0.5); maneuverLossLine.setOptions(options); } - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.timePoint, - maneuver.type); + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), + maneuver.getType()); maneuverLossLinesMap.put(t, maneuverLossLines); StringBuilder sb = new StringBuilder(); - sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.maneuverLossInMeters) + sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLossInMeters()) + stringMessages.metersUnit() + '\n'); - if (maneuver.type == ManeuverType.TACK) { + if (maneuver.getType() == ManeuverType.TACK) { sb.append(stringMessages.tackAngle() + ": "); - } else if (maneuver.type == ManeuverType.JIBE) { + } else if (maneuver.getType() == ManeuverType.JIBE) { sb.append(stringMessages.jibeAngle() + ": "); } else { sb.append(stringMessages.maneuverAngle() + ": "); } - sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.directionChangeInDegrees)) + sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.getDirectionChangeInDegrees())) + stringMessages.degreesUnit()); SmallTransparentInfoOverlay maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, - new MeterDistance(maneuver.maneuverLossInMeters / 2)), -1); + new MeterDistance(maneuver.getManeuverLossInMeters() / 2)), -1); maneuverLossInfoOverlay.draw(); maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java index 87271b908cd..8607f4cb942 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java @@ -26,17 +26,17 @@ class ManeuverTableData { ManeuverTableData(final CompetitorDTO competitor, final ManeuverDTO maneuver) { this.competitorName = competitor.getName(); - this.timePoint = maneuver.timePoint; - this.timePointBefore = maneuver.timePointBefore; - this.maneuverType = maneuver.type; - this.speedBeforeInKnots = maneuver.speedWithBearingBefore.speedInKnots; - this.speedAfterInKnots = maneuver.speedWithBearingAfter.speedInKnots; - this.lowestSpeedInKnots = maneuver.lowestSpeedInKnots; - this.maneuverLoss = maneuver.maneuverLossInMeters; - this.directionChange = maneuver.directionChangeInDegrees; - this.maximumTurningRate = maneuver.maxTurningRateInDegreesPerSecond; - this.averageTurningRate = maneuver.avgTurningRateInDegreesPerSecond; - this.markPassing = maneuver.markPassingTimePoint != null && maneuver.markPassingSide != null; + this.timePoint = maneuver.getTimePoint(); + this.timePointBefore = maneuver.getTimePointBefore(); + this.maneuverType = maneuver.getType(); + this.speedBeforeInKnots = maneuver.getSpeedWithBearingBefore().speedInKnots; + this.speedAfterInKnots = maneuver.getSpeedWithBearingAfter().speedInKnots; + this.lowestSpeedInKnots = maneuver.getLowestSpeedInKnots(); + this.maneuverLoss = maneuver.getManeuverLossInMeters(); + this.directionChange = maneuver.getDirectionChangeInDegrees(); + this.maximumTurningRate = maneuver.getMaxTurningRateInDegreesPerSecond(); + this.averageTurningRate = maneuver.getAvgTurningRateInDegreesPerSecond(); + this.markPassing = maneuver.getMarkPassingTimePoint() != null && maneuver.getMarkPassingSide() != null; } public String getCompetitorName() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java index 35c94d9fa89..c7033cf144a 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java @@ -384,7 +384,7 @@ public class ManeuverTablePanel extends AbstractCompositeComponent> cachedData = competitorDataProvider.getCachedData(); for (final Entry> entry : cachedData.entrySet()) { for (ManeuverDTO maneuver : entry.getValue()) { - if (settings.getSelectedManeuverTypes().contains(maneuver.type)) { + if (settings.getSelectedManeuverTypes().contains(maneuver.getType())) { data.add(new ManeuverTableData(entry.getKey(), maneuver)); } } @@ -499,7 +499,7 @@ public class ManeuverTablePanel extends AbstractCompositeComponent m.timePoint, LOADING_OFFSET_TO_NEXT_MANEUVER_PROVIDER, true); + super(timeRangeProvider, timer, m -> m.getTimePoint(), LOADING_OFFSET_TO_NEXT_MANEUVER_PROVIDER, true); this.asyncActionsExecutor = asyncActionsExecutor; this.raceIdentifier = raceIdentifier; this.sailingService = sailingService; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java index 37fb2fe1ecb..94a8c9332de 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java @@ -13,35 +13,35 @@ import com.sap.sailing.domain.common.Tack; import com.sap.sailing.gwt.ui.client.StringMessages; public class ManeuverDTO implements IsSerializable { - public ManeuverType type; + private ManeuverType type; - public Tack newTack; + private Tack newTack; - public Position position; + private Position position; - public Date timePoint; + private Date timePoint; - public Date timePointBefore; + private Date timePointBefore; - public SpeedWithBearingDTO speedWithBearingBefore; + private SpeedWithBearingDTO speedWithBearingBefore; - public SpeedWithBearingDTO speedWithBearingAfter; + private SpeedWithBearingDTO speedWithBearingAfter; - public double directionChangeInDegrees; + private double directionChangeInDegrees; - public Double maneuverLossInMeters; + private Double maneuverLossInMeters; - public double maxTurningRateInDegreesPerSecond; + private double maxTurningRateInDegreesPerSecond; - public double avgTurningRateInDegreesPerSecond; + private double avgTurningRateInDegreesPerSecond; - public double lowestSpeedInKnots; + private double lowestSpeedInKnots; - public Date markPassingTimePoint; + private Date markPassingTimePoint; - public NauticalSide markPassingSide; + private NauticalSide markPassingSide; - public ManeuverLossDTO maneuverLoss; + private ManeuverLossDTO maneuverLoss; public ManeuverDTO() {} @@ -68,14 +68,14 @@ public class ManeuverDTO implements IsSerializable { } public String toString(StringMessages stringMessages) { - SpeedWithBearingDTO before = this.speedWithBearingBefore; - SpeedWithBearingDTO after = this.speedWithBearingAfter; + SpeedWithBearingDTO before = this.getSpeedWithBearingBefore(); + SpeedWithBearingDTO after = this.getSpeedWithBearingAfter(); final DateTimeFormat dateTimeFormat = DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL); - String timeAndManeuver = dateTimeFormat.format(this.timePoint) + ": " + this.type.name(); - String timePointBefore = " (started: " + dateTimeFormat.format(this.timePointBefore) + ")"; + String timeAndManeuver = dateTimeFormat.format(this.getTimePoint()) + ": " + this.getType().name(); + String timePointBefore = " (started: " + dateTimeFormat.format(this.getTimePointBefore()) + ")"; String directionChange = stringMessages.directionChange() + ": " - + ((int) Math.round(this.directionChangeInDegrees)) + " " + stringMessages.degreesShort() + " (" + + ((int) Math.round(this.getDirectionChangeInDegrees())) + " " + stringMessages.degreesShort() + " (" + ((int) Math.round(before.bearingInDegrees)) + " " + stringMessages.degreesShort() + " -> " + ((int) Math.round(after.bearingInDegrees)) + " " + stringMessages.degreesShort() + ")"; String speedChange = stringMessages.speedChange() + ": " @@ -84,25 +84,85 @@ public class ManeuverDTO implements IsSerializable { + stringMessages.knotsUnit() + " -> " + NumberFormat.getDecimalFormat().format(after.speedInKnots) + " " + stringMessages.knotsUnit() + ")"; String maxTurningRate = stringMessages.maxTurningRate() + ": " - + NumberFormat.getDecimalFormat().format(this.maxTurningRateInDegreesPerSecond) + " " + + NumberFormat.getDecimalFormat().format(this.getMaxTurningRateInDegreesPerSecond()) + " " + stringMessages.degreesPerSecondUnit(); String avgTurningRate = stringMessages.avgTurningRate() + ": " - + NumberFormat.getDecimalFormat().format(this.avgTurningRateInDegreesPerSecond) + " " + + NumberFormat.getDecimalFormat().format(this.getAvgTurningRateInDegreesPerSecond()) + " " + stringMessages.degreesPerSecondUnit(); String lowestSpeed = stringMessages.lowestSpeed() + ": " - + NumberFormat.getDecimalFormat().format(this.lowestSpeedInKnots) + " " + stringMessages.knotsUnit(); - String maneuverLoss = this.maneuverLossInMeters == null ? "" + + NumberFormat.getDecimalFormat().format(this.getLowestSpeedInKnots()) + " " + stringMessages.knotsUnit(); + String maneuverLoss = this.getManeuverLossInMeters() == null ? "" : ("; " + stringMessages.maneuverLoss() + ": " - + NumberFormat.getDecimalFormat().format(this.maneuverLossInMeters) + " " + + NumberFormat.getDecimalFormat().format(this.getManeuverLossInMeters()) + " " + stringMessages.metersUnit()); - String markPassing = markPassingTimePoint == null ? "" + String markPassing = getMarkPassingTimePoint() == null ? "" : "; " + stringMessages.markPassedToAt( - this.markPassingSide == null ? "" - : this.markPassingSide == NauticalSide.PORT ? stringMessages.portSide() + this.getMarkPassingSide() == null ? "" + : this.getMarkPassingSide() == NauticalSide.PORT ? stringMessages.portSide() : stringMessages.starboardSide(), - DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(this.markPassingTimePoint)); + DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(this.getMarkPassingTimePoint())); String maneuverTitle = timeAndManeuver + timePointBefore + "; " + directionChange + "; " + speedChange + "; " + maxTurningRate + "; " + avgTurningRate + "; " + lowestSpeed + maneuverLoss + markPassing; return maneuverTitle; } + + public ManeuverType getType() { + return type; + } + + public Tack getNewTack() { + return newTack; + } + + public Position getPosition() { + return position; + } + + public Date getTimePoint() { + return timePoint; + } + + public Date getTimePointBefore() { + return timePointBefore; + } + + public SpeedWithBearingDTO getSpeedWithBearingBefore() { + return speedWithBearingBefore; + } + + public SpeedWithBearingDTO getSpeedWithBearingAfter() { + return speedWithBearingAfter; + } + + public double getDirectionChangeInDegrees() { + return directionChangeInDegrees; + } + + public Double getManeuverLossInMeters() { + return maneuverLossInMeters; + } + + public double getMaxTurningRateInDegreesPerSecond() { + return maxTurningRateInDegreesPerSecond; + } + + public double getAvgTurningRateInDegreesPerSecond() { + return avgTurningRateInDegreesPerSecond; + } + + public double getLowestSpeedInKnots() { + return lowestSpeedInKnots; + } + + public Date getMarkPassingTimePoint() { + return markPassingTimePoint; + } + + public NauticalSide getMarkPassingSide() { + return markPassingSide; + } + + public ManeuverLossDTO getManeuverLoss() { + return maneuverLoss; + } } From 1f41b2c96463ec792eed1ada07e549c6f0d6a08b Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Mon, 9 Jul 2018 15:39:49 +0200 Subject: [PATCH 21/53] bug4572: refactoring and deleting old entries for maneuver.maneuverLoss replaced by maneuver.maneuverLoss.projectedDistanceLost. Changes made respectivly also for the corresponding DTOs. --- .../impl/data/ManeuverWithContext.java | 2 +- .../test/ManeuverLossOnPenaltyCircleTest.java | 2 +- .../impl/AbstractLeaderboardWithCache.java | 4 ++-- .../impl/ManeuverDetectorImpl.java | 7 ++---- .../sap/sailing/domain/tracking/Maneuver.java | 5 ++-- .../sailing/domain/tracking/ManeuverLoss.java | 1 - .../domain/tracking/impl/ManeuverImpl.java | 12 ++-------- ...neuverWithCoarseGrainedBoundariesImpl.java | 2 +- .../ManeuverWithMainCurveBoundariesImpl.java | 5 ++-- ...ithStableSpeedAndCourseBoundariesImpl.java | 5 ++-- .../gwt/ui/client/shared/racemap/RaceMap.java | 13 ++++------- .../racemap/maneuver/ManeuverTableData.java | 2 +- .../gwt/ui/server/SailingServiceImpl.java | 5 ++-- .../sailing/gwt/ui/shared/ManeuverDTO.java | 13 +++-------- .../gwt/ui/shared/ManeuverLossDTO.java | 23 ++++++++++++++++++- .../ManeuverBasedWindEstimationTrackImpl.java | 2 +- .../impl/ManeuverJsonSerializer.java | 2 +- .../sailing/xmlexport/LeaderboardData.java | 4 ++-- 18 files changed, 51 insertions(+), 58 deletions(-) diff --git a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java index 484756aedc6..7dcd5f52e79 100644 --- a/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java +++ b/java/com.sap.sailing.datamining/src/com/sap/sailing/datamining/impl/data/ManeuverWithContext.java @@ -108,7 +108,7 @@ public class ManeuverWithContext implements HasManeuverContext { @Override public Distance getManeuverLossDistanceLost() { - return getManeuver().getManeuverLossDistanceLost(); + return getManeuver().getManeuverLoss() == null ? null : getManeuver().getManeuverLoss().getProjectedDistanceLost(); } @Override diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverLossOnPenaltyCircleTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverLossOnPenaltyCircleTest.java index e4eb2569a84..612e6943436 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverLossOnPenaltyCircleTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ManeuverLossOnPenaltyCircleTest.java @@ -56,6 +56,6 @@ public class ManeuverLossOnPenaltyCircleTest extends OnlineTracTracBasedTest { Competitor canottieri = getCompetitorByName("Club Canottieri Roggero di Lauria"); final Iterable maneuversCanottieri = getTrackedRace().getManeuvers(canottieri, getTrackedRace().getStartOfRace(), getTrackedRace().getEndOfRace(), /* waitForLatest */ true); final Optional penaltyCircleCanottieri = StreamSupport.stream(maneuversCanottieri.spliterator(), /* parallel */ false).filter(m->m.getType()==ManeuverType.PENALTY_CIRCLE).findAny(); - assertThat(penaltyCircleCanottieri.get().getManeuverLossDistanceLost(), new IsGreaterThan<>(new MeterDistance(30))); + assertThat(penaltyCircleCanottieri.get().getManeuverLoss().getProjectedDistanceLost(), new IsGreaterThan<>(new MeterDistance(30))); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java index 01aa7e721f1..7bc60eb8f96 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractLeaderboardWithCache.java @@ -1009,12 +1009,12 @@ public abstract class AbstractLeaderboardWithCache implements Leaderboard { case PENALTY_CIRCLE: if (!maneuver.getTimePoint().before(startOfLeg) && (legFinishTime == null || legFinishTime.after(timePoint) || maneuver.getTimePoint().before(legFinishTime))) { - if (maneuver.getManeuverLossDistanceLost() != null) { + if (maneuver.getManeuverLoss() != null) { result.numberOfManeuvers.put(maneuver.getType(), result.numberOfManeuvers.get(maneuver.getType()) + 1); totalManeuverLossInMeters.put(maneuver.getType(), totalManeuverLossInMeters.get(maneuver.getType()) - + maneuver.getManeuverLossDistanceLost().getMeters()); + + maneuver.getManeuverLoss().getProjectedDistanceLost().getMeters()); } } break; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java index 996db5bbfd4..a9ba715c9e4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/maneuverdetection/impl/ManeuverDetectorImpl.java @@ -640,7 +640,6 @@ public class ManeuverDetectorImpl extends AbstractManeuverDetectorImpl { ManeuverCurveBoundaries maneuverUnstableCourseAndSpeedBoundaries, Wind wind, MarkPassing markPassing) { final Maneuver maneuver; final ManeuverType maneuverType; - Distance maneuverLossDistanceLost = null; Tack tackAfterManeuver = null; ManeuverLoss maneuverLoss = null; final Position maneuverPosition = track.getEstimatedPosition(maneuverMainCurveDetails.getTimePoint(), @@ -681,10 +680,9 @@ public class ManeuverDetectorImpl extends AbstractManeuverDetectorImpl { } else { tackAfterManeuver = null; } - maneuverLossDistanceLost = getManeuverLoss(maneuverUnstableCourseAndSpeedBoundaries).getProjectedDistanceLost(); maneuverLoss = getManeuverLoss(maneuverUnstableCourseAndSpeedBoundaries); maneuver = new ManeuverWithStableSpeedAndCourseBoundariesImpl(maneuverType, tackAfterManeuver, - maneuverPosition, maneuverLossDistanceLost, maneuverMainCurveDetails.getTimePoint(), + maneuverPosition, maneuverMainCurveDetails.getTimePoint(), maneuverMainCurveDetails.extractCurveBoundariesOnly(), maneuverUnstableCourseAndSpeedBoundaries, maneuverMainCurveDetails.getMaxTurningRateInDegreesPerSecond(), markPassing, maneuverLoss); } else { @@ -700,8 +698,7 @@ public class ManeuverDetectorImpl extends AbstractManeuverDetectorImpl { } catch (NoWindException e) { tackAfterManeuver = null; } - maneuver = new ManeuverWithMainCurveBoundariesImpl(maneuverType, tackAfterManeuver, maneuverPosition, - maneuverLossDistanceLost, maneuverMainCurveDetails.getTimePoint(), + maneuver = new ManeuverWithMainCurveBoundariesImpl(maneuverType, tackAfterManeuver, maneuverPosition, maneuverMainCurveDetails.getTimePoint(), maneuverMainCurveDetails.extractCurveBoundariesOnly(), maneuverUnstableCourseAndSpeedBoundaries, maneuverMainCurveDetails.getMaxTurningRateInDegreesPerSecond(), markPassing, maneuverLoss); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java index 22cd92f0fc4..0a9a591d1d7 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/Maneuver.java @@ -6,7 +6,6 @@ import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; -import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; import com.sap.sse.common.Speed; import com.sap.sse.common.TimePoint; @@ -62,8 +61,8 @@ public interface Maneuver extends GPSFix { * and exiting the maneuver that the boat lost compared to not having maneuvered. The maneuver loss is calculated * considering the maneuver curve, which was performed between {@link #getTimePointBefore()} and * {@link #getTimePointAfter()}. - */ - Distance getManeuverLossDistanceLost(); +// */ +// Distance getManeuverLossDistanceLost(); /** * Gets the ManeuverLoss Object of a maneuver, that is calculated in {@link ManeuverDetectorImpl#getManeuverLoss} diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java index 114e2ce1e8b..db11f85e6cc 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java @@ -76,5 +76,4 @@ public class ManeuverLoss { public Bearing getMiddleManeuverAngle() { return middleManeuverAngle; } - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java index 94385bb7893..06c4810e935 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverImpl.java @@ -10,7 +10,6 @@ import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; import com.sap.sailing.domain.tracking.ManeuverLoss; import com.sap.sailing.domain.tracking.MarkPassing; -import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; import com.sap.sse.common.Speed; import com.sap.sse.common.TimePoint; @@ -25,21 +24,19 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve private final Tack newTack; private final Position position; private final TimePoint timePoint; - private final Distance maneuverLossDistanceLost; private final double maxTurningRateInDegreesPerSecond; private final ManeuverCurveBoundaries mainCurveBoundaries; private final ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries; private final MarkPassing markPassing; private final ManeuverLoss maneuverLoss; - public ManeuverImpl(ManeuverType type, Tack newTack, Position position, Distance maneuverLossDistanceLost, TimePoint timePoint, + public ManeuverImpl(ManeuverType type, Tack newTack, Position position, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries, double maxTurningRateInDegreesPerSecond, MarkPassing markPassing, ManeuverLoss maneuverLoss) { this.type = type; this.newTack = newTack; this.position = position; - this.maneuverLossDistanceLost = maneuverLossDistanceLost; this.timePoint = timePoint; this.mainCurveBoundaries = mainCurveBoundaries; this.maneuverCurveWithStableSpeedAndCourseBoundaries = maneuverCurveWithStableSpeedAndCourseBoundaries; @@ -78,11 +75,6 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve return newTack; } - @Override - public Distance getManeuverLossDistanceLost() { - return maneuverLossDistanceLost; - } - @Override public double getDirectionChangeInDegrees() { return getManeuverBoundaries().getDirectionChangeInDegrees(); @@ -108,7 +100,7 @@ public abstract class ManeuverImpl extends AbstractGPSFixImpl implements Maneuve return super.toString() + " " + type + " on new tack " + newTack + " on position " + position + " at time point " + timePoint + ", " + getManeuverBoundaries() + ", max. turning rate: " + maxTurningRateInDegreesPerSecond - + (getManeuverLossDistanceLost() == null ? "" : ", Lost approximately " + getManeuverLossDistanceLost()) + ", Mark passing: " + + (getManeuverLoss() == null ? "" : ", Lost approximately " + getManeuverLoss().getProjectedDistanceLost()) + ", Mark passing: " + markPassing; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java index 328e073e972..e781816192b 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java @@ -26,7 +26,7 @@ public class ManeuverWithCoarseGrainedBoundariesImpl extends ManeuverImpl { public ManeuverWithCoarseGrainedBoundariesImpl(ManeuverType type, Tack newTack, Position position, TimePoint timePoint, ManeuverCurveBoundaries maneuverBoundaries) { - super(type, newTack, position, null, timePoint, maneuverBoundaries, maneuverBoundaries, Math.abs(maneuverBoundaries.getDirectionChangeInDegrees()), null); + super(type, newTack, position, timePoint, maneuverBoundaries, maneuverBoundaries, Math.abs(maneuverBoundaries.getDirectionChangeInDegrees()), null, null); } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java index 907a2a33e7b..9f8e240a1da 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithMainCurveBoundariesImpl.java @@ -7,7 +7,6 @@ import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; import com.sap.sailing.domain.tracking.ManeuverLoss; import com.sap.sailing.domain.tracking.MarkPassing; -import com.sap.sse.common.Distance; import com.sap.sse.common.TimePoint; /** @@ -21,10 +20,10 @@ public class ManeuverWithMainCurveBoundariesImpl extends ManeuverImpl { private static final long serialVersionUID = 5831188137884083419L; public ManeuverWithMainCurveBoundariesImpl(ManeuverType type, Tack newTack, Position position, - Distance maneuverLossDistanceLost, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, + TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries, double maxTurningRateInDegreesPerSecond, MarkPassing markPassing, ManeuverLoss maneuverLoss) { - super(type, newTack, position, maneuverLossDistanceLost, timePoint, mainCurveBoundaries, + super(type, newTack, position, timePoint, mainCurveBoundaries, maneuverCurveWithStableSpeedAndCourseBoundaries, maxTurningRateInDegreesPerSecond, markPassing, maneuverLoss); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java index b91683f82a0..914f18ebb99 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithStableSpeedAndCourseBoundariesImpl.java @@ -7,7 +7,6 @@ import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.ManeuverCurveBoundaries; import com.sap.sailing.domain.tracking.ManeuverLoss; import com.sap.sailing.domain.tracking.MarkPassing; -import com.sap.sse.common.Distance; import com.sap.sse.common.TimePoint; /** @@ -22,10 +21,10 @@ public class ManeuverWithStableSpeedAndCourseBoundariesImpl extends ManeuverImpl private static final long serialVersionUID = 5831188137884083419L; public ManeuverWithStableSpeedAndCourseBoundariesImpl(ManeuverType type, Tack newTack, Position position, - Distance maneuverLossDistanceLost, TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, + TimePoint timePoint, ManeuverCurveBoundaries mainCurveBoundaries, ManeuverCurveBoundaries maneuverCurveWithStableSpeedAndCourseBoundaries, double maxTurningRateInDegreesPerSecond, MarkPassing markPassing, ManeuverLoss maneuverLoss) { - super(type, newTack, position, maneuverLossDistanceLost, timePoint, mainCurveBoundaries, + super(type, newTack, position, timePoint, mainCurveBoundaries, maneuverCurveWithStableSpeedAndCourseBoundaries, maxTurningRateInDegreesPerSecond, markPassing, maneuverLoss); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 0b83507eafd..828769e1041 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2216,7 +2216,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } if (maneuver.getManeuverLoss() != null) { Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), - numberFormatOneDecimal.format(maneuver.getManeuverLossInMeters()) + " " + stringMessages.metersUnit()); + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLostInMeters()) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); @@ -3213,7 +3213,7 @@ public class RaceMap extends AbstractCompositeComponent impleme intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - String color = maneuver.getManeuverLossInMeters() > 0 ? color = "#FF0000" : "#00FF00"; + String color = maneuver.getManeuverLoss().getDistanceLostInMeters() > 0 ? color = "#FF0000" : "#00FF00"; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, "#ffffff", 1, 0.5)); @@ -3225,16 +3225,11 @@ public class RaceMap extends AbstractCompositeComponent impleme projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00", 1, 0.3)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 2, 1.0)); -// for (Polyline maneuverLossLine : maneuverLossLines) { -// PolylineOptions options = PolylineOptions.newInstance(); -// options.setStrokeOpacity(0.5); -// maneuverLossLine.setOptions(options); -// } Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); maneuverLossLinesMap.put(t, maneuverLossLines); StringBuilder sb = new StringBuilder(); - sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLossInMeters()) + sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLostInMeters()) + stringMessages.metersUnit() + '\n'); if (maneuver.getType() == ManeuverType.TACK) { sb.append(stringMessages.tackAngle() + ": "); @@ -3248,7 +3243,7 @@ public class RaceMap extends AbstractCompositeComponent impleme SmallTransparentInfoOverlay maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, - new MeterDistance(maneuver.getManeuverLossInMeters() / 2)), -1); + new MeterDistance(maneuver.getManeuverLoss().getDistanceLostInMeters() / 2)), -1); maneuverLossInfoOverlay.draw(); maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java index 8607f4cb942..196586aee4c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java @@ -32,7 +32,7 @@ class ManeuverTableData { this.speedBeforeInKnots = maneuver.getSpeedWithBearingBefore().speedInKnots; this.speedAfterInKnots = maneuver.getSpeedWithBearingAfter().speedInKnots; this.lowestSpeedInKnots = maneuver.getLowestSpeedInKnots(); - this.maneuverLoss = maneuver.getManeuverLossInMeters(); + this.maneuverLoss = maneuver.getManeuverLoss() == null ? 0.0 : maneuver.getManeuverLoss().getDistanceLostInMeters(); this.directionChange = maneuver.getDirectionChangeInDegrees(); this.maximumTurningRate = maneuver.getMaxTurningRateInDegreesPerSecond(); this.averageTurningRate = maneuver.getAvgTurningRateInDegreesPerSecond(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 68f138e0637..be99db5814e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -3626,7 +3626,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final SpeedWithBearingDTO speedBefore = createSpeedWithBearingDTO(maneuver.getSpeedWithBearingBefore()); final SpeedWithBearingDTO speedAfter = createSpeedWithBearingDTO(maneuver.getSpeedWithBearingAfter()); final double directionChangeInDegrees = maneuver.getDirectionChangeInDegrees(); - final Double maneuverLossInMeters = maneuver.getManeuverLossDistanceLost() == null ? null : maneuver.getManeuverLossDistanceLost().getMeters(); final double maxTurningRateInDegreesPerSecond = maneuver.getMaxTurningRateInDegreesPerSecond(); final double averageTurningRateInDegreesPerSecond = maneuver.getAvgTurningRateInDegreesPerSecond(); final double lowestSpeedInKnots = maneuver.getLowestSpeed().getKnots(); @@ -3639,9 +3638,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final ManeuverLossDTO maneuverLoss = maneuver.getManeuverLoss() == null ? null : new ManeuverLossDTO(maneuver.getManeuverLoss().getManeuverStartPosition(), maneuver.getManeuverLoss().getManeuverEndPosition(), speedWithBearingBeforeManeuverLoss, middleManeuverAngle, - maneuver.getManeuverLoss().getManeuverDuration()); + maneuver.getManeuverLoss().getManeuverDuration(), maneuver.getManeuverLoss().getProjectedDistanceLost().getMeters()); result.add(new ManeuverDTO(type, newTack, position, timepoint, timePointBefore, speedBefore, - speedAfter, directionChangeInDegrees, maneuverLossInMeters, maxTurningRateInDegreesPerSecond, + speedAfter, directionChangeInDegrees, maxTurningRateInDegreesPerSecond, averageTurningRateInDegreesPerSecond, lowestSpeedInKnots, markPassingTimePoint, markPassingSide, maneuverLoss)); } return result; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java index 94a8c9332de..0c5065409dc 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverDTO.java @@ -29,8 +29,6 @@ public class ManeuverDTO implements IsSerializable { private double directionChangeInDegrees; - private Double maneuverLossInMeters; - private double maxTurningRateInDegreesPerSecond; private double avgTurningRateInDegreesPerSecond; @@ -47,7 +45,7 @@ public class ManeuverDTO implements IsSerializable { public ManeuverDTO(ManeuverType type, Tack newTack, Position position, Date timePoint, Date timePointBefore, SpeedWithBearingDTO speedWithBearingBefore, SpeedWithBearingDTO speedWithBearingAfter, - double directionChangeInDegrees, Double maneuverLossInMeters, double maxTurningRateInDegreesPerSecond, + double directionChangeInDegrees, double maxTurningRateInDegreesPerSecond, double avgTurningRateInDegreesPerSecond, double lowestSpeedInKnots, Date markPassingTimePoint, NauticalSide markPassingSide, ManeuverLossDTO maneuverLoss) { this.type = type; @@ -58,7 +56,6 @@ public class ManeuverDTO implements IsSerializable { this.speedWithBearingBefore = speedWithBearingBefore; this.speedWithBearingAfter = speedWithBearingAfter; this.directionChangeInDegrees = directionChangeInDegrees; - this.maneuverLossInMeters = maneuverLossInMeters; this.maxTurningRateInDegreesPerSecond = maxTurningRateInDegreesPerSecond; this.avgTurningRateInDegreesPerSecond = avgTurningRateInDegreesPerSecond; this.lowestSpeedInKnots = lowestSpeedInKnots; @@ -91,9 +88,9 @@ public class ManeuverDTO implements IsSerializable { + stringMessages.degreesPerSecondUnit(); String lowestSpeed = stringMessages.lowestSpeed() + ": " + NumberFormat.getDecimalFormat().format(this.getLowestSpeedInKnots()) + " " + stringMessages.knotsUnit(); - String maneuverLoss = this.getManeuverLossInMeters() == null ? "" + String maneuverLoss = this.getManeuverLoss() == null ? "" : ("; " + stringMessages.maneuverLoss() + ": " - + NumberFormat.getDecimalFormat().format(this.getManeuverLossInMeters()) + " " + + NumberFormat.getDecimalFormat().format(this.getManeuverLoss().getDistanceLostInMeters()) + " " + stringMessages.metersUnit()); String markPassing = getMarkPassingTimePoint() == null ? "" : "; " + stringMessages.markPassedToAt( @@ -138,10 +135,6 @@ public class ManeuverDTO implements IsSerializable { return directionChangeInDegrees; } - public Double getManeuverLossInMeters() { - return maneuverLossInMeters; - } - public double getMaxTurningRateInDegreesPerSecond() { return maxTurningRateInDegreesPerSecond; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java index 40a4900f410..30c62f5a3f5 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java @@ -10,16 +10,37 @@ public class ManeuverLossDTO implements IsSerializable { public SpeedWithBearingDTO speedWithBearingBefore; public Double middleManeuverAngle; public Duration maneuverDuration; + public Double projectedDistanceLostInMeters; public ManeuverLossDTO() { } public ManeuverLossDTO(Position maneuverStartPosition, Position maneuverEndPosition, - SpeedWithBearingDTO speedWithBearingBefore, Double middleManeuverAngle, Duration maneuverDuration) { + SpeedWithBearingDTO speedWithBearingBefore, Double middleManeuverAngle, Duration maneuverDuration, Double projectedDistanceLostInMeters) { this.maneuverStartPosition = maneuverStartPosition; this.maneuverEndPosition = maneuverEndPosition; this.speedWithBearingBefore = speedWithBearingBefore; this.middleManeuverAngle = middleManeuverAngle; this.maneuverDuration = maneuverDuration; + this.projectedDistanceLostInMeters = projectedDistanceLostInMeters; + } + + public Duration getManeuverDuration() { + return maneuverDuration; + } + public Position getManeuverStartPosition() { + return maneuverStartPosition; + } + public Position getManeuverEndPosition() { + return maneuverEndPosition; + } + public Double getMiddleManeuverAngle() { + return middleManeuverAngle; + } + public SpeedWithBearingDTO getSpeedWithBearingBefore() { + return speedWithBearingBefore; + } + public Double getDistanceLostInMeters() { + return projectedDistanceLostInMeters; } } \ No newline at end of file diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java index 3bd4e594870..54188f2d1d0 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/windestimation/ManeuverBasedWindEstimationTrackImpl.java @@ -195,7 +195,7 @@ public class ManeuverBasedWindEstimationTrackImpl extends WindTrackImpl { this.speedAtManeuverStart = maneuver.getSpeedWithBearingBefore(); this.middleManeuverCourse = maneuver.getSpeedWithBearingBefore().getBearing() .middle(maneuver.getSpeedWithBearingAfter().getBearing()); - this.maneuverLossDistanceLost = maneuver.getManeuverLossDistanceLost(); + this.maneuverLossDistanceLost = maneuver.getManeuverLoss() == null ? null : maneuver.getManeuverLoss().getProjectedDistanceLost(); @SuppressWarnings("unchecked") Pair>[] myLikelihoodAndTWSBasedOnSpeedAndAngleCache = (Pair>[]) new Pair[ManeuverType .values().length]; diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java index 14e3f1f4681..9020e2894cc 100755 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/ManeuverJsonSerializer.java @@ -44,7 +44,7 @@ public class ManeuverJsonSerializer implements JsonSerializer { : maneuver.getSpeedWithBearingAfter().getBearing().getDegrees()); result.put(DIRECTION_CHANGE_IN_DEGREES, maneuver.getDirectionChangeInDegrees()); result.put(MANEUVER_LOSS, - maneuver.getManeuverLossDistanceLost() == null ? null : distanceSerializer.serialize(maneuver.getManeuverLossDistanceLost())); + maneuver.getManeuverLoss() == null ? null : distanceSerializer.serialize(maneuver.getManeuverLoss().getProjectedDistanceLost())); result.put(POSITION_AND_TIME, gpsFixSerializer.serialize(maneuver)); result.put(MAX_TURNING_RATE_IN_DEGREES_PER_SECOND, maneuver.getMaxTurningRateInDegreesPerSecond()); result.put(AVG_TURNING_RATE_IN_DEGREES_PER_SECOND, maneuver.getAvgTurningRateInDegreesPerSecond()); diff --git a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java index d719aa9726d..d813d9dd68d 100644 --- a/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java +++ b/java/com.sap.sailing.xmlexport/src/com/sap/sailing/xmlexport/LeaderboardData.java @@ -796,8 +796,8 @@ public class LeaderboardData extends ExportAction { addNamedElementWithValue(maneuverInformation, "type", man.getType().toString()); addNamedElementWithValue(maneuverInformation, "position", maneuverCounter); addNamedElementWithValue(maneuverInformation, "direction_change_in_degrees", man.getDirectionChangeInDegrees()); - Distance maneuverLossDistanceLost = man.getManeuverLossDistanceLost(); - addNamedElementWithValue(maneuverInformation, "loss_in_meters", maneuverLossDistanceLost != null ? maneuverLossDistanceLost.getMeters() : 0.0); + addNamedElementWithValue(maneuverInformation, "loss_in_meters", + man.getManeuverLoss() != null ? man.getManeuverLoss().getProjectedDistanceLost().getMeters() : 0.0); addNamedElementWithValue(maneuverInformation, "speed_before_in_knots", man.getSpeedWithBearingBefore().getKnots()); addNamedElementWithValue(maneuverInformation, "speed_after_in_knots", man.getSpeedWithBearingAfter().getKnots()); maneuversElement.addContent(maneuverInformation); From a15d0728d2fc181720cb81b659b401cc018a127d Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 16:36:53 +0200 Subject: [PATCH 22/53] made ManeuverLossDTO fields private now that they have getters Change-Id: Ia6d4b6a02b85ccf7bcf78176661e70528af7fc1d --- .../sap/sailing/gwt/ui/shared/ManeuverLossDTO.java | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java index 30c62f5a3f5..ad58ce5307e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/ManeuverLossDTO.java @@ -5,12 +5,12 @@ import com.sap.sailing.domain.common.Position; import com.sap.sse.common.Duration; public class ManeuverLossDTO implements IsSerializable { - public Position maneuverStartPosition; - public Position maneuverEndPosition; - public SpeedWithBearingDTO speedWithBearingBefore; - public Double middleManeuverAngle; - public Duration maneuverDuration; - public Double projectedDistanceLostInMeters; + private Position maneuverStartPosition; + private Position maneuverEndPosition; + private SpeedWithBearingDTO speedWithBearingBefore; + private Double middleManeuverAngle; + private Duration maneuverDuration; + private Double projectedDistanceLostInMeters; public ManeuverLossDTO() { } From 938932f0f7d8466f9469ad053e89e5d501f428ad Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 18:29:21 +0200 Subject: [PATCH 23/53] started adding CustomFieldSerializers for relevant SpeedWithBearing subclasses Change-Id: I002077405971ffff60defa9d62c6c1c33b6d0e42 --- .../impl/PreciseCompactGPSFixMovingImpl.java | 2 +- .../impl/VeryCompactGPSFixMovingImpl.java | 2 +- ...WithBearingImpl_CustomFieldSerializer.java | 50 ++++++++++++++++++ .../impl/PreciseCompactGPSFixMovingImpl.java | 45 ++++++++++++++++ .../impl/VeryCompactGPSFixMovingImpl.java | 51 +++++++++++++++++++ .../gwt/ui/client/shared/racemap/RaceMap.java | 22 ++++---- .../racemap/maneuver/ManeuverTableData.java | 8 +-- .../racemap/maneuver/ManeuverTablePanel.java | 2 +- .../gwt/ui/server/SailingServiceImpl.java | 6 +-- .../sailing/gwt/ui/shared/ManeuverDTO.java | 7 +-- .../gwt/ui/shared/ManeuverLossDTO.java | 19 ++++--- 11 files changed, 181 insertions(+), 33 deletions(-) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/impl/KnotSpeedWithBearingImpl_CustomFieldSerializer.java diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java index cc14f625de5..6b6868ad95e 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java @@ -38,7 +38,7 @@ public class PreciseCompactGPSFixMovingImpl extends AbstractCompactGPSFixMovingI private double cachedEstimatedSpeedInKnots; - private class PreciseCompactSpeedWithBearing extends AbstractCompactSpeedWithBearing implements SpeedWithBearing { + public class PreciseCompactSpeedWithBearing extends AbstractCompactSpeedWithBearing implements SpeedWithBearing { private static final long serialVersionUID = 1802065090733146728L; @Override diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java index 77c271e87c2..87a8b7a7171 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java @@ -72,7 +72,7 @@ public class VeryCompactGPSFixMovingImpl extends AbstractCompactGPSFixMovingImpl } } - private class VeryCompactSpeedWithBearing extends AbstractCompactSpeedWithBearing { + public class VeryCompactSpeedWithBearing extends AbstractCompactSpeedWithBearing { private static final long serialVersionUID = 1802065090733146728L; @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/impl/KnotSpeedWithBearingImpl_CustomFieldSerializer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/impl/KnotSpeedWithBearingImpl_CustomFieldSerializer.java new file mode 100755 index 00000000000..69926ff376c --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/impl/KnotSpeedWithBearingImpl_CustomFieldSerializer.java @@ -0,0 +1,50 @@ +package com.google.gwt.user.client.rpc.core.com.sap.sailing.domain.common.impl; + +import com.google.gwt.user.client.rpc.CustomFieldSerializer; +import com.google.gwt.user.client.rpc.SerializationException; +import com.google.gwt.user.client.rpc.SerializationStreamReader; +import com.google.gwt.user.client.rpc.SerializationStreamWriter; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; +import com.sap.sse.common.impl.DegreeBearingImpl; + +public class KnotSpeedWithBearingImpl_CustomFieldSerializer extends CustomFieldSerializer { + + @Override + public void serializeInstance(SerializationStreamWriter streamWriter, KnotSpeedWithBearingImpl instance) + throws SerializationException { + serialize(streamWriter, instance); + } + + public static void serialize(SerializationStreamWriter streamWriter, KnotSpeedWithBearingImpl instance) + throws SerializationException { + streamWriter.writeDouble(instance.getKnots()); + streamWriter.writeDouble(instance.getBearing().getDegrees()); + } + + @Override + public boolean hasCustomInstantiateInstance() { + return true; + } + + @Override + public KnotSpeedWithBearingImpl instantiateInstance(SerializationStreamReader streamReader) + throws SerializationException { + return instantiate(streamReader); + } + + public static KnotSpeedWithBearingImpl instantiate(SerializationStreamReader streamReader) + throws SerializationException { + return new KnotSpeedWithBearingImpl(streamReader.readDouble(), new DegreeBearingImpl(streamReader.readDouble())); + } + + @Override + public void deserializeInstance(SerializationStreamReader streamReader, KnotSpeedWithBearingImpl instance) + throws SerializationException { + deserialize(streamReader, instance); + } + + public static void deserialize(SerializationStreamReader streamReader, KnotSpeedWithBearingImpl instance) { + // Done by instantiateInstance + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java index e84ad7f3faf..6d1e098894f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java @@ -5,7 +5,10 @@ import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializationStreamReader; import com.google.gwt.user.client.rpc.SerializationStreamWriter; import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl.PreciseCompactPosition; +import com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl.PreciseCompactSpeedWithBearing; +import com.sap.sse.common.impl.DegreeBearingImpl; public final class PreciseCompactGPSFixMovingImpl { public static final class PreciseCompactPosition_CustomFieldSerializer extends CustomFieldSerializer { @@ -49,4 +52,46 @@ public final class PreciseCompactGPSFixMovingImpl { streamWriter.writeDouble(instance.getLngDeg()); } } + + public static final class PreciseCompactSpeedWithBearing_CustomFieldSerializer extends CustomFieldSerializer { + @Override + public boolean hasCustomInstantiateInstance() { + return true; + } + + @Override + public PreciseCompactSpeedWithBearing instantiateInstance(SerializationStreamReader streamReader) + throws SerializationException { + return instantiate(streamReader); + } + + public static PreciseCompactSpeedWithBearing instantiate(SerializationStreamReader streamReader) throws SerializationException { + final double speedInKnots = streamReader.readDouble(); + final double bearingDeg = streamReader.readDouble(); + return (PreciseCompactSpeedWithBearing) new com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl( + /* position */ null, /* timePoint */null, new KnotSpeedWithBearingImpl(speedInKnots, new DegreeBearingImpl(bearingDeg))).getSpeed(); + } + + @Override + public void deserializeInstance(SerializationStreamReader streamReader, PreciseCompactSpeedWithBearing instance) + throws SerializationException { + deserialize(streamReader, instance); + } + + public static void deserialize(SerializationStreamReader streamReader, PreciseCompactSpeedWithBearing instance) { + // handled by instantiate + } + + @Override + public void serializeInstance(SerializationStreamWriter streamWriter, PreciseCompactSpeedWithBearing instance) + throws SerializationException { + serialize(streamWriter, instance); + } + + public static void serialize(SerializationStreamWriter streamWriter, PreciseCompactSpeedWithBearing instance) + throws SerializationException { + streamWriter.writeDouble(instance.getKnots()); + streamWriter.writeDouble(instance.getBearing().getDegrees()); + } + } } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java index f4d26da9263..0c1ca18c8ee 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java @@ -8,8 +8,11 @@ import com.google.gwt.user.client.rpc.SerializationException; import com.google.gwt.user.client.rpc.SerializationStreamReader; import com.google.gwt.user.client.rpc.SerializationStreamWriter; import com.sap.sailing.domain.common.impl.DegreePosition; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException; import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl.VeryCompactPosition; +import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl.VeryCompactSpeedWithBearing; +import com.sap.sse.common.impl.DegreeBearingImpl; public final class VeryCompactGPSFixMovingImpl { private static final Logger logger = Logger.getLogger(VeryCompactGPSFixMovingImpl.class.getName()); @@ -61,4 +64,52 @@ public final class VeryCompactGPSFixMovingImpl { streamWriter.writeDouble(instance.getLngDeg()); } } + + public static final class VeryCompactSpeedWithBearing_CustomFieldSerializer extends CustomFieldSerializer { + @Override + public boolean hasCustomInstantiateInstance() { + return true; + } + + @Override + public VeryCompactSpeedWithBearing instantiateInstance(SerializationStreamReader streamReader) + throws SerializationException { + return instantiate(streamReader); + } + + public static VeryCompactSpeedWithBearing instantiate(SerializationStreamReader streamReader) throws SerializationException { + final double knotSpeed = streamReader.readDouble(); + final double bearingDeg = streamReader.readDouble(); + try { + return (VeryCompactSpeedWithBearing) new com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl( + /* position */ null, /* timePoint */null, new KnotSpeedWithBearingImpl(knotSpeed, new DegreeBearingImpl(bearingDeg))).getSpeed(); + } catch (CompactionNotPossibleException e) { + logger.log(Level.SEVERE, "Internal error: an object that was a very compact position and was serialized "+ + "couldn't be de-serialized again as such an object", e); + throw new RuntimeException(e); + } + } + + @Override + public void deserializeInstance(SerializationStreamReader streamReader, VeryCompactSpeedWithBearing instance) + throws SerializationException { + deserialize(streamReader, instance); + } + + public static void deserialize(SerializationStreamReader streamReader, VeryCompactSpeedWithBearing instance) { + // handled by instantiate + } + + @Override + public void serializeInstance(SerializationStreamWriter streamWriter, VeryCompactSpeedWithBearing instance) + throws SerializationException { + serialize(streamWriter, instance); + } + + public static void serialize(SerializationStreamWriter streamWriter, VeryCompactSpeedWithBearing instance) + throws SerializationException { + streamWriter.writeDouble(instance.getKnots()); + streamWriter.writeDouble(instance.getBearing().getDegrees()); + } + } } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 828769e1041..7a5a58b6cfb 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2216,7 +2216,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } if (maneuver.getManeuverLoss() != null) { Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), - numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLostInMeters()) + " " + stringMessages.metersUnit()); + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); @@ -3194,17 +3194,15 @@ public class RaceMap extends AbstractCompositeComponent impleme } /** - * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLoss()} and draw - * SmallTransparentInfoOverlays attached to the Polylines visualizing the maneuver. - * + * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLossInMeters()} and draw + * {@link SmallTransparentInfoOverlay}s attached to the {@link Polyline}s visualizing the maneuver. */ private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { - Set maneuverLossLines = new HashSet<>(); - Bearing bearingBefore = new DegreeBearingImpl(maneuver.getManeuverLoss().getSpeedWithBearingBefore().bearingInDegrees); + final Set maneuverLossLines = new HashSet<>(); + Bearing bearingBefore = maneuver.getManeuverLoss().getSpeedWithBearingBefore().getBearing(); Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.getManeuverLoss().getMiddleManeuverAngle()); - Distance extrapolationOfManeuverStartPoint = new MeterDistance( - maneuver.getManeuverLoss().getSpeedWithBearingBefore().speedInKnots - * maneuver.getManeuverLoss().getManeuverDuration().asHours() * 1852); + Distance extrapolationOfManeuverStartPoint = + maneuver.getManeuverLoss().getSpeedWithBearingBefore().travel(maneuver.getManeuverLoss().getManeuverDuration()); Position extrapolatedManeuverStartPosition = maneuver.getManeuverLoss().getManeuverStartPosition() .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.getManeuverLoss().getManeuverStartPosition() @@ -3213,7 +3211,7 @@ public class RaceMap extends AbstractCompositeComponent impleme intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - String color = maneuver.getManeuverLoss().getDistanceLostInMeters() > 0 ? color = "#FF0000" : "#00FF00"; + String color = maneuver.getManeuverLoss().getDistanceLost().compareTo(Distance.NULL) > 0 ? color = "#FF0000" : "#00FF00"; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, "#ffffff", 1, 0.5)); @@ -3229,7 +3227,7 @@ public class RaceMap extends AbstractCompositeComponent impleme maneuver.getType()); maneuverLossLinesMap.put(t, maneuverLossLines); StringBuilder sb = new StringBuilder(); - sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLostInMeters()) + sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + stringMessages.metersUnit() + '\n'); if (maneuver.getType() == ManeuverType.TACK) { sb.append(stringMessages.tackAngle() + ": "); @@ -3243,7 +3241,7 @@ public class RaceMap extends AbstractCompositeComponent impleme SmallTransparentInfoOverlay maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, - new MeterDistance(maneuver.getManeuverLoss().getDistanceLostInMeters() / 2)), -1); + maneuver.getManeuverLoss().getDistanceLost().scale(0.5)), /* transition time */ -1); maneuverLossInfoOverlay.draw(); maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java index 196586aee4c..564f80b57c9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTableData.java @@ -20,7 +20,7 @@ class ManeuverTableData { private final double lowestSpeedInKnots; private final double maximumTurningRate; private final double averageTurningRate; - private final Double maneuverLoss; + private final Double maneuverLossInMeters; private final double directionChange; private final boolean markPassing; @@ -32,7 +32,7 @@ class ManeuverTableData { this.speedBeforeInKnots = maneuver.getSpeedWithBearingBefore().speedInKnots; this.speedAfterInKnots = maneuver.getSpeedWithBearingAfter().speedInKnots; this.lowestSpeedInKnots = maneuver.getLowestSpeedInKnots(); - this.maneuverLoss = maneuver.getManeuverLoss() == null ? 0.0 : maneuver.getManeuverLoss().getDistanceLostInMeters(); + this.maneuverLossInMeters = maneuver.getManeuverLoss() == null ? 0.0 : maneuver.getManeuverLoss().getDistanceLost().getMeters(); this.directionChange = maneuver.getDirectionChangeInDegrees(); this.maximumTurningRate = maneuver.getMaxTurningRateInDegreesPerSecond(); this.averageTurningRate = maneuver.getAvgTurningRateInDegreesPerSecond(); @@ -83,8 +83,8 @@ class ManeuverTableData { return averageTurningRate; } - public Double getManeuverLoss() { - return maneuverLoss; + public Double getManeuverLossInMeters() { + return maneuverLossInMeters; } public double getDirectionChange() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java index d9260a02037..5b588fb674f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/maneuver/ManeuverTablePanel.java @@ -153,7 +153,7 @@ public class ManeuverTablePanel extends AbstractCompositeComponent Date: Mon, 9 Jul 2018 18:42:17 +0200 Subject: [PATCH 24/53] added more missing custom field serializers Change-Id: Id7c0d3d6440bb372991febba96228c91c38fd3a2 --- .../impl/VeryCompactGPSFixMovingImpl.java | 2 +- .../impl/VeryCompactGPSFixMovingImpl.java | 49 +++++++++++++++++++ 2 files changed, 50 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java index 87a8b7a7171..287cf22c770 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java @@ -104,7 +104,7 @@ public class VeryCompactGPSFixMovingImpl extends AbstractCompactGPSFixMovingImpl } } - private class VeryCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl { + public class VeryCompactEstimatedSpeed extends AbstractSpeedWithAbstractBearingImpl { private static final long serialVersionUID = -5871855443391817248L; @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java index 0c1ca18c8ee..c65fc00ddaf 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java @@ -10,6 +10,7 @@ import com.google.gwt.user.client.rpc.SerializationStreamWriter; import com.sap.sailing.domain.common.impl.DegreePosition; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.tracking.impl.CompactionNotPossibleException; +import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl.VeryCompactEstimatedSpeed; import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl.VeryCompactPosition; import com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl.VeryCompactSpeedWithBearing; import com.sap.sse.common.impl.DegreeBearingImpl; @@ -112,4 +113,52 @@ public final class VeryCompactGPSFixMovingImpl { streamWriter.writeDouble(instance.getBearing().getDegrees()); } } + + public static final class VeryCompactEstimatedSpeed_CustomFieldSerializer extends CustomFieldSerializer { + @Override + public boolean hasCustomInstantiateInstance() { + return true; + } + + @Override + public VeryCompactEstimatedSpeed instantiateInstance(SerializationStreamReader streamReader) + throws SerializationException { + return instantiate(streamReader); + } + + public static VeryCompactEstimatedSpeed instantiate(SerializationStreamReader streamReader) throws SerializationException { + final double knotSpeed = streamReader.readDouble(); + final double bearingDeg = streamReader.readDouble(); + try { + return (VeryCompactEstimatedSpeed) new com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl( + /* position */ null, /* timePoint */null, new KnotSpeedWithBearingImpl(knotSpeed, new DegreeBearingImpl(bearingDeg))).getSpeed(); + } catch (CompactionNotPossibleException e) { + logger.log(Level.SEVERE, "Internal error: an object that was a very compact position and was serialized "+ + "couldn't be de-serialized again as such an object", e); + throw new RuntimeException(e); + } + } + + @Override + public void deserializeInstance(SerializationStreamReader streamReader, VeryCompactEstimatedSpeed instance) + throws SerializationException { + deserialize(streamReader, instance); + } + + public static void deserialize(SerializationStreamReader streamReader, VeryCompactEstimatedSpeed instance) { + // handled by instantiate + } + + @Override + public void serializeInstance(SerializationStreamWriter streamWriter, VeryCompactEstimatedSpeed instance) + throws SerializationException { + serialize(streamWriter, instance); + } + + public static void serialize(SerializationStreamWriter streamWriter, VeryCompactEstimatedSpeed instance) + throws SerializationException { + streamWriter.writeDouble(instance.getKnots()); + streamWriter.writeDouble(instance.getBearing().getDegrees()); + } + } } \ No newline at end of file From 8e1cdd5a1f13be7e8e7f5676e6615b9efa6a7850 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 22:11:01 +0200 Subject: [PATCH 25/53] fixed custom field serializer Change-Id: I05324b9400fed54c00ad17167ff390989dd7f8ea --- .../tracking/impl/PreciseCompactGPSFixMovingImpl.java | 2 +- .../common/tracking/impl/VeryCompactGPSFixMovingImpl.java | 7 +++++-- 2 files changed, 6 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java index 6d1e098894f..066d5800090 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/PreciseCompactGPSFixMovingImpl.java @@ -69,7 +69,7 @@ public final class PreciseCompactGPSFixMovingImpl { final double speedInKnots = streamReader.readDouble(); final double bearingDeg = streamReader.readDouble(); return (PreciseCompactSpeedWithBearing) new com.sap.sailing.domain.common.tracking.impl.PreciseCompactGPSFixMovingImpl( - /* position */ null, /* timePoint */null, new KnotSpeedWithBearingImpl(speedInKnots, new DegreeBearingImpl(bearingDeg))).getSpeed(); + /* dummy position */ new DegreePosition(0, 0), /* timePoint */null, new KnotSpeedWithBearingImpl(speedInKnots, new DegreeBearingImpl(bearingDeg))).getSpeed(); } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java index c65fc00ddaf..355eec4bcad 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/google/gwt/user/client/rpc/core/com/sap/sailing/domain/common/tracking/impl/VeryCompactGPSFixMovingImpl.java @@ -130,8 +130,11 @@ public final class VeryCompactGPSFixMovingImpl { final double knotSpeed = streamReader.readDouble(); final double bearingDeg = streamReader.readDouble(); try { - return (VeryCompactEstimatedSpeed) new com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl( - /* position */ null, /* timePoint */null, new KnotSpeedWithBearingImpl(knotSpeed, new DegreeBearingImpl(bearingDeg))).getSpeed(); + final KnotSpeedWithBearingImpl speed = new KnotSpeedWithBearingImpl(knotSpeed, new DegreeBearingImpl(bearingDeg)); + com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl fix = new com.sap.sailing.domain.common.tracking.impl.VeryCompactGPSFixMovingImpl( + /* dummy position */ new DegreePosition(0, 0), /* timePoint */null, speed); + fix.cacheEstimatedSpeed(speed); + return (VeryCompactEstimatedSpeed) fix.getCachedEstimatedSpeed(); } catch (CompactionNotPossibleException e) { logger.log(Level.SEVERE, "Internal error: an object that was a very compact position and was serialized "+ "couldn't be de-serialized again as such an object", e); From 3fdfdbc96033ff4a09d7e02a3599112b4ea33e75 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 22:26:07 +0200 Subject: [PATCH 26/53] turned maneuverLossCheckBoxValueStore into a Set instead of a Map Change-Id: Ia233b897d429582a9ecd941e59394a86da3d401e --- .../gwt/ui/client/shared/racemap/RaceMap.java | 48 +++++++++---------- 1 file changed, 22 insertions(+), 26 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 7a5a58b6cfb..06b324e939f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -22,6 +22,8 @@ import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; 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.event.shared.HandlerRegistration; import com.google.gwt.i18n.client.DateTimeFormat; import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; @@ -89,6 +91,7 @@ import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing; import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition; import com.sap.sailing.domain.common.windfinder.SpotDTO; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; import com.sap.sailing.gwt.ui.actions.GetBoatPositionsAction; import com.sap.sailing.gwt.ui.actions.GetPolarAction; import com.sap.sailing.gwt.ui.actions.GetRaceMapDataAction; @@ -444,7 +447,7 @@ public class RaceMap extends AbstractCompositeComponent impleme private ManagedInfoWindow managedInfoWindow; /** The Map where for each competitor and each maneuver is stored if it is visualized or not.*/ - private Map, Boolean> maneuverLossCheckBoxValueStore = new HashMap<>(); + private Set> maneuverLossCheckBoxValueStore = new HashSet<>(); /** The Map where the polylines for the specific maneuver are stored.*/ private Map, Set> maneuverLossLinesMap = new HashMap<>(); @@ -2218,17 +2221,17 @@ public class RaceMap extends AbstractCompositeComponent impleme Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), - maneuver.getType()); - if (maneuverLossCheckBoxValueStore.isEmpty()) { - maneuverLossCheckBoxValueStore.put(t, false); - } - maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.get(t)); - maneuverLossLinesCheckBox.addClickHandler(new ClickHandler() { + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.contains(t)); + maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { @Override - public void onClick(ClickEvent event) { - visualizeManeuverLoss(maneuver, maneuverLossLinesCheckBox.getValue(), competitor); - maneuverLossCheckBoxValueStore.put(t, maneuverLossLinesCheckBox.getValue()); + public void onValueChange(ValueChangeEvent event) { + visualizeManeuverLoss(maneuver, event.getValue(), competitor); + if (event.getValue()) { + maneuverLossCheckBoxValueStore.add(t); + } else { + maneuverLossCheckBoxValueStore.remove(t); + } } }); HorizontalPanel hPanel = new HorizontalPanel(); @@ -3173,24 +3176,17 @@ public class RaceMap extends AbstractCompositeComponent impleme * Polylines. Also clears the CheckboxValueStore for maneuverLoss. */ private void removeManeuverLossLinesAndInfoOverlays() { - if (!maneuverLossLinesMap.isEmpty()) { - for (Triple t : maneuverLossLinesMap.keySet()) { - for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { - maneuverLossLine.setMap((MapWidget) null); - } + for (Triple t : maneuverLossLinesMap.keySet()) { + for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { + maneuverLossLine.setMap((MapWidget) null); } - maneuverLossLinesMap.clear(); } - if (!maneuverLossInfoOverlayMap.isEmpty()) { - for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap - .entrySet()) { - e.getValue().removeFromMap(); - } - maneuverLossInfoOverlayMap.clear(); - } - if (!maneuverLossCheckBoxValueStore.isEmpty()) { - maneuverLossCheckBoxValueStore.clear(); + maneuverLossLinesMap.clear(); + for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap.entrySet()) { + e.getValue().removeFromMap(); } + maneuverLossInfoOverlayMap.clear(); + maneuverLossCheckBoxValueStore.clear(); } /** From 5b232953a621f1ce8ed972f347cc3fcb82ae5b7b Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 22:38:36 +0200 Subject: [PATCH 27/53] removed extra blank in "maneuverAngle" string message Change-Id: Id917cae3df9cfb43e1e36b92c04ab7159f19ebf2 --- .../com/sap/sailing/gwt/ui/client/StringMessages.properties | 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/client/StringMessages.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.properties index 1b1f7ff2261..dc6acb71edb 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 @@ -2065,7 +2065,7 @@ multiVideoOffsetInput=Global video offset in milliseconds: multiVideoDescription=The webserver is required to provide index listing (subfolders supported if indexed) to allow file discovery. After an initial analysis of the metadata contained in the mp4 files, the videos that should be added, need to be selected via the checkbox column on the left. The "Add Audio/Video" button will create media tracks for all selected files, and add those files to all races selected in the right column. jibeAngle=Jibe angle tackAngle=Tacking angle -maneuverAngle= Maneuver angle +maneuverAngle=Maneuver angle multiUrlChangeMediaTrack=Adjust multipe media track urls multiUrlChangeReplace=Replace with multiUrlChangeFind=Find From 653f391f03310fd2b93b9fc92b814a3f6f853948 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 22:40:52 +0200 Subject: [PATCH 28/53] added comments to null parameter values Change-Id: Ibbceb30d2ad2007a00eb97b36fbbc5da2b3387ab --- .../impl/ManeuverWithCoarseGrainedBoundariesImpl.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java index e781816192b..ae79943505c 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/ManeuverWithCoarseGrainedBoundariesImpl.java @@ -26,7 +26,9 @@ public class ManeuverWithCoarseGrainedBoundariesImpl extends ManeuverImpl { public ManeuverWithCoarseGrainedBoundariesImpl(ManeuverType type, Tack newTack, Position position, TimePoint timePoint, ManeuverCurveBoundaries maneuverBoundaries) { - super(type, newTack, position, timePoint, maneuverBoundaries, maneuverBoundaries, Math.abs(maneuverBoundaries.getDirectionChangeInDegrees()), null, null); + super(type, newTack, position, timePoint, maneuverBoundaries, maneuverBoundaries, + Math.abs(maneuverBoundaries.getDirectionChangeInDegrees()), /* mark passing */ null, + /* maneuver loss */ null); } @Override From abbf5e6bc816ab174edfad4fdfea498b6a556a3e Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 9 Jul 2018 22:42:55 +0200 Subject: [PATCH 29/53] fixed format of Javadoc header Change-Id: I9ecaec188558886c873a14b614f64e46e29173ea --- .../com/sap/sailing/domain/tracking/ManeuverLoss.java | 9 ++------- 1 file changed, 2 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java index db11f85e6cc..8bdaa808bc9 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/ManeuverLoss.java @@ -7,19 +7,14 @@ import com.sap.sse.common.Bearing; import com.sap.sse.common.Distance; import com.sap.sse.common.Duration; -/** - * - * @author Vladislav Chumak (D069712) - * - */ - /** * Updated ManeuverLoss class to use it to visualize the correct maneuver loss on the race map. To draw the ManeuverLoss * needs the maneuverStartPosition, maneuverEndPosition, middleManeuverAngle, speedWithBearingBefore and * maneuverDuration. The ManeuverLoss is calculated by {@link ManeuverDetectorImpl#getManeuverLoss()}. + * + * @author Vladislav Chumak (D069712) */ public class ManeuverLoss { - private final Distance distanceSailedProjectedOnMiddleManeuverAngle; private final Distance distanceSailedIfNotManeuveringProjectedOnMiddeManeuverAngle; private final Position maneuverStartPosition; From ecac04d30c3818f2ef2522ace312f5703c331384 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Tue, 10 Jul 2018 09:58:15 +0200 Subject: [PATCH 30/53] bug4572: added null-safety in SailingServiceImpl speedWithBearingBeforeManeuverLoss --- .../com/sap/sailing/gwt/ui/server/SailingServiceImpl.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 7c2d018b39c..56ec4202349 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -3632,8 +3632,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final Date markPassingTimePoint = maneuver.isMarkPassing() ? maneuver.getMarkPassing().getTimePoint().asDate() : null; final NauticalSide markPassingSide = maneuver.isMarkPassing() ? maneuver.getToSide() : null; - final SpeedWithBearing speedWithBearingBeforeManeuverLoss = maneuver.getManeuverLoss() == null ? null - : maneuver.getManeuverLoss().getSpeedWithBearingBefore(); + final SpeedWithBearing speedWithBearingBeforeManeuverLoss = (maneuver.getManeuverLoss() == null + || maneuver.getManeuverLoss().getSpeedWithBearingBefore() == null) ? null + : maneuver.getManeuverLoss().getSpeedWithBearingBefore(); final Double middleManeuverAngle = maneuver.getManeuverLoss() == null ? null : maneuver.getManeuverLoss().getMiddleManeuverAngle().getDegrees(); final ManeuverLossDTO maneuverLoss = maneuver.getManeuverLoss() == null ? null : new ManeuverLossDTO(maneuver.getManeuverLoss().getManeuverStartPosition(), From 4a55b4be12456ad51f1961583f4bcdedbfa5a18b Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Tue, 10 Jul 2018 10:11:25 +0200 Subject: [PATCH 31/53] bug4572: Introduced color constants for maneuverloss lines. Used ManeuverColor.getManeuverColor() to define red or green. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 19 ++++++++++++------- 1 file changed, 12 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 06b324e939f..84a8ad59f95 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -169,6 +169,8 @@ public class RaceMap extends AbstractCompositeComponent impleme private static final String ADVANTAGE_LINE_COLOR = "#ff9900"; // orange private static final String FINISH_LINE_COLOR = "#000000"; private static final Color LOWLIGHTED_TAIL_COLOR = new RGBColor(200, 200, 200); + private static final String MANEUVERLOSSLINES_PROJECTEDLINES_COLOR = "#ffff00"; + private static final String MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR = "#ffffff"; public static final String GET_RACE_MAP_DATA_CATEGORY = "getRaceMapData"; public static final String GET_WIND_DATA_CATEGORY = "getWindData"; private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; @@ -3207,18 +3209,21 @@ public class RaceMap extends AbstractCompositeComponent impleme intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - String color = maneuver.getManeuverLoss().getDistanceLost().compareTo(Distance.NULL) > 0 ? color = "#FF0000" : "#00FF00"; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, - middleManeuverAngleLineInfoProvider, "#ffffff", 1, 0.5)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverStartPosition(), - extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, "#ffffff", 1, 0.5)); + middleManeuverAngleLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, 1, 0.5)); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, + maneuver.getManeuverLoss().getManeuverStartPosition(), extrapolatedManeuverStartPosition, + extrapolatedLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, 1, 0.5)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, - projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, "#FFFF00", 1, 0.3)); + projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, + MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, 1, 0.3)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverEndPosition(), - projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, "#FFFF00", 1, 0.3)); + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, + MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, 1, 0.3)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, - projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 2, 1.0)); + projectedManeuverEndPosition, maneuverLossLineInfoProvider, + ManeuverColor.getManeuverColor(maneuver).toString(), 2, 1.0)); Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); maneuverLossLinesMap.put(t, maneuverLossLines); From a080b28ce6b47a48db2ff14a1365ae30f644aafd Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Tue, 10 Jul 2018 10:41:22 +0200 Subject: [PATCH 32/53] bug4572: reverted ManeuverColor.getColor and used constants for Loss and "Win" in maneuver. Added string messages for projected extrapolated maneuver start position and projected maneuver end position. --- .../sap/sailing/gwt/ui/client/StringMessages.java | 2 ++ .../sailing/gwt/ui/client/StringMessages.properties | 2 ++ .../gwt/ui/client/StringMessages_de.properties | 4 +++- .../gwt/ui/client/shared/racemap/RaceMap.java | 13 ++++++++----- 4 files changed, 15 insertions(+), 6 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 91d326af5db..7c89c6b209f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2084,6 +2084,8 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String teaserOverallLinkToolTip(); String middleManeuverAngle(); String extrapolatedManeuverStartPosition(); + String projectedExtrapolatedManeuverStartPosition(); + String projectedManeuverEndPosition(); String gender(); String manage2SailEventIdBox(); String manage2SailEventURLBox(); 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 dc6acb71edb..ac2e5efed49 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 @@ -2079,6 +2079,8 @@ lastEvent=Last Event: {0} teaserOverallLinkToolTip=To see the overall series, click the yellow corner middleManeuverAngle=Middle maneuver angle extrapolatedManeuverStartPosition=Extrapolated Maneuver Start Position +projectedExtrapolatedManeuverStartPosition=Projection of the extrapolated maneuver start position on middle maneuver angle line +projectedManeuverEndPosition=Projection of the maneuver end position on middle maneuver angle line gender=Gender manage2SailEventIdBox=Manage2Sail Event ID manage2SailEventURLBox=Manage2Sail EventURL (json) 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 046fe7993e3..8bf0d72999f 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 @@ -2072,8 +2072,10 @@ multiUrlChangeExplain=Dieser Dialog ermöglicht es, mehrere URL-Anfänge gleichz resetZoom=Reset zoom lastEvent=Letztes Event: {0} teaserOverallLinkToolTip=Um die Serien-Übersicht zu sehen, in die gelbe Ecke klicken -middleManeuverAngle=Manöver-Winkelhalbierende +middleManeuverAngle=Manöverwinkel-Halbierende extrapolatedManeuverStartPosition=Extrapolierte Manöver Anfangsposition +projectedExtrapolatedManeuverStartPosition=Projektion der extrapolierten Manöver Startposition auf die Manöverwinkel-Halbierende +projectedManeuverEndPosition=Projektion der Manöverendposition auf die Manöverwinkel-Halbierende gender=Geschlecht manage2SailEventId=Manage2Sail Event ID manage2SailEventURL=Manage2Sail Event URL (json) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 84a8ad59f95..2b3617a5968 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -171,6 +171,8 @@ public class RaceMap extends AbstractCompositeComponent impleme private static final Color LOWLIGHTED_TAIL_COLOR = new RGBColor(200, 200, 200); private static final String MANEUVERLOSSLINES_PROJECTEDLINES_COLOR = "#ffff00"; private static final String MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR = "#ffffff"; + private static final String MANEUVERLOSSLINES_RED = "#ff0000"; + private static final String MANEUVERLOSSLINES_GREEN = "#00ff00"; public static final String GET_RACE_MAP_DATA_CATEGORY = "getRaceMapData"; public static final String GET_WIND_DATA_CATEGORY = "getWindData"; private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; @@ -3136,13 +3138,13 @@ public class RaceMap extends AbstractCompositeComponent impleme final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return ""; + return stringMessages.projectedExtrapolatedManeuverStartPosition(); } }; final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return ""; + return stringMessages.projectedManeuverEndPosition(); } }; final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { @@ -3208,7 +3210,9 @@ public class RaceMap extends AbstractCompositeComponent impleme Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + String color = maneuver.getManeuverLoss().getDistanceLost().compareTo(Distance.NULL) > 0 + ? color = MANEUVERLOSSLINES_RED : MANEUVERLOSSLINES_GREEN; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, 1, 0.5)); @@ -3222,8 +3226,7 @@ public class RaceMap extends AbstractCompositeComponent impleme projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, 1, 0.3)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, - projectedManeuverEndPosition, maneuverLossLineInfoProvider, - ManeuverColor.getManeuverColor(maneuver).toString(), 2, 1.0)); + projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 2, 1.0)); Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); maneuverLossLinesMap.put(t, maneuverLossLines); From ac0fc23ea834311d4250fa12e7a26f51ca179275 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Tue, 10 Jul 2018 12:08:05 +0200 Subject: [PATCH 33/53] bug4572: Added a showManeuverLossLinesVisualization in RaceMapSettings. Made it default true. --- .../startanalysis/card/StartAnalysisCard.java | 2 +- .../autoplay/client/utils/AutoplayHelper.java | 2 +- .../shared/racemap/CombinedWindPanel.java | 2 +- .../gwt/ui/client/shared/racemap/RaceMap.java | 1468 +++++++++-------- .../shared/racemap/RaceMapLifecycle.java | 4 +- .../shared/racemap/RaceMapSettings.java | 24 +- .../RaceMapSettingsDialogComponent.java | 11 +- .../racemap/TrueNorthIndicatorPanel.java | 2 +- .../EmbeddedMapAndWindChartEntryPoint.java | 2 +- .../gwt/ui/raceboard/StartAnalysisMode.java | 3 +- .../gwt/ui/raceboard/WinningLanesMode.java | 3 +- 11 files changed, 857 insertions(+), 666 deletions(-) diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java index 66858859563..497dc402680 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java @@ -163,7 +163,7 @@ public class StartAnalysisCard extends Composite implements HasWidgets, StartAna defaultRaceMapSettings.isShowWindStreamletOverlay(), defaultRaceMapSettings.isShowSimulationOverlay(), defaultRaceMapSettings.isShowMapControls(), defaultRaceMapSettings.getManeuverTypesToShow(), defaultRaceMapSettings.isShowDouglasPeuckerPoints(), defaultRaceMapSettings.isShowEstimatedDuration(), - defaultRaceMapSettings.getStartCountDownFontSizeScaling()); + defaultRaceMapSettings.getStartCountDownFontSizeScaling(), defaultRaceMapSettings.isShowManeuverLossVisualization()); RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingServiceAsync, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java index bd37fc96661..2525da31353 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java @@ -284,7 +284,7 @@ public class AutoplayHelper { RaceMapZoomSettings autoFollowRace = new RaceMapZoomSettings(typesToConsiderOnZoom, true); RaceMapSettings settings = new RaceMapSettings(autoFollowRace, new RaceMapHelpLinesSettings(), false, 15, 100000l, false, RaceMapSettings.DEFAULT_BUOY_ZONE_RADIUS, false, true, false, false, false, false, - RaceMapSettings.getDefaultManeuvers(), false, false, /* startCountDownFontSizeScaling */ 1.5); + RaceMapSettings.getDefaultManeuvers(), false, false, /* startCountDownFontSizeScaling */ 1.5, false); RaceMapLifecycle raceMapLifecycle = new RaceMapLifecycle(StringMessages.INSTANCE); final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(currentLiveRace, competitorsAndTheirBoats); RaceCompetitorSelectionModel competitorSelectionProvider = new RaceCompetitorSelectionModel( diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java index 9d91e40a447..50cbefb7269 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java @@ -54,7 +54,7 @@ public class CombinedWindPanel extends FlowPanel { newShowStreamletsOverlaySetting, oldRaceMapSettings.isShowSimulationOverlay(), oldRaceMapSettings.isShowMapControls(), oldRaceMapSettings.getManeuverTypesToShow(), oldRaceMapSettings.isShowDouglasPeuckerPoints(), oldRaceMapSettings.isShowEstimatedDuration(), - oldRaceMapSettings.getStartCountDownFontSizeScaling()); + oldRaceMapSettings.getStartCountDownFontSizeScaling(), oldRaceMapSettings.isShowManeuverLossVisualization()); map.updateSettings(newRaceMapSettings); } }); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 2b3617a5968..7b46093cdb9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -164,8 +164,9 @@ import com.sap.sse.gwt.client.shared.components.SettingsDialog; import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; import com.sap.sse.gwt.client.shared.settings.ComponentContext; -public class RaceMap extends AbstractCompositeComponent implements TimeListener, CompetitorSelectionChangeListener, - RaceTimesInfoProviderListener, TailFactory, RequiresDataInitialization, RequiresResize, QuickRankProvider { +public class RaceMap extends AbstractCompositeComponent + implements TimeListener, CompetitorSelectionChangeListener, RaceTimesInfoProviderListener, TailFactory, + RequiresDataInitialization, RequiresResize, QuickRankProvider { private static final String ADVANTAGE_LINE_COLOR = "#ff9900"; // orange private static final String FINISH_LINE_COLOR = "#000000"; private static final Color LOWLIGHTED_TAIL_COLOR = new RGBColor(200, 200, 200); @@ -176,22 +177,22 @@ public class RaceMap extends AbstractCompositeComponent impleme public static final String GET_RACE_MAP_DATA_CATEGORY = "getRaceMapData"; public static final String GET_WIND_DATA_CATEGORY = "getWindData"; private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; - + private static final String COMPACT_HEADER_STYLE = "compactHeader"; public static final Color WATER_COLOR = new RGBColor(0, 67, 125); - + private AbsolutePanel rootPanel = new AbsolutePanel(); - + private MapWidget map; - + /** * Always valid, non-null. Must be used to map all coordinates, headings, bearings, and directions * displayed on the map, including the orientations of any canvases such as boat icons, wind displays etc. that are - * embedded in the map. The coordinate systems facilitates the possibility of transformed displays such as - * rotated and translated versions of the map, implementing the "wind-up" view. + * embedded in the map. The coordinate systems facilitates the possibility of transformed displays such as rotated + * and translated versions of the map, implementing the "wind-up" view. */ private DelegateCoordinateSystem coordinateSystem; - + private FlowPanel headerPanel; private AbsolutePanel panelForLeftHeaderLabels; private AbsolutePanel panelForRightHeaderLabels; @@ -212,17 +213,18 @@ public class RaceMap extends AbstractCompositeComponent impleme private Polyline finishLine; /** - * Polyline for the advantage line (the leading line for the boats, orthogonal to the wind direction; touching the leading boat). + * Polyline for the advantage line (the leading line for the boats, orthogonal to the wind direction; touching the + * leading boat). */ private Polyline advantageLine; - + private AdvantageLineAnimator advantageTimer; - + /** * The windward of two Polylines representing a triangle between startline and first mark. */ private Polyline windwardStartLineMarkToFirstMarkLine; - + /** * The leeward of two Polylines representing a triangle between startline and first mark. */ @@ -231,12 +233,12 @@ public class RaceMap extends AbstractCompositeComponent impleme private class AdvantageLineMouseOverMapHandler implements MouseOverMapHandler { private double trueWindAngle; private Date date; - + public AdvantageLineMouseOverMapHandler(double trueWindAngle, Date date) { this.trueWindAngle = trueWindAngle; this.date = date; } - + public void setTrueWindBearing(double trueWindAngle) { this.trueWindAngle = trueWindAngle; } @@ -247,42 +249,43 @@ public class RaceMap extends AbstractCompositeComponent impleme @Override public void onEvent(MouseOverMapEvent event) { - map.setTitle(stringMessages.advantageLine()+" (from "+new DegreeBearingImpl(Math.round(trueWindAngle)).reverse().getDegrees()+"deg"+ - (date == null ? ")" : ", "+ date) + ")"); + map.setTitle(stringMessages.advantageLine() + " (from " + + new DegreeBearingImpl(Math.round(trueWindAngle)).reverse().getDegrees() + "deg" + + (date == null ? ")" : ", " + date) + ")"); } }; - + private AdvantageLineMouseOverMapHandler advantageLineMouseOverHandler; - + /** * Polylines for the course middle lines; keys are the two control points delimiting the leg for which the - * {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there - * are multiple legs using the same control points in different directions, using a {@link Set} makes this - * independent of the order of the two control points. If no course middle line is currently being shown for - * a pair of control points, the map will not contain a value for this pair. + * {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there are + * multiple legs using the same control points in different directions, using a {@link Set} makes this independent + * of the order of the two control points. If no course middle line is currently being shown for a pair of control + * points, the map will not contain a value for this pair. */ private final Map, Polyline> courseMiddleLines; private final Map courseSidelines; - + /** - * When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the - * lines that show the tooltip text in a little info box linked to the line. When the line is removed by + * When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the lines + * that show the tooltip text in a little info box linked to the line. When the line is removed by * {@link #showOrRemoveOrUpdateLine(Polyline, boolean, Position, Position, LineInfoProvider, String)}, these - * overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is - * deactivated, all these overlays need to go. + * overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is deactivated, + * all these overlays need to go. */ private final Map infoOverlaysForLinesForCourseGeometry; - + /** * Wind data used to display the advantage line. Retrieved by a {@link GetWindInfoAction} execution and used in * {@link #showAdvantageLine(Iterable, Date)}. */ private WindInfoForRaceDTO lastCombinedWindTrackInfoDTO; - + /** - * Manages the cached set of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the boat positions as well as their graphical counterpart in the - * form of {@link Polyline}s. + * Manages the cached set of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the boat positions as well as their + * graphical counterpart in the form of {@link Polyline}s. */ private final FixesAndTails fixesAndTails; @@ -295,7 +298,7 @@ public class RaceMap extends AbstractCompositeComponent impleme * html5 canvases used for competitor info display on the map */ private final CompetitorInfoOverlays competitorInfoOverlays; - + private SmallTransparentInfoOverlay countDownOverlay; /** @@ -308,11 +311,12 @@ public class RaceMap extends AbstractCompositeComponent impleme * html5 canvas used to display course marks including buoy zones */ private final Map courseMarkOverlays; - + private final Map courseMarkClickHandlers; /** - * Maps from the {@link MarkDTO#getIdAsString() mark's ID converted to a string} to the corresponding {@link MarkDTO} + * Maps from the {@link MarkDTO#getIdAsString() mark's ID converted to a string} to the corresponding + * {@link MarkDTO} */ private final Map markDTOs; @@ -331,9 +335,9 @@ public class RaceMap extends AbstractCompositeComponent impleme private Map> lastManeuverResult; private Map> lastDouglasPeuckerResult; - + private final RaceCompetitorSelectionProvider competitorSelection; - + private final RaceCompetitorSet raceCompetitorSet; /** @@ -344,7 +348,7 @@ public class RaceMap extends AbstractCompositeComponent impleme private final Timer timer; private RaceTimesInfoDTO lastRaceTimesInfo; - + /** * 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, @@ -361,81 +365,83 @@ public class RaceMap extends AbstractCompositeComponent impleme */ private int startedProcessingRequestID; - private RaceMapImageManager raceMapImageManager; + private RaceMapImageManager raceMapImageManager; private RaceMapSettings settings; private final RaceMapLifecycle raceMapLifecycle; - + private final StringMessages stringMessages; - + private boolean isMapInitialized; private Date lastTimeChangeBeforeInitialization; - + private int lastLegNumber; /** * The strategy for maintaining and delivering the "quick ranks" information. The provider will be informed about - * quick ranks received from a {@link RaceMapDataDTO#quickRanks} field but may choose to ignore this information, e.g., - * if it can assume that more current information about ranks and leg numbers is available from a {@link LeaderboardDTO}. + * quick ranks received from a {@link RaceMapDataDTO#quickRanks} field but may choose to ignore this information, + * e.g., if it can assume that more current information about ranks and leg numbers is available from a + * {@link LeaderboardDTO}. */ private QuickRanksDTOProvider quickRanksDTOProvider; - + private final CombinedWindPanel combinedWindPanel; private final TrueNorthIndicatorPanel trueNorthIndicatorPanel; private final FlowPanel topLeftControlsWrapperPanel; - + private final AsyncActionsExecutor asyncActionsExecutor; /** - * The map bounds as last received by map callbacks; used to determine whether to suppress the boat animation during zoom/pan + * The map bounds as last received by map callbacks; used to determine whether to suppress the boat animation during + * zoom/pan */ private LatLngBounds currentMapBounds; // bounds to which bounds-changed-handler compares - private int currentZoomLevel; // zoom-level to which bounds-changed-handler compares - - private boolean autoZoomIn = false; // flags auto-zoom-in in progress + private int currentZoomLevel; // zoom-level to which bounds-changed-handler compares + + private boolean autoZoomIn = false; // flags auto-zoom-in in progress private boolean autoZoomOut = false; // flags auto-zoom-out in progress - private int autoZoomLevel; // zoom-level to which auto-zoom-in/-out is zooming - LatLngBounds autoZoomLatLngBounds; // bounds to which auto-zoom-in/-out is panning&zooming - + private int autoZoomLevel; // zoom-level to which auto-zoom-in/-out is zooming + LatLngBounds autoZoomLatLngBounds; // bounds to which auto-zoom-in/-out is panning&zooming + private RaceSimulationOverlay simulationOverlay; private WindStreamletsRaceboardOverlay streamletOverlay; - + private final boolean isSimulationEnabled; - + private static final String GET_POLAR_CATEGORY = "getPolar"; - + /** * Tells about the availability of polar / VPP data for this race. If available, the simulation feature can be * offered to the user. */ private boolean hasPolar; - + private final RegattaAndRaceIdentifier raceIdentifier; - + /** - * When the user requests wind-up display this may happen at a point where no mark positions are known or when - * no wind direction is known yet. In this case, this flag will be set, and when wind information or course mark + * When the user requests wind-up display this may happen at a point where no mark positions are known or when no + * wind direction is known yet. In this case, this flag will be set, and when wind information or course mark * positions are received later, this flag is checked, and if set, a {@link #updateCoordinateSystemFromSettings()} * call is issued to make sure that the user's request for a new coordinate system is honored. */ private boolean requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown; - + /** - * Tells whether currently an auto-zoom is in progress; this is used particularly to keep the smooth CSS boat transitions - * active while auto-zooming whereas stopping them seems the better option for manual zooms. + * Tells whether currently an auto-zoom is in progress; this is used particularly to keep the smooth CSS boat + * transitions active while auto-zooming whereas stopping them seems the better option for manual zooms. */ private boolean autoZoomInProgress; - + /** - * Tells whether currently an orientation change is in progress; this is required handle map events during the configuration of the map - * during an orientation change. + * Tells whether currently an orientation change is in progress; this is required handle map events during the + * configuration of the map during an orientation change. */ private boolean orientationChangeInProgress; - + private final NumberFormat numberFormatOneDecimal = NumberFormat.getFormat("0.0"); private final NumberFormat numberFormatNoDecimal = NumberFormat.getFormat("0"); - + /** * The competitor for which the advantage line is currently showing. Should this competitor's quick rank change, or * should ranks be received while this field is {@code null}, the advantage line @@ -445,48 +451,53 @@ public class RaceMap extends AbstractCompositeComponent impleme protected Label estimatedDurationOverlay; private RaceMapStyle raceMapStyle; private final boolean showHeaderPanel; - + /** Callback to set the visibility of the wind chart. */ private final Consumer showWindChartForProvider; private ManagedInfoWindow managedInfoWindow; - - /** The Map where for each competitor and each maneuver is stored if it is visualized or not.*/ + + /** The Map where for each competitor and each maneuver is stored if it is visualized or not. */ private Set> maneuverLossCheckBoxValueStore = new HashSet<>(); - - /** The Map where the polylines for the specific maneuver are stored.*/ + + /** The Map where the polylines for the specific maneuver are stored. */ private Map, Set> maneuverLossLinesMap = new HashMap<>(); - - /** The Map where the info overlays for a maneuver loss visualization are stored.*/ + + /** The Map where the info overlays for a maneuver loss visualization are stored. */ private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); private class AdvantageLineUpdater implements QuickRanksListener { @Override public void rankChanged(String competitorIdAsString, QuickRankDTO oldQuickRank, QuickRankDTO quickRank) { - if (advantageLineCompetitor == null || - (oldQuickRank != null && advantageLineCompetitor.getIdAsString().equals(oldQuickRank.competitor.getIdAsString())) || - (quickRank != null && advantageLineCompetitor.getIdAsString().equals(quickRank.competitor.getIdAsString()))) { - showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), /* timeForPositionTransitionMillis */ -1 /* (no transition) */); + if (advantageLineCompetitor == null + || (oldQuickRank != null + && advantageLineCompetitor.getIdAsString().equals(oldQuickRank.competitor.getIdAsString())) + || (quickRank != null + && advantageLineCompetitor.getIdAsString().equals(quickRank.competitor.getIdAsString()))) { + showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), + /* timeForPositionTransitionMillis */ -1 /* (no transition) */); } } } - + public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, - RaceMapSettings raceMapSettings, - SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, - ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, - RaceCompetitorSet raceCompetitorSet, StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, - RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider) { + RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, + AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer timer, + RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, + StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, + boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider) { this(parent, context, raceMapLifecycle, raceMapSettings, sailingService, asyncActionsExecutor, errorReporter, timer, competitorSelection, raceCompetitorSet, stringMessages, raceIdentifier, raceMapResources, - showHeaderPanel, quickRanksDTOProvider, visible -> {}); + showHeaderPanel, quickRanksDTOProvider, visible -> { + }); } - + public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, - RaceMapSettings raceMapSettings, - SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, - ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, - StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, - RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, Consumer showWindChartForProvider) { + RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, + AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer timer, + RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, + StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, + boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, + Consumer showWindChartForProvider) { super(parent, context); this.showHeaderPanel = showHeaderPanel; this.quickRanksDTOProvider = quickRanksDTOProvider; @@ -529,9 +540,11 @@ public class RaceMap extends AbstractCompositeComponent impleme initializeData(settings.isShowMapControls(), showHeaderPanel); raceMapStyle = raceMapResources.raceMapStyle(); raceMapStyle.ensureInjected(); - combinedWindPanel = new CombinedWindPanel(this, raceMapImageManager, raceMapStyle, stringMessages, coordinateSystem); + combinedWindPanel = new CombinedWindPanel(this, raceMapImageManager, raceMapStyle, stringMessages, + coordinateSystem); combinedWindPanel.setVisible(false); - trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, raceMapStyle, stringMessages, coordinateSystem); + trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, raceMapStyle, stringMessages, + coordinateSystem); trueNorthIndicatorPanel.setVisible(true); topLeftControlsWrapperPanel = new FlowPanel(); topLeftControlsWrapperPanel.add(combinedWindPanel); @@ -542,20 +555,22 @@ public class RaceMap extends AbstractCompositeComponent impleme initWidget(rootPanel); this.setSize("100%", "100%"); } - + public void setQuickRanksDTOProvider(QuickRanksDTOProvider newQuickRanksDTOProvider) { if (this.quickRanksDTOProvider != null) { this.quickRanksDTOProvider.moveListernersTo(newQuickRanksDTOProvider); } this.quickRanksDTOProvider = newQuickRanksDTOProvider; } + /** * The {@link WindDTO#dampenedTrueWindFromDeg} direction if {@link #lastCombinedWindTrackInfoDTO} has a * {@link WindSourceType#COMBINED} source which has at least one fix recorded; null otherwise. */ private Bearing getLastCombinedTrueWindFromDirection() { if (lastCombinedWindTrackInfoDTO != null) { - for (Entry e : lastCombinedWindTrackInfoDTO.windTrackInfoByWindSource.entrySet()) { + for (Entry e : lastCombinedWindTrackInfoDTO.windTrackInfoByWindSource + .entrySet()) { if (e.getKey().getType() == WindSourceType.COMBINED) { final List windFixes = e.getValue().windFixes; if (!windFixes.isEmpty()) { @@ -566,7 +581,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } return null; } - + /** * @return {@code true} if the map was redrawn by the call to this method */ @@ -579,7 +594,8 @@ public class RaceMap extends AbstractCompositeComponent impleme if (centerOfCourse != null) { final Bearing lastCombinedTrueWindFromDirection = getLastCombinedTrueWindFromDirection(); if (lastCombinedTrueWindFromDirection != null) { - // new equator shall point 90deg right of the "from" wind direction to make wind come from top of map + // new equator shall point 90deg right of the "from" wind direction to make wind come from top of + // map coordinateSystem.setCoordinateSystem(new RotateAndTranslateCoordinateSystem(centerOfCourse, lastCombinedTrueWindFromDirection.add(new DegreeBearingImpl(90)))); if (map != null) { @@ -606,17 +622,19 @@ public class RaceMap extends AbstractCompositeComponent impleme } coordinateSystem.setCoordinateSystem(new IdentityCoordinateSystem()); } - if (mapOptions != null) { // if no coordinate system change happened that affects an existing map, don't redraw + if (mapOptions != null) { // if no coordinate system change happened that affects an existing map, don't redraw fixesAndTails.clearTails(); redraw(); redrawn = true; - // zooming and setting options while the event loop is still working doesn't work reliably; defer until event loop returns + // zooming and setting options while the event loop is still working doesn't work reliably; defer until + // event loop returns Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { if (map != null) { map.setOptions(mapOptions); - // ensure zooming to what the settings tell, or defaults if what the settings tell isn't possible right now + // ensure zooming to what the settings tell, or defaults if what the settings tell isn't + // possible right now mapFirstZoomDone = false; trueNorthIndicatorPanel.redraw(); orientationChangeInProgress = false; @@ -636,116 +654,122 @@ public class RaceMap extends AbstractCompositeComponent impleme loadLibraries.add(LoadLibrary.GEOMETRY); Runnable onLoad = new Runnable() { - @Override - public void run() { - MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false); - map = new MapWidget(mapOptions); - rootPanel.add(map, 0, 0); - if (showHeaderPanel) { - Image sapLogo = createSAPLogo(); - rootPanel.add(sapLogo); - } - - map.setControls(ControlPosition.LEFT_TOP, topLeftControlsWrapperPanel); - adjustLeftControlsIndent(); + @Override + public void run() { + MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false); + map = new MapWidget(mapOptions); + rootPanel.add(map, 0, 0); + if (showHeaderPanel) { + Image sapLogo = createSAPLogo(); + rootPanel.add(sapLogo); + } - RaceMap.this.raceMapImageManager.loadMapIcons(map); - map.setSize("100%", "100%"); - map.addZoomChangeHandler(new ZoomChangeMapHandler() { - @Override - public void onEvent(ZoomChangeMapEvent event) { - if (!autoZoomIn && !autoZoomOut && !orientationChangeInProgress) { - // stop automatic zoom after a manual zoom event; automatic zoom in zoomMapToNewBounds will restore old settings - final List emptyList = Collections.emptyList(); - RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, settings.getZoomSettings().isZoomToSelectedCompetitors()); - settings = new RaceMapSettings(settings, clearedZoomSettings); - } - // TODO bug489 when in wind-up mode, avoid zooming out too far; perhaps zoom back in if zoomed out too far - } - }); - map.addDragEndHandler(new DragEndMapHandler() { - @Override - public void onEvent(DragEndMapEvent event) { - // stop automatic zoom after a manual drag event - autoZoomIn = false; - autoZoomOut = false; - final List emptyList = Collections.emptyList(); - RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, settings.getZoomSettings().isZoomToSelectedCompetitors()); - settings = new RaceMapSettings(settings, clearedZoomSettings); - } - }); - map.addIdleHandler(new IdleMapHandler() { - @Override - public void onEvent(IdleMapEvent event) { - // the "idle"-event is raised at the end of map-animations - if (autoZoomIn) { - // finalize zoom-in that was started with panTo() in zoomMapToNewBounds() - map.setZoom(autoZoomLevel); - autoZoomIn = false; - } - if (autoZoomOut) { - // finalize zoom-out that was started with setZoom() in zoomMapToNewBounds() - map.panTo(autoZoomLatLngBounds.getCenter()); - autoZoomOut = false; - } - } - }); - map.addBoundsChangeHandler(new BoundsChangeMapHandler() { - @Override - public void onEvent(BoundsChangeMapEvent event) { - int newZoomLevel = map.getZoom(); - if (!isAutoZoomInProgress() && (newZoomLevel != currentZoomLevel)) { - removeTransitions(); - } - if ((streamletOverlay != null) && !map.getBounds().equals(currentMapBounds)) { - streamletOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); - } - if ((simulationOverlay != null) && !map.getBounds().equals(currentMapBounds)) { - simulationOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); - } - currentMapBounds = map.getBounds(); - currentZoomLevel = newZoomLevel; - headerPanel.getElement().getStyle().setWidth(map.getOffsetWidth(), Unit.PX); - } - }); - - // If there was a time change before the API was loaded, reset the time - if (lastTimeChangeBeforeInitialization != null) { - timeChanged(lastTimeChangeBeforeInitialization, null); - lastTimeChangeBeforeInitialization = null; - } - // Initialize streamlet canvas for wind visualization; it shouldn't be doing anything unless it's visible - streamletOverlay = new WindStreamletsRaceboardOverlay(getMap(), /* zIndex */ 0, - timer, raceIdentifier, sailingService, asyncActionsExecutor, stringMessages, coordinateSystem); - streamletOverlay.addToMap(); - if (settings.isShowWindStreamletOverlay()) { - streamletOverlay.setColors(settings.isShowWindStreamletColors()); - streamletOverlay.setVisible(true); - } + map.setControls(ControlPosition.LEFT_TOP, topLeftControlsWrapperPanel); + adjustLeftControlsIndent(); - if (isSimulationEnabled) { - // determine availability of polar diagram - setHasPolar(); - // initialize simulation canvas - simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, sailingService, stringMessages, asyncActionsExecutor, coordinateSystem); - simulationOverlay.addToMap(); - showSimulationOverlay(settings.isShowSimulationOverlay()); - } - if (showHeaderPanel) { - createHeaderPanel(map); - } - if (showMapControls) { - createSettingsButton(map); - } - // Data has been initialized - RaceMap.this.isMapInitialized = true; - RaceMap.this.redraw(); - trueNorthIndicatorPanel.redraw(); - showAdditionalControls(map); - RaceMap.this.managedInfoWindow = new ManagedInfoWindow(map); - } + RaceMap.this.raceMapImageManager.loadMapIcons(map); + map.setSize("100%", "100%"); + map.addZoomChangeHandler(new ZoomChangeMapHandler() { + @Override + public void onEvent(ZoomChangeMapEvent event) { + if (!autoZoomIn && !autoZoomOut && !orientationChangeInProgress) { + // stop automatic zoom after a manual zoom event; automatic zoom in zoomMapToNewBounds will + // restore old settings + final List emptyList = Collections.emptyList(); + RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, + settings.getZoomSettings().isZoomToSelectedCompetitors()); + settings = new RaceMapSettings(settings, clearedZoomSettings); + } + // TODO bug489 when in wind-up mode, avoid zooming out too far; perhaps zoom back in if zoomed + // out too far + } + }); + map.addDragEndHandler(new DragEndMapHandler() { + @Override + public void onEvent(DragEndMapEvent event) { + // stop automatic zoom after a manual drag event + autoZoomIn = false; + autoZoomOut = false; + final List emptyList = Collections.emptyList(); + RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, + settings.getZoomSettings().isZoomToSelectedCompetitors()); + settings = new RaceMapSettings(settings, clearedZoomSettings); + } + }); + map.addIdleHandler(new IdleMapHandler() { + @Override + public void onEvent(IdleMapEvent event) { + // the "idle"-event is raised at the end of map-animations + if (autoZoomIn) { + // finalize zoom-in that was started with panTo() in zoomMapToNewBounds() + map.setZoom(autoZoomLevel); + autoZoomIn = false; + } + if (autoZoomOut) { + // finalize zoom-out that was started with setZoom() in zoomMapToNewBounds() + map.panTo(autoZoomLatLngBounds.getCenter()); + autoZoomOut = false; + } + } + }); + map.addBoundsChangeHandler(new BoundsChangeMapHandler() { + @Override + public void onEvent(BoundsChangeMapEvent event) { + int newZoomLevel = map.getZoom(); + if (!isAutoZoomInProgress() && (newZoomLevel != currentZoomLevel)) { + removeTransitions(); + } + if ((streamletOverlay != null) && !map.getBounds().equals(currentMapBounds)) { + streamletOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); + } + if ((simulationOverlay != null) && !map.getBounds().equals(currentMapBounds)) { + simulationOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); + } + currentMapBounds = map.getBounds(); + currentZoomLevel = newZoomLevel; + headerPanel.getElement().getStyle().setWidth(map.getOffsetWidth(), Unit.PX); + } + }); + + // If there was a time change before the API was loaded, reset the time + if (lastTimeChangeBeforeInitialization != null) { + timeChanged(lastTimeChangeBeforeInitialization, null); + lastTimeChangeBeforeInitialization = null; + } + // Initialize streamlet canvas for wind visualization; it shouldn't be doing anything unless it's + // visible + streamletOverlay = new WindStreamletsRaceboardOverlay(getMap(), /* zIndex */ 0, timer, raceIdentifier, + sailingService, asyncActionsExecutor, stringMessages, coordinateSystem); + streamletOverlay.addToMap(); + if (settings.isShowWindStreamletOverlay()) { + streamletOverlay.setColors(settings.isShowWindStreamletColors()); + streamletOverlay.setVisible(true); + } + + if (isSimulationEnabled) { + // determine availability of polar diagram + setHasPolar(); + // initialize simulation canvas + simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, + sailingService, stringMessages, asyncActionsExecutor, coordinateSystem); + simulationOverlay.addToMap(); + showSimulationOverlay(settings.isShowSimulationOverlay()); + } + if (showHeaderPanel) { + createHeaderPanel(map); + } + if (showMapControls) { + createSettingsButton(map); + } + // Data has been initialized + RaceMap.this.isMapInitialized = true; + RaceMap.this.redraw(); + trueNorthIndicatorPanel.redraw(); + showAdditionalControls(map); + RaceMap.this.managedInfoWindow = new ManagedInfoWindow(map); + } }; - LoadApi.go(onLoad, loadLibraries, sensor, GoogleMapAPIKey.V3_PARAMS); + LoadApi.go(onLoad, loadLibraries, sensor, GoogleMapAPIKey.V3_PARAMS); } /** @@ -774,14 +798,13 @@ public class RaceMap extends AbstractCompositeComponent impleme } /** - * Creates a header panel where additional information can be displayed by using - * {@link #getLeftHeaderPanel()} or {@link #getRightHeaderPanel()}. + * Creates a header panel where additional information can be displayed by using {@link #getLeftHeaderPanel()} or + * {@link #getRightHeaderPanel()}. * - * This panel is transparent and configured in such a way that it moves other controls - * down by its height. To achieve the goal of not having added widgets transparent - * this widget consists of two parts: First one is the transparent panel and the - * second one is the panel for the controls. The controls then need to moved onto - * the panel by using CSS. + * This panel is transparent and configured in such a way that it moves other controls down by its height. To + * achieve the goal of not having added widgets transparent this widget consists of two parts: First one is the + * transparent panel and the second one is the panel for the controls. The controls then need to moved onto the + * panel by using CSS. */ private void createHeaderPanel(MapWidget map) { // we need a panel that does not have any transparency to have the @@ -801,7 +824,7 @@ public class RaceMap extends AbstractCompositeComponent impleme // controls at RIGHT would not get the correct top setting map.setControls(ControlPosition.TOP_RIGHT, headerPanel); } - + private void createSettingsButton(MapWidget map) { final Component component = this; Button settingsButton = new Button(); @@ -838,11 +861,11 @@ public class RaceMap extends AbstractCompositeComponent impleme public void redraw() { timeChanged(timer.getTime(), null); } - + Map getBoatOverlays() { return Collections.unmodifiableMap(boatOverlays); } - + protected RaceCompetitorSelectionProvider getCompetitorSelection() { return competitorSelection; } @@ -858,33 +881,36 @@ public class RaceMap extends AbstractCompositeComponent impleme public MapWidget getMap() { return map; } - + public RaceSimulationOverlay getSimulationOverlay() { return simulationOverlay; } - + /** * @return the Panel where labels or other controls for the header can be positioned */ public AbsolutePanel getLeftHeaderPanel() { return panelForLeftHeaderLabels; } - + public AbsolutePanel getRightHeaderPanel() { return panelForRightHeaderLabels; } - + @Override - public void raceTimesInfosReceived(Map raceTimesInfos, long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { - timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived); + public void raceTimesInfosReceived(Map raceTimesInfos, + long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { + timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, + clientTimeWhenResponseWasReceived); this.lastRaceTimesInfo = raceTimesInfos.get(raceIdentifier); } /** - * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard contents}, null - * is used as time point. The condition for this is encapsulated in this method so others can find out. For example, when a time change - * is signaled due to local offset / delay adjustments, no additional call to {@link #loadCompleteLeaderboard(Date)} would be required - * as null will be passed in any case, not being affected by local time offsets. + * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard + * contents}, null is used as time point. The condition for this is encapsulated in this method so + * others can find out. For example, when a time change is signaled due to local offset / delay adjustments, no + * additional call to {@link #loadCompleteLeaderboard(Date)} would be required as null will be passed + * in any case, not being affected by local time offsets. */ private boolean useNullAsTimePoint() { return timer.getPlayMode() == PlayModes.Live; @@ -898,57 +924,66 @@ public class RaceMap extends AbstractCompositeComponent impleme final Iterable competitorsToShow = getCompetitorsToShow(); if (race != null) { final long transitionTimeInMillis = calculateTimeForPositionTransitionInMillis(newTime, oldTime); - final com.sap.sse.common.Util.Triple, Map, Map> fromAndToAndOverlap = - fixesAndTails.computeFromAndTo(newTime, competitorsToShow, settings.getEffectiveTailLengthInMilliseconds()); + final com.sap.sse.common.Util.Triple, Map, Map> fromAndToAndOverlap = fixesAndTails + .computeFromAndTo(newTime, competitorsToShow, + settings.getEffectiveTailLengthInMilliseconds()); // Request map data update, possibly in two calls; see method details - callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers(fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow); + callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers( + fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow); // draw the wind into the map, get the combined wind List windSourceTypeNames = new ArrayList(); windSourceTypeNames.add(WindSourceType.EXPEDITION.name()); windSourceTypeNames.add(WindSourceType.WINDFINDER.name()); windSourceTypeNames.add(WindSourceType.COMBINED.name()); - GetWindInfoAction getWindInfoAction = new GetWindInfoAction(sailingService, race, newTime, 1000L, 1, windSourceTypeNames, + GetWindInfoAction getWindInfoAction = new GetWindInfoAction(sailingService, race, newTime, 1000L, 1, + windSourceTypeNames, /* onlyUpToNewestEvent==false means get us any data we can get by a best effort */ false); - asyncActionsExecutor.execute(getWindInfoAction, GET_WIND_DATA_CATEGORY, new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining wind information: " + caught.getMessage(), true /*silentMode */); - } - - @Override - public void onSuccess(WindInfoForRaceDTO windInfo) { - List> windSourcesToShow = new ArrayList>(); - if (windInfo != null) { - lastCombinedWindTrackInfoDTO = windInfo; - showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); - for (WindSource windSource : windInfo.windTrackInfoByWindSource.keySet()) { - WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource.get(windSource); - switch (windSource.getType()) { - case EXPEDITION: - case WINDFINDER: - // we filter out measured wind sources with very low confidence - if (windTrackInfoDTO.minWindConfidence > 0.0001) { - windSourcesToShow.add(new com.sap.sse.common.Util.Pair(windSource, windTrackInfoDTO)); - } - break; - case COMBINED: - showCombinedWindOnMap(windSource, windTrackInfoDTO); - if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { - updateCoordinateSystemFromSettings(); - } - break; - default: - // Which wind sources are requested is defined in a list above this - // action. So we throw here an exception to notice a missing source. - throw new UnsupportedOperationException( - "There is currently no support for the enum value '" - + windSource.getType() + "' in this method."); - } + asyncActionsExecutor.execute(getWindInfoAction, GET_WIND_DATA_CATEGORY, + new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError( + "Error obtaining wind information: " + caught.getMessage(), + true /* silentMode */); } - } - showWindSensorsOnMap(windSourcesToShow); - } - }); + + @Override + public void onSuccess(WindInfoForRaceDTO windInfo) { + List> windSourcesToShow = new ArrayList>(); + if (windInfo != null) { + lastCombinedWindTrackInfoDTO = windInfo; + showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); + for (WindSource windSource : windInfo.windTrackInfoByWindSource.keySet()) { + WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource + .get(windSource); + switch (windSource.getType()) { + case EXPEDITION: + case WINDFINDER: + // we filter out measured wind sources with very low confidence + if (windTrackInfoDTO.minWindConfidence > 0.0001) { + windSourcesToShow.add( + new com.sap.sse.common.Util.Pair( + windSource, windTrackInfoDTO)); + } + break; + case COMBINED: + showCombinedWindOnMap(windSource, windTrackInfoDTO); + if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { + updateCoordinateSystemFromSettings(); + } + break; + default: + // Which wind sources are requested is defined in a list above this + // action. So we throw here an exception to notice a missing source. + throw new UnsupportedOperationException( + "There is currently no support for the enum value '" + + windSource.getType() + "' in this method."); + } + } + } + showWindSensorsOnMap(windSourcesToShow); + } + }); } } } @@ -957,6 +992,7 @@ public class RaceMap extends AbstractCompositeComponent impleme public WindInfoForRaceDTO getLastCombinedWindTrackInfoDTO() { return lastCombinedWindTrackInfoDTO; } + /** * Requests updates for map data and, when received, updates the map structures accordingly. *

@@ -977,12 +1013,14 @@ public class RaceMap extends AbstractCompositeComponent impleme * current tail length setting the new tail segment does not overlap with the old one, requiring a full load of the * entire tail data for that competitor. For these non-overlapping requests, this method creates a * {@link GetBoatPositionsAction} request loading the entire tail required, but not quick ranks, sidelines and mark - * positions. Updating the results of this call is done in {@link #updateBoatPositions(Date, long, Map, Iterable, Map, boolean)}. + * positions. Updating the results of this call is done in + * {@link #updateBoatPositions(Date, long, Map, Iterable, Map, boolean)}. *

*/ private void callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers( final Triple, Map, Map> fromAndToAndOverlap, - RegattaAndRaceIdentifier race, final Date newTime, final long transitionTimeInMillis, final Iterable competitorsToShow) { + RegattaAndRaceIdentifier race, final Date newTime, final long transitionTimeInMillis, + final Iterable competitorsToShow) { final Map fromTimesForQuickCall = new HashMap<>(); final Map toTimesForQuickCall = new HashMap<>(); final Map fromTimesForNonOverlappingTailsCall = new HashMap<>(); @@ -993,7 +1031,8 @@ public class RaceMap extends AbstractCompositeComponent impleme fromTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); toTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getB().get(e.getKey())); } else { - // no overlap; add competitor to request with a zero-length interval asking only position at newTime, not the entire tail + // no overlap; add competitor to request with a zero-length interval asking only position at newTime, + // not the entire tail fromTimesForQuickCall.put(e.getKey(), newTime); toTimesForQuickCall.put(e.getKey(), newTime); fromTimesForNonOverlappingTailsCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); @@ -1011,46 +1050,56 @@ public class RaceMap extends AbstractCompositeComponent impleme // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for // all its entries so that the other response received for GetBoatPositionsAction will consider this an // overlap if it happens after this update. - asyncActionsExecutor.execute(new GetRaceMapDataAction(sailingService, competitorsByIdAsString, - race, useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, /* extrapolate */true, - (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null), - raceCompetitorSet.getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID(), newTime, settings.isShowEstimatedDuration()), - GET_RACE_MAP_DATA_CATEGORY, - getRaceMapDataCallback(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter)); - // next, if necessary, do the full thing; the two calls have different action classes, so throttling should not drop one for the other + asyncActionsExecutor.execute( + new GetRaceMapDataAction(sailingService, competitorsByIdAsString, race, + useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, + /* extrapolate */true, + (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null), + raceCompetitorSet + .getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID(), + newTime, settings.isShowEstimatedDuration()), + GET_RACE_MAP_DATA_CATEGORY, getRaceMapDataCallback(newTime, transitionTimeInMillis, + fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter)); + // next, if necessary, do the full thing; the two calls have different action classes, so throttling should not + // drop one for the other if (!fromTimesForNonOverlappingTailsCall.keySet().isEmpty()) { - asyncActionsExecutor.execute(new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, toTimesForNonOverlappingTailsCall, - /* extrapolate */ true), GET_RACE_MAP_DATA_CATEGORY, - new MarkedAsyncCallback<>(new AsyncCallback() { + asyncActionsExecutor.execute( + new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, + toTimesForNonOverlappingTailsCall, /* extrapolate */ true), + GET_RACE_MAP_DATA_CATEGORY, new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable t) { - errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), true /*silentMode */); + errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), + true /* silentMode */); } - + @Override public void onSuccess(CompactBoatPositionsDTO result) { - // Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to updateBoatPositions for those - // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for - // all its entries so that the other response received for GetRaceMapDataAction will consider this an + // Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to + // updateBoatPositions for those + // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will + // contain true for + // all its entries so that the other response received for GetRaceMapDataAction will + // consider this an // overlap if it happens after this update. - updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, - result.getBoatPositionsForCompetitors(competitorsByIdAsString), /* updateTailsOnly */ true); + updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), + competitorsToShow, result.getBoatPositionsForCompetitors(competitorsByIdAsString), + /* updateTailsOnly */ true); } })); } } - private AsyncCallback getRaceMapDataCallback( - final Date newTime, - final long transitionTimeInMillis, + private AsyncCallback getRaceMapDataCallback(final Date newTime, final long transitionTimeInMillis, final Map hasTailOverlapForCompetitor, final Iterable competitorsToShow, final int requestID) { return new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining racemap data: " + caught.getMessage(), true /*silentMode */); + errorReporter.reportError("Error obtaining racemap data: " + caught.getMessage(), + true /* silentMode */); } - + @Override public void onSuccess(RaceMapDataDTO raceMapDataDTO) { if (map != null && raceMapDataDTO != null) { @@ -1059,20 +1108,23 @@ public class RaceMap extends AbstractCompositeComponent impleme startedProcessingRequestID = requestID; if (raceMapDataDTO.raceCompetitorIdsAsStrings != null) { try { - raceCompetitorSet.setIdsAsStringsOfCompetitorsInRace(raceMapDataDTO.raceCompetitorIdsAsStrings); + raceCompetitorSet + .setIdsAsStringsOfCompetitorsInRace(raceMapDataDTO.raceCompetitorIdsAsStrings); } catch (Exception e) { - GWT.log("Error trying to update competitor set for race "+raceIdentifier.getRaceName()+ - " in regatta "+raceIdentifier.getRegattaName(), e); + GWT.log("Error trying to update competitor set for race " + raceIdentifier.getRaceName() + + " in regatta " + raceIdentifier.getRegattaName(), e); } } quickRanksDTOProvider.quickRanksReceivedFromServer(raceMapDataDTO.quickRanks); if (isSimulationEnabled && settings.isShowSimulationOverlay()) { lastLegNumber = raceMapDataDTO.coursePositions.currentLegNumber; - simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion); + simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, + raceMapDataDTO.simulationResultVersion); } // Do boat specific actions Map> boatData = raceMapDataDTO.boatPositions; - updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, competitorsToShow, boatData, /* updateTailsOnly */ false); + updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, + competitorsToShow, boatData, /* updateTailsOnly */ false); if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); } @@ -1082,7 +1134,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (!maneuverLossLinesMap.isEmpty()) { removeManeuverLossLinesAndInfoOverlays(); } - + if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } @@ -1091,29 +1143,30 @@ public class RaceMap extends AbstractCompositeComponent impleme showCourseSidelinesOnMap(raceMapDataDTO.courseSidelines); showStartAndFinishAndCourseMiddleLines(raceMapDataDTO.coursePositions); showStartLineToFirstMarkTriangle(raceMapDataDTO.coursePositions); - + // Rezoom the map LatLngBounds zoomToBounds = null; - if (!settings.getZoomSettings().containsZoomType(ZoomTypes.NONE)) { // Auto zoom if setting is not manual + if (!settings.getZoomSettings().containsZoomType(ZoomTypes.NONE)) { // Auto zoom if setting is + // not manual zoomToBounds = settings.getZoomSettings().getNewBounds(RaceMap.this); if (zoomToBounds == null && !mapFirstZoomDone) { - zoomToBounds = getDefaultZoomBounds(); // the user-specified zoom couldn't find what it was looking for; try defaults once + zoomToBounds = getDefaultZoomBounds(); // the user-specified zoom couldn't find what it + // was looking for; try defaults once } } else if (!mapFirstZoomDone) { // Zoom once to the marks if marks exist zoomToBounds = new CourseMarksBoundsCalculator().calculateNewBounds(RaceMap.this); if (zoomToBounds == null) { - zoomToBounds = getDefaultZoomBounds(); // use default zoom, e.g., + zoomToBounds = getDefaultZoomBounds(); // use default zoom, e.g., } /* - * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that - * the map was just zoomed to the bounds of the marks, it was not a zoom or pan - * triggered by the user. As a consequence the - * mapZoomedOrPannedSinceLastRaceSelection option has to reset again. + * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that the map was + * just zoomed to the bounds of the marks, it was not a zoom or pan triggered by the user. + * As a consequence the mapZoomedOrPannedSinceLastRaceSelection option has to reset again. */ } zoomMapToNewBounds(zoomToBounds); mapFirstZoomDone = true; - + updateEstimatedDuration(raceMapDataDTO.estimatedDuration); } } else { @@ -1130,13 +1183,13 @@ public class RaceMap extends AbstractCompositeComponent impleme if (estimatedDurationOverlay == null) { estimatedDurationOverlay = new Label(""); estimatedDurationOverlay.setStyleName(raceMapStyle.estimatedDuration()); - if(showHeaderPanel) { + if (showHeaderPanel) { estimatedDurationOverlay.addStyleName(raceMapStyle.estimatedDurationWithHeader()); } map.setControls(ControlPosition.TOP_CENTER, estimatedDurationOverlay); } - estimatedDurationOverlay.setText(stringMessages.estimatedDuration() - + " " + DateAndTimeFormatterUtil.formatElapsedTime(estimatedDuration.asMillis())); + estimatedDurationOverlay.setText(stringMessages.estimatedDuration() + " " + + DateAndTimeFormatterUtil.formatElapsedTime(estimatedDuration.asMillis())); } @@ -1144,17 +1197,18 @@ public class RaceMap extends AbstractCompositeComponent impleme * @param hasTailOverlapForCompetitor * if for a competitor whose fixes are provided in fixesForCompetitors this holds * false, any fixes previously stored for that competitor are removed, and the tail is - * deleted from the map (see {@link #removeTail(CompetitorWithBoatDTO)}); the new fixes are then added to the - * {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). If - * this map holds true, {@link #mergeFixes(CompetitorWithBoatDTO, List, long)} is used to merge the - * new fixes from fixesForCompetitors into the {@link #fixes} collection, and the tail is - * left unchanged. NOTE: When a non-overlapping set of fixes is updated (false), this - * map's record for the competitor is UPDATED to true after the tail deletion and - * {@link #fixes} replacement has taken place. This helps in cases where this update is only one of two - * into which an original request was split (one quick update of the tail's head and another one for the - * longer tail itself), such that the second request that uses the same map will be considered - * having an overlap now, not leading to a replacement of the previous update originating from the same - * request. See also {@link FixesAndTails#updateFixes(Map, Map, TailFactory, long)}. + * deleted from the map (see {@link #removeTail(CompetitorWithBoatDTO)}); the new fixes are then added to + * the {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). + * If this map holds true, {@link #mergeFixes(CompetitorWithBoatDTO, List, long)} is used to + * merge the new fixes from fixesForCompetitors into the {@link #fixes} collection, and the + * tail is left unchanged. NOTE: When a non-overlapping set of fixes is updated + * (false), this map's record for the competitor is UPDATED to true + * after the tail deletion and {@link #fixes} replacement has taken place. This helps in cases where this + * update is only one of two into which an original request was split (one quick update of the tail's + * head and another one for the longer tail itself), such that the second request that uses the + * same map will be considered having an overlap now, not leading to a replacement of the + * previous update originating from the same request. See also + * {@link FixesAndTails#updateFixes(Map, Map, TailFactory, long)}. * @param updateTailsOnly * if true, only the tails are updated according to boatData and * hasTailOverlapForCompetitor, but the advantage line is not updated, and neither are the @@ -1165,13 +1219,15 @@ public class RaceMap extends AbstractCompositeComponent impleme */ private void updateBoatPositions(final Date newTime, final long transitionTimeInMillis, final Map hasTailOverlapForCompetitor, - final Iterable competitorsToShow, Map> boatData, boolean updateTailsOnly) { + final Iterable competitorsToShow, + Map> boatData, boolean updateTailsOnly) { fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis); showBoatsOnMap(newTime, transitionTimeInMillis, /* re-calculate; it could have changed since the asynchronous request was made: */ getCompetitorsToShow(), updateTailsOnly); if (!updateTailsOnly) { - showCompetitorInfoOnMap(newTime, transitionTimeInMillis, competitorSelection.getSelectedFilteredCompetitors()); + showCompetitorInfoOnMap(newTime, transitionTimeInMillis, + competitorSelection.getSelectedFilteredCompetitors()); // even though the wind data is retrieved by a separate call, re-draw the advantage line because it needs to // adjust to new boat positions showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); @@ -1179,13 +1235,13 @@ public class RaceMap extends AbstractCompositeComponent impleme } private void showCourseSidelinesOnMap(List sidelinesDTOs) { - if (map != null && sidelinesDTOs != null ) { + if (map != null && sidelinesDTOs != null) { Map toRemoveSidelines = new HashMap(courseSidelines); for (SidelineDTO sidelineDTO : sidelinesDTOs) { if (sidelineDTO.getMarks().size() == 2) { // right now we only support sidelines with 2 marks Polygon sideline = courseSidelines.get(sidelineDTO); LatLng[] sidelinePoints = new LatLng[sidelineDTO.getMarks().size()]; - int i=0; + int i = 0; for (MarkDTO sidelineMark : sidelineDTO.getMarks()) { sidelinePoints[i] = coordinateSystem.toLatLng(sidelineMark.position); i++; @@ -1198,7 +1254,7 @@ public class RaceMap extends AbstractCompositeComponent impleme options.setStrokeOpacity(1.0); options.setFillColor(null); options.setFillOpacity(1.0); - + sideline = Polygon.newInstance(options); MVCArray pointsAsArray = MVCArray.newInstance(sidelinePoints); sideline.setPath(pointsAsArray); @@ -1232,25 +1288,29 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + private void showCourseMarksOnMap(CoursePositionsDTO courseDTO, long transitionTimeInMillis) { if (map != null && courseDTO != null) { WaypointDTO endWaypointForCurrentLegNumber = null; - if(courseDTO.currentLegNumber > 0 && courseDTO.currentLegNumber <= courseDTO.totalLegsCount) { + if (courseDTO.currentLegNumber > 0 && courseDTO.currentLegNumber <= courseDTO.totalLegsCount) { endWaypointForCurrentLegNumber = courseDTO.getEndWaypointForLegNumber(courseDTO.currentLegNumber); } - - Map toRemoveCourseMarks = new HashMap(courseMarkOverlays); + + Map toRemoveCourseMarks = new HashMap( + courseMarkOverlays); if (courseDTO.marks != null) { for (MarkDTO markDTO : courseDTO.marks) { boolean isSelected = false; - if (endWaypointForCurrentLegNumber != null && Util.contains(endWaypointForCurrentLegNumber.controlPoint.getMarks(), markDTO)) { + if (endWaypointForCurrentLegNumber != null + && Util.contains(endWaypointForCurrentLegNumber.controlPoint.getMarks(), markDTO)) { isSelected = true; } CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTO.getIdAsString()); if (courseMarkOverlay == null) { - courseMarkOverlay = new CourseMarkOverlay(map, RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, markDTO, coordinateSystem, courseDTO); - courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); + courseMarkOverlay = new CourseMarkOverlay(map, RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, + markDTO, coordinateSystem, courseDTO); + courseMarkOverlay + .setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); courseMarkOverlay.setBuoyZoneRadius(settings.getBuoyZoneRadius()); courseMarkOverlay.setSelected(isSelected); courseMarkOverlays.put(markDTO.getIdAsString(), courseMarkOverlay); @@ -1259,7 +1319,8 @@ public class RaceMap extends AbstractCompositeComponent impleme courseMarkOverlay.addToMap(); } else { courseMarkOverlay.setMarkPosition(markDTO.position, transitionTimeInMillis); - courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); + courseMarkOverlay + .setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); courseMarkOverlay.setBuoyZoneRadius(settings.getBuoyZoneRadius()); courseMarkOverlay.setSelected(isSelected); courseMarkOverlay.setCourse(courseDTO); @@ -1276,10 +1337,10 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + /** - * Based on the mark positions in {@link #courseMarkOverlays}' values this method determines the center of gravity of these marks' - * {@link CourseMarkOverlay#getPosition() positions}. + * Based on the mark positions in {@link #courseMarkOverlays}' values this method determines the center of gravity + * of these marks' {@link CourseMarkOverlay#getPosition() positions}. */ private Position getCenterOfCourse() { ScalablePosition center = null; @@ -1303,16 +1364,18 @@ public class RaceMap extends AbstractCompositeComponent impleme } } - private void showWindSensorsOnMap(List> windSensorsList) { + private void showWindSensorsOnMap( + List> windSensorsList) { if (map != null) { Set toRemoveWindSources = new HashSet(windSensorOverlays.keySet()); for (com.sap.sse.common.Util.Pair windSourcePair : windSensorsList) { - WindSource windSource = windSourcePair.getA(); + WindSource windSource = windSourcePair.getA(); WindTrackInfoDTO windTrackInfoDTO = windSourcePair.getB(); WindSensorOverlay windSensorOverlay = windSensorOverlays.get(windSource); if (windSensorOverlay == null) { - windSensorOverlay = createWindSensorOverlay(RaceMapOverlaysZIndexes.WINDSENSOR_ZINDEX, windSource, windTrackInfoDTO); + windSensorOverlay = createWindSensorOverlay(RaceMapOverlaysZIndexes.WINDSENSOR_ZINDEX, windSource, + windTrackInfoDTO); windSensorOverlays.put(windSource, windSensorOverlay); windSensorOverlay.addToMap(); } else { @@ -1330,11 +1393,13 @@ public class RaceMap extends AbstractCompositeComponent impleme } } - private void showCompetitorInfoOnMap(final Date newTime, final long timeForPositionTransitionMillis, final Iterable competitorsToShow) { + private void showCompetitorInfoOnMap(final Date newTime, final long timeForPositionTransitionMillis, + final Iterable competitorsToShow) { if (map != null) { if (settings.isShowSelectedCompetitorsInfo()) { Set toRemoveCompetorInfoOverlaysIdsAsStrings = new HashSet<>(); - Util.addAll(competitorInfoOverlays.getCompetitorIdsAsStrings(), toRemoveCompetorInfoOverlaysIdsAsStrings); + Util.addAll(competitorInfoOverlays.getCompetitorIdsAsStrings(), + toRemoveCompetorInfoOverlaysIdsAsStrings); for (CompetitorDTO competitorDTO : competitorsToShow) { if (fixesAndTails.hasFixesFor(competitorDTO)) { GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = getBoatFix(competitorDTO, newTime); @@ -1342,11 +1407,13 @@ public class RaceMap extends AbstractCompositeComponent impleme CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(competitorDTO); final Integer rank = getRank(competitorDTO); if (competitorInfoOverlay == null) { - competitorInfoOverlay = competitorInfoOverlays.createCompetitorInfoOverlay(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO, - lastBoatFix, rank, timeForPositionTransitionMillis); + competitorInfoOverlay = competitorInfoOverlays.createCompetitorInfoOverlay( + RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO, lastBoatFix, rank, + timeForPositionTransitionMillis); competitorInfoOverlay.addToMap(); } else { - competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix, timeForPositionTransitionMillis); + competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix, + timeForPositionTransitionMillis); } toRemoveCompetorInfoOverlaysIdsAsStrings.remove(competitorDTO.getIdAsString()); } @@ -1392,7 +1459,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } return timeForPositionTransitionMillisSmoothed; } - + /** * @param updateTailsOnly * if false, tails of competitors not in competitorsToShow are removed from the @@ -1418,14 +1485,17 @@ public class RaceMap extends AbstractCompositeComponent impleme if (!fixesAndTails.hasTail(competitorDTO)) { fixesAndTails.createTailAndUpdateIndices(competitorDTO, tailsFromTime, tailsToTime, this); } else { - fixesAndTails.updateTail(competitorDTO, tailsFromTime, tailsToTime, (int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2)); + fixesAndTails.updateTail(competitorDTO, tailsFromTime, tailsToTime, + (int) (timeForPositionTransitionMillis == -1 ? -1 + : timeForPositionTransitionMillis / 2)); if (!updateTailsOnly) { competitorDTOsOfUnusedTails.remove(competitorDTO); } PolylineOptions newOptions = createTailStyle(competitorDTO, displayHighlighted(competitorDTO)); fixesAndTails.getTail(competitorDTO).setOptions(newOptions); } - boolean usedExistingBoatCanvas = updateBoatCanvasForCompetitor(competitorDTO, newTime, timeForPositionTransitionMillis); + boolean usedExistingBoatCanvas = updateBoatCanvasForCompetitor(competitorDTO, newTime, + timeForPositionTransitionMillis); if (usedExistingBoatCanvas && !updateTailsOnly) { competitorDTOsOfUnusedBoatCanvases.remove(competitorDTO); } @@ -1450,32 +1520,34 @@ public class RaceMap extends AbstractCompositeComponent impleme * necessary if greater distances are allowed. */ public LatLng calculatePositionAlongRhumbline(LatLng position, double bearingDeg, Distance distance) { - double distanceRad = distance.getCentralAngleRad(); + double distanceRad = distance.getCentralAngleRad(); double lat1 = position.getLatitude() / 180. * Math.PI; double lon1 = position.getLongitude() / 180. * Math.PI; double bearingRad = bearingDeg / 180. * Math.PI; - double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distanceRad) + - Math.cos(lat1) * Math.sin(distanceRad) * Math.cos(bearingRad)); - double lon2 = lon1 + Math.atan2(Math.sin(bearingRad)*Math.sin(distanceRad)*Math.cos(lat1), - Math.cos(distanceRad)-Math.sin(lat1)*Math.sin(lat2)); - lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalize to -180..+180� + double lat2 = Math.asin( + Math.sin(lat1) * Math.cos(distanceRad) + Math.cos(lat1) * Math.sin(distanceRad) * Math.cos(bearingRad)); + double lon2 = lon1 + Math.atan2(Math.sin(bearingRad) * Math.sin(distanceRad) * Math.cos(lat1), + Math.cos(distanceRad) - Math.sin(lat1) * Math.sin(lat2)); + lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180..+180� // position is already in LatLng space, so no mapping through coordinateSystem is required here - return LatLng.newInstance(lat2 / Math.PI * 180., lon2 / Math.PI * 180.); + return LatLng.newInstance(lat2 / Math.PI * 180., lon2 / Math.PI * 180.); } - + /** - * Returns a pair whose first component is the leg number (one-based) of the competitor returned as the second component. - * The competitor returned currently has the best ranking in the quick ranks provided by the {@link #quickRanksDTOProvider}. + * Returns a pair whose first component is the leg number (one-based) of the competitor returned as the second + * component. The competitor returned currently has the best ranking in the quick ranks provided by the + * {@link #quickRanksDTOProvider}. */ private com.sap.sse.common.Util.Pair getBestVisibleCompetitorWithOneBasedLegNumber( Iterable competitorsToShow) { CompetitorDTO leadingCompetitorDTO = null; int legOfLeaderCompetitor = -1; int bestOneBasedRank = Integer.MAX_VALUE; - for (QuickRankDTO competitorFromBestToWorstAndOneBasedLegNumber : quickRanksDTOProvider.getQuickRanks().values()) { - if (Util.contains(competitorsToShow, competitorFromBestToWorstAndOneBasedLegNumber.competitor) && - competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased != 0 && - competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank < bestOneBasedRank) { + for (QuickRankDTO competitorFromBestToWorstAndOneBasedLegNumber : quickRanksDTOProvider.getQuickRanks() + .values()) { + if (Util.contains(competitorsToShow, competitorFromBestToWorstAndOneBasedLegNumber.competitor) + && competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased != 0 + && competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank < bestOneBasedRank) { leadingCompetitorDTO = competitorFromBestToWorstAndOneBasedLegNumber.competitor; legOfLeaderCompetitor = competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased; bestOneBasedRank = competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank; @@ -1484,18 +1556,22 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - return leadingCompetitorDTO == null ? null : - new com.sap.sse.common.Util.Pair(legOfLeaderCompetitor, leadingCompetitorDTO); + return leadingCompetitorDTO == null ? null + : new com.sap.sse.common.Util.Pair(legOfLeaderCompetitor, leadingCompetitorDTO); } - final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with the visible area of the map; bug 616 - private void showAdvantageLine(Iterable competitorsToShow, Date date, long timeForPositionTransitionMillis) { + final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with + // the visible area of the map; bug 616 + + private void showAdvantageLine(Iterable competitorsToShow, Date date, + long timeForPositionTransitionMillis) { if (map != null && lastRaceTimesInfo != null && !quickRanksDTOProvider.getQuickRanks().isEmpty() && lastCombinedWindTrackInfoDTO != null) { boolean drawAdvantageLine = false; if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) { // find competitor with highest rank - com.sap.sse.common.Util.Pair visibleLeaderInfo = getBestVisibleCompetitorWithOneBasedLegNumber(competitorsToShow); + com.sap.sse.common.Util.Pair visibleLeaderInfo = getBestVisibleCompetitorWithOneBasedLegNumber( + competitorsToShow); // the boat fix may be null; may mean that no positions were loaded yet for the leading visible boat; // don't show anything GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = null; @@ -1503,8 +1579,7 @@ public class RaceMap extends AbstractCompositeComponent impleme boolean isLegTypeKnown = false; WindTrackInfoDTO windDataForLegMiddle = null; LegInfoDTO legInfoDTO = null; - if (visibleLeaderInfo != null - && visibleLeaderInfo.getA() > 0 + if (visibleLeaderInfo != null && visibleLeaderInfo.getA() > 0 && visibleLeaderInfo.getA() <= lastRaceTimesInfo.getLegInfos().size() // get wind at middle of leg for leading visible competitor && (windDataForLegMiddle = lastCombinedWindTrackInfoDTO @@ -1517,7 +1592,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } lastBoatFix = getBoatFix(visibleLeaderInfo.getB(), date); } - if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null && lastBoatFix.speedWithBearing != null) { + if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null + && lastBoatFix.speedWithBearing != null) { BoatDTO boat = competitorSelection.getBoat(visibleLeaderInfo.getB()); Distance distanceFromBoatPositionInKm = boat.getBoatClass().getHullLength(); // one hull length // implement and use Position.translateRhumb() @@ -1543,7 +1619,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (rotatedBearingDeg2 < 0.0) { rotatedBearingDeg2 += 360.0; } - break; + break; } case REACHING: { rotatedBearingDeg1 = legInfoDTO.legBearingInDegrees + 90.0; @@ -1554,7 +1630,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (rotatedBearingDeg2 < 0.0) { rotatedBearingDeg2 += 360.0; } - break; + break; } } MVCArray nextPath = MVCArray.newInstance(); @@ -1569,7 +1645,7 @@ public class RaceMap extends AbstractCompositeComponent impleme options.setStrokeColor(ADVANTAGE_LINE_COLOR); options.setStrokeWeight(1); options.setStrokeOpacity(0.5); - + advantageLine = Polyline.newInstance(options); advantageTimer = new AdvantageLineAnimator(advantageLine); MVCArray pointsAsArray = MVCArray.newInstance(); @@ -1578,7 +1654,7 @@ public class RaceMap extends AbstractCompositeComponent impleme advantageLine.setPath(pointsAsArray); advantageLine.setMap(map); Hoverline advantageHoverline = new Hoverline(advantageLine, options, this); - + advantageLineMouseOverHandler = new AdvantageLineMouseOverMapHandler( bearingOfCombinedWindInDeg, new Date(windFix.measureTimepoint)); advantageLine.addMouseOverHandler(advantageLineMouseOverHandler); @@ -1591,7 +1667,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } else { nextPath.push(advantageLinePos1); nextPath.push(advantageLinePos2); - advantageTimer.setNextPositionAndTransitionMillis(nextPath, timeForPositionTransitionMillis); + advantageTimer.setNextPositionAndTransitionMillis(nextPath, + timeForPositionTransitionMillis); if (advantageLineMouseOverHandler != null) { advantageLineMouseOverHandler.setTrueWindBearing(bearingOfCombinedWindInDeg); advantageLineMouseOverHandler.setDate(new Date(windFix.measureTimepoint)); @@ -1611,11 +1688,11 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + private final StringBuilder windwardStartLineMarkToFirstMarkLineText = new StringBuilder(); private final StringBuilder leewardStartLineMarkToFirstMarkLineText = new StringBuilder(); - - private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO){ + + private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO) { final List startMarkPositions = courseDTO.getStartMarkPositions(); if (startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1) { final Position windwardStartLinePosition = startMarkPositions.get(0); @@ -1623,12 +1700,10 @@ public class RaceMap extends AbstractCompositeComponent impleme final Position firstMarkPosition = courseDTO.waypointPositions.get(1); windwardStartLineMarkToFirstMarkLineText.replace(0, windwardStartLineMarkToFirstMarkLineText.length(), stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(windwardStartLinePosition.getDistance(firstMarkPosition) - .getMeters()))); + .format(windwardStartLinePosition.getDistance(firstMarkPosition).getMeters()))); leewardStartLineMarkToFirstMarkLineText.replace(0, leewardStartLineMarkToFirstMarkLineText.length(), stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(leewardStartLinePosition.getDistance(firstMarkPosition) - .getMeters()))); + .format(leewardStartLinePosition.getDistance(firstMarkPosition).getMeters()))); final LineInfoProvider windwardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { @@ -1641,12 +1716,16 @@ public class RaceMap extends AbstractCompositeComponent impleme return leewardStartLineMarkToFirstMarkLineText.toString(); } }; - windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* showLine */ + windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* + * showLine + */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider, "grey", 1, 1.0); - leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */ + leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* + * showLine + */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider, @@ -1659,112 +1738,135 @@ public class RaceMap extends AbstractCompositeComponent impleme final LineInfoProvider startLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.startLine()+startLineAdvantageText; + return stringMessages.startLine() + startLineAdvantageText; } }; final LineInfoProvider finishLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.finishLine()+finishLineAdvantageText; + return stringMessages.finishLine() + finishLineAdvantageText; } }; private void showStartAndFinishAndCourseMiddleLines(final CoursePositionsDTO courseDTO) { - if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null && - !courseDTO.course.waypoints.isEmpty()) { + if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null + && !courseDTO.course.waypoints.isEmpty()) { // draw the start line final WaypointDTO startWaypoint = courseDTO.course.waypoints.get(0); updateCountdownCanvas(startWaypoint); - final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 : courseDTO.getStartMarkPositions().size(); - final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 : courseDTO.getFinishMarkPositions().size(); - final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null : courseDTO.getStartMarkPositions().get(0); - final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null : courseDTO.getStartMarkPositions().get(1); + final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 + : courseDTO.getStartMarkPositions().size(); + final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 + : courseDTO.getFinishMarkPositions().size(); + final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null + : courseDTO.getStartMarkPositions().get(0); + final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null + : courseDTO.getStartMarkPositions().get(1); if (courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { - startLineAdvantageText.replace(0, startLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( - numberFormatOneDecimal.format(courseDTO.startLineLengthInMeters), - numberFormatOneDecimal.format(Math.abs(courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), - courseDTO.startLineAdvantageousSide.name().charAt(0)+courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), - numberFormatOneDecimal.format(courseDTO.startLineAdvantageInMeters))); + startLineAdvantageText.replace(0, startLineAdvantageText.length(), + " " + stringMessages.lineAngleToWindAndAdvantage( + numberFormatOneDecimal.format(courseDTO.startLineLengthInMeters), + numberFormatOneDecimal.format(Math.abs( + courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), + courseDTO.startLineAdvantageousSide.name().charAt(0) + + courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), + numberFormatOneDecimal.format(courseDTO.startLineAdvantageInMeters))); } else { startLineAdvantageText.delete(0, startLineAdvantageText.length()); } - final boolean showStartLineBasedOnCurrentLeg = numberOfStartWaypointMarks == 2 && courseDTO.currentLegNumber <= 1; - final boolean showFinishLineBasedOnCurrentLeg = numberOfFinishWaypointMarks == 2 && courseDTO.currentLegNumber == courseDTO.totalLegsCount; + final boolean showStartLineBasedOnCurrentLeg = numberOfStartWaypointMarks == 2 + && courseDTO.currentLegNumber <= 1; + final boolean showFinishLineBasedOnCurrentLeg = numberOfFinishWaypointMarks == 2 + && courseDTO.currentLegNumber == courseDTO.totalLegsCount; // show the line when STARTLINE is selected and the current leg is around the start leg, - // or when COURSEGEOMETRY is selected and the finish line isn't equal and wouldn't be shown at the same time based on the current leg. - // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will not be displayed if + // or when COURSEGEOMETRY is selected and the finish line isn't equal and wouldn't be shown at the same time + // based on the current leg. + // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will not be + // displayed if // based on the race progress the finish line is to be preferred, so only the finish line will be shown. - final boolean reallyShowStartLine = - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) && showStartLineBasedOnCurrentLeg) || - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && - (!showFinishLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO))); + final boolean reallyShowStartLine = (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) + && showStartLineBasedOnCurrentLeg) + || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) + && (!showFinishLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO))); // show the line when FINISHLINE is selected and the current leg is the last leg, // or when COURSEGEOMETRY is selected and the start line isn't equal or the current leg is the last leg. - // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will be displayed unless + // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will be + // displayed unless // the finish line should take precedence based on race progress. - final boolean reallyShowFinishLine = showFinishLineBasedOnCurrentLeg && - (!showStartLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO)) && - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) && showFinishLineBasedOnCurrentLeg) || - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && - (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); - startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, - startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff", 1, 1.0); + final boolean reallyShowFinishLine = showFinishLineBasedOnCurrentLeg + && (!showStartLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO)) + && (settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) + && showFinishLineBasedOnCurrentLeg) + || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) + && (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); + startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, startLineLeftPosition, + startLineRightPosition, startLineInfoProvider, "#ffffff", 1, 1.0); // draw the finish line - final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0); - final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null : courseDTO.getFinishMarkPositions().get(1); + final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null + : courseDTO.getFinishMarkPositions().get(0); + final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null + : courseDTO.getFinishMarkPositions().get(1); if (courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { - finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( - numberFormatOneDecimal.format(courseDTO.finishLineLengthInMeters), - numberFormatOneDecimal.format(Math.abs(courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), - courseDTO.finishLineAdvantageousSide.name().charAt(0)+courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), - numberFormatOneDecimal.format(courseDTO.finishLineAdvantageInMeters))); + finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), + " " + stringMessages.lineAngleToWindAndAdvantage( + numberFormatOneDecimal.format(courseDTO.finishLineLengthInMeters), + numberFormatOneDecimal.format(Math.abs( + courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), + courseDTO.finishLineAdvantageousSide.name().charAt(0) + + courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), + numberFormatOneDecimal.format(courseDTO.finishLineAdvantageInMeters))); } else { finishLineAdvantageText.delete(0, finishLineAdvantageText.length()); } - finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, - finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR, 1, 1.0); + finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, finishLineLeftPosition, + finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR, 1, 1.0); // the control point pairs for which we already decided whether or not // to show a course middle line for; values tell whether to show the line and for which zero-based // start waypoint index to do so; when for an equal control point pair multiple decisions with different // outcome are made, a decision to show the line overrules the decision to not show it (OR-semantics) final Map, Pair> keysAlreadyHandled = new HashMap<>(); - for (int zeroBasedIndexOfStartWaypoint = 0; zeroBasedIndexOfStartWaypoint key = getCourseMiddleLinesKey(courseDTO, zeroBasedIndexOfStartWaypoint); - boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() || - settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) || - (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE) - && courseDTO.currentLegNumber > 0 - && courseDTO.currentLegNumber-1 == zeroBasedIndexOfStartWaypoint); + boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() + || settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) + || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE) + && courseDTO.currentLegNumber > 0 + && courseDTO.currentLegNumber - 1 == zeroBasedIndexOfStartWaypoint); keysAlreadyHandled.put(key, new Pair<>(showCourseMiddleLine, zeroBasedIndexOfStartWaypoint)); } Set> keysToConsider = new HashSet<>(keysAlreadyHandled.keySet()); keysToConsider.addAll(courseMiddleLines.keySet()); for (final Set key : keysToConsider) { - final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) ? - keysAlreadyHandled.get(key).getB() : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter + final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) + ? keysAlreadyHandled.get(key).getB() + : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter final Pair showLineAndZeroBasedIndexOfStartWaypoint = keysAlreadyHandled.get(key); - final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null && showLineAndZeroBasedIndexOfStartWaypoint.getA(); - courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), zeroBasedIndexOfStartWaypoint, showCourseMiddleLine)); + final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null + && showLineAndZeroBasedIndexOfStartWaypoint.getA(); + courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), + zeroBasedIndexOfStartWaypoint, showCourseMiddleLine)); } } } private boolean startLineEqualsFinishLine(CoursePositionsDTO courseDTO) { final List waypoints; - return courseDTO != null && courseDTO.course != null && - (waypoints = courseDTO.course.waypoints) != null && - waypoints.get(0).controlPoint.equals(waypoints.get(waypoints.size()-1).controlPoint); + return courseDTO != null && courseDTO.course != null && (waypoints = courseDTO.course.waypoints) != null + && waypoints.get(0).controlPoint.equals(waypoints.get(waypoints.size() - 1).controlPoint); } /** - * Given a zero-based index into courseDTO's {@link RaceCourseDTO#waypoints waypoints list} that denotes the start - * waypoint of the leg in question, returns a key that can be used for the {@link #courseMiddleLines} map, consisting of a set - * that holds the two {@link ControlPointDTO}s representing the start and finish control point of that leg. + * Given a zero-based index into courseDTO's {@link RaceCourseDTO#waypoints waypoints list} that + * denotes the start waypoint of the leg in question, returns a key that can be used for the + * {@link #courseMiddleLines} map, consisting of a set that holds the two {@link ControlPointDTO}s representing the + * start and finish control point of that leg. */ private Set getCourseMiddleLinesKey(final CoursePositionsDTO courseDTO, final int zeroBasedIndexOfStartWaypoint) { ControlPointDTO startControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint).controlPoint; - ControlPointDTO endControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint+1).controlPoint; + ControlPointDTO endControlPoint = courseDTO.course.waypoints + .get(zeroBasedIndexOfStartWaypoint + 1).controlPoint; final Set key = new HashSet<>(); key.add(startControlPoint); key.add(endControlPoint); @@ -1780,26 +1882,28 @@ public class RaceMap extends AbstractCompositeComponent impleme * @return null if the line is not shown; the polyline object representing the line being displayed * otherwise */ - private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, Polyline lineToShowOrRemoveOrUpdate, - final int zeroBasedIndexOfStartWaypoint, final boolean showLine) { + private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, + Polyline lineToShowOrRemoveOrUpdate, final int zeroBasedIndexOfStartWaypoint, final boolean showLine) { final Position position1DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint); - final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint+1); + final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint + 1); final LineInfoProvider lineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { final StringBuilder sb = new StringBuilder(); sb.append(stringMessages.courseMiddleLine()); sb.append('\n'); - sb.append(numberFormatNoDecimal.format( - Math.abs(position1DTO.getDistance(position2DTO).getMeters()))+stringMessages.metersUnit()); + sb.append(numberFormatNoDecimal.format(Math.abs(position1DTO.getDistance(position2DTO).getMeters())) + + stringMessages.metersUnit()); if (lastCombinedWindTrackInfoDTO != null) { - final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO.getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint); - if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null && !windTrackAtLegMiddle.windFixes.isEmpty()) { + final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO + .getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint); + if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null + && !windTrackAtLegMiddle.windFixes.isEmpty()) { WindDTO windAtLegMiddle = windTrackAtLegMiddle.windFixes.get(0); final double legBearingDeg = position1DTO.getBearingGreatCircle(position2DTO).getDegrees(); - final String diff = numberFormatOneDecimal.format( - Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg-legBearingDeg), - Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg-legBearingDeg))); + final String diff = numberFormatOneDecimal + .format(Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg - legBearingDeg), + Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg - legBearingDeg))); sb.append(", "); sb.append(stringMessages.degreesToWind(diff)); } @@ -1807,13 +1911,14 @@ public class RaceMap extends AbstractCompositeComponent impleme return sb.toString(); } }; - return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, lineInfoProvider, "#2268a0", 1, 1.0); + return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, + lineInfoProvider, "#2268a0", 1, 1.0); } private interface LineInfoProvider { String getLineInfo(); } - + /** * @param showLine * tells whether or not to show the line; if the lineToShowOrRemoveOrUpdate references a @@ -1842,7 +1947,8 @@ public class RaceMap extends AbstractCompositeComponent impleme lineToShowOrRemoveOrUpdate = Polyline.newInstance(options); lineToShowOrRemoveOrUpdate.setPath(pointsAsArray); lineToShowOrRemoveOrUpdate.setMap(map); - Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, this); + Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, + this); lineToShowOrRemoveOrUpdate.addMouseOverHandler(new MouseOverMapHandler() { @Override public void onEvent(MouseOverMapEvent event) { @@ -1860,7 +1966,8 @@ public class RaceMap extends AbstractCompositeComponent impleme pointsAsArray.removeAt(1); pointsAsArray.removeAt(0); } - adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, lineInfoProvider); + adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, + lineInfoProvider); pointsAsArray.insertAt(0, courseMiddleLinePoint1); pointsAsArray.insertAt(1, courseMiddleLinePoint2); } else { @@ -1875,7 +1982,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } private void adjustInfoOverlayForRemovedLine(Polyline lineToShowOrRemoveOrUpdate) { - SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); + SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry + .remove(lineToShowOrRemoveOrUpdate); if (infoOverlay != null) { infoOverlay.removeFromMap(); } @@ -1886,9 +1994,10 @@ public class RaceMap extends AbstractCompositeComponent impleme SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.get(lineToShowOrRemoveOrUpdate); if (getSettings().getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) { if (infoOverlay == null) { - infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, lineInfoProvider.getLineInfo(), coordinateSystem); + infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, + lineInfoProvider.getLineInfo(), coordinateSystem); infoOverlaysForLinesForCourseGeometry.put(lineToShowOrRemoveOrUpdate, infoOverlay); - infoOverlay.addToMap(); + infoOverlay.addToMap(); } else { infoOverlay.setInfoText(lineInfoProvider.getLineInfo()); } @@ -1897,28 +2006,29 @@ public class RaceMap extends AbstractCompositeComponent impleme infoOverlay.draw(); } else { if (infoOverlay != null) { - infoOverlay.removeFromMap(); - infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); + infoOverlay.removeFromMap(); + infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); } } } - + /** - * If, according to {@link #lastRaceTimesInfo} and {@link #timer} the race is - * still in the pre-start phase, show a {@link SmallTransparentInfoOverlay} at the - * start line that shows the count down. + * If, according to {@link #lastRaceTimesInfo} and {@link #timer} the race is still in the pre-start phase, show a + * {@link SmallTransparentInfoOverlay} at the start line that shows the count down. */ private void updateCountdownCanvas(WaypointDTO startWaypoint) { - if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null || Util.isEmpty(startWaypoint.controlPoint.getMarks()) - || lastRaceTimesInfo == null || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { + if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null + || Util.isEmpty(startWaypoint.controlPoint.getMarks()) || lastRaceTimesInfo == null + || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { if (countDownOverlay != null) { countDownOverlay.removeFromMap(); countDownOverlay = null; } } else { long timeToStartInMs = lastRaceTimesInfo.startOfRace.getTime() - timer.getTime().getTime(); - String countDownText = timeToStartInMs > 1000 ? stringMessages.timeToStart(DateAndTimeFormatterUtil - .formatElapsedTime(timeToStartInMs)) : stringMessages.start(); + String countDownText = timeToStartInMs > 1000 + ? stringMessages.timeToStart(DateAndTimeFormatterUtil.formatElapsedTime(timeToStartInMs)) + : stringMessages.start(); if (countDownOverlay == null) { countDownOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, countDownText, coordinateSystem, getSettings().getStartCountDownFontSizeScaling()); @@ -1926,7 +2036,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } else { countDownOverlay.setInfoText(countDownText); } - countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, /* transition time */ -1); + countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, + /* transition time */ -1); countDownOverlay.draw(); } } @@ -1945,7 +2056,8 @@ public class RaceMap extends AbstractCompositeComponent impleme int MAX_ZOOM = 20; // maximum zoom-level that should be automatically selected double LOG2 = Math.log(2.0); double deltaLng = bounds.getNorthEast().getLongitude() - bounds.getSouthWest().getLongitude(); - double deltaLat = getMercatorLatitude(bounds.getNorthEast().getLatitude()) - getMercatorLatitude(bounds.getSouthWest().getLatitude()); + double deltaLat = getMercatorLatitude(bounds.getNorthEast().getLatitude()) + - getMercatorLatitude(bounds.getSouthWest().getLatitude()); if ((deltaLng == 0) && (deltaLat == 0)) { return MAX_ZOOM; } @@ -1953,27 +2065,28 @@ public class RaceMap extends AbstractCompositeComponent impleme deltaLng += 360; } int zoomLng = (int) Math.floor(Math.log(map.getDiv().getClientWidth() * 360 / deltaLng / GLOBE_PXSIZE) / LOG2); - int zoomLat = (int) Math.floor(Math.log(map.getDiv().getClientHeight() * 2 * Math.PI / deltaLat / GLOBE_PXSIZE) / LOG2); + int zoomLat = (int) Math + .floor(Math.log(map.getDiv().getClientHeight() * 2 * Math.PI / deltaLat / GLOBE_PXSIZE) / LOG2); return Math.min(Math.min(zoomLat, zoomLng), MAX_ZOOM); } - + private void zoomMapToNewBounds(LatLngBounds newBounds) { if (newBounds != null) { LatLngBounds currentMapBounds; - if (map.getBounds() == null - || !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds) + if (map.getBounds() == null || !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds) || graticuleAreaRatio(currentMapBounds, newBounds) > 10) { // only change bounds if the new bounds don't fit into the current map zoom - Iterable oldZoomTypesToConsiderSettings = settings.getZoomSettings().getTypesToConsiderOnZoom(); + Iterable oldZoomTypesToConsiderSettings = settings.getZoomSettings() + .getTypesToConsiderOnZoom(); setAutoZoomInProgress(true); autoZoomLatLngBounds = newBounds; - int newZoomLevel = getZoomLevel(autoZoomLatLngBounds); + int newZoomLevel = getZoomLevel(autoZoomLatLngBounds); if (newZoomLevel != map.getZoom()) { // distinguish between zoom-in and zoom-out, because the sequence of panTo() and setZoom() // appears different on the screen due to map-animations // following sequences keep the selected boats allways visible: - // zoom-in : 1. panTo(), 2. setZoom() - // zoom-out: 1. setZoom(), 2. panTo() + // zoom-in : 1. panTo(), 2. setZoom() + // zoom-out: 1. setZoom(), 2. panTo() autoZoomIn = newZoomLevel > map.getZoom(); autoZoomOut = !autoZoomIn; autoZoomLevel = newZoomLevel; @@ -1986,13 +2099,14 @@ public class RaceMap extends AbstractCompositeComponent impleme } else { map.panTo(autoZoomLatLngBounds.getCenter()); } - RaceMapZoomSettings restoredZoomSettings = new RaceMapZoomSettings(oldZoomTypesToConsiderSettings, settings.getZoomSettings().isZoomToSelectedCompetitors()); + RaceMapZoomSettings restoredZoomSettings = new RaceMapZoomSettings(oldZoomTypesToConsiderSettings, + settings.getZoomSettings().isZoomToSelectedCompetitors()); settings = new RaceMapSettings(settings, restoredZoomSettings); setAutoZoomInProgress(false); } } } - + private double graticuleAreaRatio(LatLngBounds containing, LatLngBounds contained) { assert BoundsUtil.contains(containing, contained); double containingAreaRatio = getGraticuleArea(containing) / getGraticuleArea(contained); @@ -2000,26 +2114,29 @@ public class RaceMap extends AbstractCompositeComponent impleme } /** - * A much simplified "area" calculation for a {@link Bounds} object, multiplying the differences in latitude and longitude degrees. - * The result therefore is in the order of magnitude of 60*60 square nautical miles. + * A much simplified "area" calculation for a {@link Bounds} object, multiplying the differences in latitude and + * longitude degrees. The result therefore is in the order of magnitude of 60*60 square nautical miles. */ private double getGraticuleArea(LatLngBounds bounds) { - return ((BoundsUtil.isCrossesDateLine(bounds) ? bounds.getNorthEast().getLongitude()+360 : bounds.getNorthEast().getLongitude())-bounds.getSouthWest().getLongitude()) * - (bounds.getNorthEast().getLatitude() - bounds.getSouthWest().getLatitude()); + return ((BoundsUtil.isCrossesDateLine(bounds) ? bounds.getNorthEast().getLongitude() + 360 + : bounds.getNorthEast().getLongitude()) - bounds.getSouthWest().getLongitude()) + * (bounds.getNorthEast().getLatitude() - bounds.getSouthWest().getLatitude()); } private void setAutoZoomInProgress(boolean autoZoomInProgress) { this.autoZoomInProgress = autoZoomInProgress; } - + boolean isAutoZoomInProgress() { return autoZoomInProgress; } - + /** - * @param timeForPositionTransitionMillis use -1 to not animate the position transition, e.g., during map zoom or non-play + * @param timeForPositionTransitionMillis + * use -1 to not animate the position transition, e.g., during map zoom or non-play */ - private boolean updateBoatCanvasForCompetitor(CompetitorDTO competitorDTO, Date date, long timeForPositionTransitionMillis) { + private boolean updateBoatCanvasForCompetitor(CompetitorDTO competitorDTO, Date date, + long timeForPositionTransitionMillis) { boolean hasTimeJumped = timeForPositionTransitionMillis > 3 * timer.getRefreshInterval(); if (hasTimeJumped) { timeForPositionTransitionMillis = -1; @@ -2029,7 +2146,8 @@ public class RaceMap extends AbstractCompositeComponent impleme if (lastBoatFix != null) { BoatOverlay boatOverlay = boatOverlays.get(competitorDTO); if (boatOverlay == null) { - boatOverlay = createBoatOverlay(RaceMapOverlaysZIndexes.BOATS_ZINDEX, competitorDTO, displayHighlighted(competitorDTO)); + boatOverlay = createBoatOverlay(RaceMapOverlaysZIndexes.BOATS_ZINDEX, competitorDTO, + displayHighlighted(competitorDTO)); boatOverlays.put(competitorDTO, boatOverlay); boatOverlay.setDisplayMode(displayHighlighted(competitorDTO)); boatOverlay.setBoatFix(lastBoatFix, timeForPositionTransitionMillis); @@ -2056,48 +2174,47 @@ public class RaceMap extends AbstractCompositeComponent impleme return DisplayMode.DEFAULT; } } - } - else{ + } else { return competitorSelection.isSelected(competitorDTO) ? DisplayMode.SELECTED : DisplayMode.DEFAULT; } - + } - - private boolean isSomeOtherCompetitorSelected(){ + + private boolean isSomeOtherCompetitorSelected() { return Util.size(competitorSelection.getSelectedCompetitors()) > 0; } - + private class CourseMarkInfoWindowClickHandler implements ClickMapHandler { private final MarkDTO markDTO; private final CourseMarkOverlay courseMarkOverlay; - + public CourseMarkInfoWindowClickHandler(MarkDTO markDTO, CourseMarkOverlay courseMarkOverlay) { this.markDTO = markDTO; this.courseMarkOverlay = courseMarkOverlay; } - + @Override public void onEvent(ClickMapEvent event) { LatLng latlng = courseMarkOverlay.getMarkLatLngPosition(); showMarkInfoWindow(markDTO, latlng); } } - + private void registerCourseMarkInfoWindowClickHandler(final String markDTOIdAsString) { final CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTOIdAsString); - courseMarkClickHandlers.put(markDTOIdAsString, - courseMarkOverlay.addClickHandler(new CourseMarkInfoWindowClickHandler(markDTOs.get(markDTOIdAsString), courseMarkOverlay))); + courseMarkClickHandlers.put(markDTOIdAsString, courseMarkOverlay.addClickHandler( + new CourseMarkInfoWindowClickHandler(markDTOs.get(markDTOIdAsString), courseMarkOverlay))); } - + public void registerAllCourseMarkInfoWindowClickHandlers() { for (String markDTOIdAsString : markDTOs.keySet()) { registerCourseMarkInfoWindowClickHandler(markDTOIdAsString); } } - + public void unregisterAllCourseMarkInfoWindowClickHandlers() { Iterator> iterator = courseMarkClickHandlers.entrySet().iterator(); - while(iterator.hasNext()) { + while (iterator.hasNext()) { Entry handler = iterator.next(); handler.getValue().removeHandler(); iterator.remove(); @@ -2106,7 +2223,8 @@ public class RaceMap extends AbstractCompositeComponent impleme private BoatOverlay createBoatOverlay(int zIndex, final CompetitorDTO competitorDTO, DisplayMode displayMode) { final BoatDTO boatOfCompetitor = competitorSelection.getBoat(competitorDTO); - final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, boatOfCompetitor, competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); + final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, boatOfCompetitor, + competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); boatCanvas.setDisplayMode(displayMode); boatCanvas.addClickHandler(event -> { GPSFixDTOWithSpeedWindTackAndLegType latestFixForCompetitor = getBoatFix(competitorDTO, timer.getTime()); @@ -2131,8 +2249,10 @@ public class RaceMap extends AbstractCompositeComponent impleme return boatCanvas; } - protected WindSensorOverlay createWindSensorOverlay(int zIndex, final WindSource windSource, final WindTrackInfoDTO windTrackInfoDTO) { - final WindSensorOverlay windSensorOverlay = new WindSensorOverlay(map, zIndex, raceMapImageManager, stringMessages, coordinateSystem); + protected WindSensorOverlay createWindSensorOverlay(int zIndex, final WindSource windSource, + final WindTrackInfoDTO windTrackInfoDTO) { + final WindSensorOverlay windSensorOverlay = new WindSensorOverlay(map, zIndex, raceMapImageManager, + stringMessages, coordinateSystem); windSensorOverlay.setWindInfo(windTrackInfoDTO, windSource); windSensorOverlay.addClickHandler(new ClickMapHandler() { @Override @@ -2183,12 +2303,14 @@ public class RaceMap extends AbstractCompositeComponent impleme flowPanel.add(value); return flowPanel; } - + private Widget getInfoWindowContent(MarkDTO markDTO) { VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.mark(), markDTO.getName())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsSignedDecimalDegrees())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), + markDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); + vPanel.add( + createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsSignedDecimalDegrees())); return vPanel; } @@ -2223,9 +2345,15 @@ public class RaceMap extends AbstractCompositeComponent impleme } if (maneuver.getManeuverLoss() != null) { Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), - numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), + maneuver.getType()); + if (!maneuverLossCheckBoxValueStore.contains(t) && settings.isShowManeuverLossVisualization()) { + maneuverLossCheckBoxValueStore.add(t); + visualizeManeuverLoss(maneuver, true, competitor); + } maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.contains(t)); maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { @Override @@ -2244,13 +2372,12 @@ public class RaceMap extends AbstractCompositeComponent impleme vPanel.add(hPanel); } if (maneuver.getMarkPassingTimePoint() != null) { - vPanel.add( - createInfoWindowLabelAndValue(stringMessages.markPassing(), - DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getMarkPassingTimePoint()) - + (maneuver.getMarkPassingSide() == null ? "" - : " (" + (maneuver.getMarkPassingSide() == NauticalSide.PORT - ? stringMessages.portSide() : stringMessages.starboardSide()) - + ")"))); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.markPassing(), + DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getMarkPassingTimePoint()) + + (maneuver.getMarkPassingSide() == null ? "" + : " (" + (maneuver.getMarkPassingSide() == NauticalSide.PORT + ? stringMessages.portSide() + : stringMessages.starboardSide()) + ")"))); } return vPanel; } @@ -2260,11 +2387,14 @@ public class RaceMap extends AbstractCompositeComponent impleme final VerticalPanel vPanel = new VerticalPanel(); final Anchor windSourceNameAnchor = new Anchor(WindSourceTypeFormatter.format(windSource, stringMessages)); vPanel.add(createInfoWindowLabelWithWidget(stringMessages.windSource(), windSourceNameAnchor)); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.wind(), Math.round(windDTO.dampenedTrueWindFromDeg) + " " + stringMessages.degreesShort())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.windSpeed(), numberFormatOneDecimal.format(windDTO.dampenedTrueWindSpeedInKnots))); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.wind(), + Math.round(windDTO.dampenedTrueWindFromDeg) + " " + stringMessages.degreesShort())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.windSpeed(), + numberFormatOneDecimal.format(windDTO.dampenedTrueWindSpeedInKnots))); final MillisecondsTimePoint timePoint = new MillisecondsTimePoint(windDTO.measureTimepoint); vPanel.add(createInfoWindowLabelAndValue(stringMessages.time(), timePoint.asDate().toString())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), windDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), + windDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); final Label positionInDecimalDegreesLabel = new Label(windDTO.position.getAsSignedDecimalDegrees()); positionInDecimalDegreesLabel.setWordWrap(false); positionInDecimalDegreesLabel.getElement().getStyle().setFloat(Style.Float.LEFT); @@ -2304,7 +2434,8 @@ public class RaceMap extends AbstractCompositeComponent impleme private Widget getInfoWindowContent(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType lastFix) { final VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.competitor(), competitorDTO.getName())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), competitorSelection.getBoat(competitorDTO).getSailId())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), + competitorSelection.getBoat(competitorDTO).getSailId())); final Integer rank = getRank(competitorDTO); if (rank != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.rank(), String.valueOf(rank))); @@ -2315,8 +2446,10 @@ public class RaceMap extends AbstractCompositeComponent impleme speedWithBearing = new SpeedWithBearingDTO(0, 0); } vPanel.add(createInfoWindowLabelAndValue(stringMessages.speed(), - NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " "+stringMessages.knotsUnit())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), (int) speedWithBearing.bearingInDegrees + " "+stringMessages.degreesShort())); + NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " " + + stringMessages.knotsUnit())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), + (int) speedWithBearing.bearingInDegrees + " " + stringMessages.degreesShort())); if (lastFix.degreesBoatToTheWind != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.degreesBoatToTheWind(), (int) Math.abs(lastFix.degreesBoatToTheWind) + " " + stringMessages.degreesShort())); @@ -2333,11 +2466,13 @@ public class RaceMap extends AbstractCompositeComponent impleme new AsyncCallback>>() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), true /*silentMode */); + errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), + true /* silentMode */); } @Override - public void onSuccess(Map> result) { + public void onSuccess( + Map> result) { lastDouglasPeuckerResult = result; if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); @@ -2349,10 +2484,12 @@ public class RaceMap extends AbstractCompositeComponent impleme } } }); - sailingService.getManeuvers(race, timeRange, new AsyncCallback>>() { + sailingService.getManeuvers(race, timeRange, + new AsyncCallback>>() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), true /*silentMode */); + errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), + true /* silentMode */); } @Override @@ -2375,31 +2512,34 @@ public class RaceMap extends AbstractCompositeComponent impleme /** * @return the {@link CompetitorSelectionProvider#getSelectedCompetitors()} if * {@link RaceMapSettings#isShowOnlySelectedCompetitors() only selected competitors are to be shown}, the - * {@link CompetitorSelectionProvider#getFilteredCompetitors() filtered competitors} otherwise. In both cases, - * if we have {@link RaceCompetitorSet information about the competitors participating} in this map's race, - * the result set is reduced to those, no matter if other regatta participants would otherwise have been in - * the result set + * {@link CompetitorSelectionProvider#getFilteredCompetitors() filtered competitors} otherwise. In both + * cases, if we have {@link RaceCompetitorSet information about the competitors participating} in this map's + * race, the result set is reduced to those, no matter if other regatta participants would otherwise have + * been in the result set */ private Iterable getCompetitorsToShow() { final Set result = new HashSet<>(); Iterable selection = competitorSelection.getSelectedCompetitors(); - final Set raceCompetitorIdsAsString = raceCompetitorSet.getIdsOfCompetitorsParticipatingInRaceAsStrings(); + final Set raceCompetitorIdsAsString = raceCompetitorSet + .getIdsOfCompetitorsParticipatingInRaceAsStrings(); if (!settings.isShowOnlySelectedCompetitors() || Util.isEmpty(selection)) { for (final CompetitorDTO filteredCompetitor : competitorSelection.getFilteredCompetitors()) { - if (raceCompetitorIdsAsString == null || raceCompetitorIdsAsString.contains(filteredCompetitor.getIdAsString())) { + if (raceCompetitorIdsAsString == null + || raceCompetitorIdsAsString.contains(filteredCompetitor.getIdAsString())) { result.add(filteredCompetitor); } } } else { for (final CompetitorDTO selectedCompetitor : selection) { - if (raceCompetitorIdsAsString == null || raceCompetitorIdsAsString.contains(selectedCompetitor.getIdAsString())) { + if (raceCompetitorIdsAsString == null + || raceCompetitorIdsAsString.contains(selectedCompetitor.getIdAsString())) { result.add(selectedCompetitor); } } } return result; } - + protected void removeAllMarkDouglasPeuckerpoints() { if (douglasMarkers != null) { for (Marker marker : douglasMarkers) { @@ -2418,7 +2558,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } } - private void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { + private void showMarkDouglasPeuckerPoints( + Map> gpsFixPointMapForCompetitors) { douglasMarkers = new HashSet(); if (map != null && gpsFixPointMapForCompetitors != null) { Set keySet = gpsFixPointMapForCompetitors.keySet(); @@ -2429,7 +2570,7 @@ public class RaceMap extends AbstractCompositeComponent impleme for (GPSFixDTOWithSpeedWindTackAndLegType fix : gpsFix) { LatLng latLng = coordinateSystem.toLatLng(fix.position); MarkerOptions options = MarkerOptions.newInstance(); - options.setTitle(fix.timepoint+": "+fix.position+", "+fix.speedWithBearing.toString()); + options.setTitle(fix.timepoint + ": " + fix.position + ", " + fix.speedWithBearing.toString()); Marker marker = Marker.newInstance(options); marker.setPosition(latLng); douglasMarkers.add(marker); @@ -2458,8 +2599,8 @@ public class RaceMap extends AbstractCompositeComponent impleme private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { LatLng latLng = coordinateSystem.toLatLng(maneuver.getPosition()); - Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor - .get(new Pair(maneuver.getType(), ManeuverColor.getManeuverColor(maneuver))); + Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor.get( + new Pair(maneuver.getType(), ManeuverColor.getManeuverColor(maneuver))); MarkerOptions options = MarkerOptions.newInstance(); options.setIcon(maneuverMarker.getIcon_MarkerImage()); Marker marker = Marker.newInstance(options); @@ -2486,37 +2627,45 @@ public class RaceMap extends AbstractCompositeComponent impleme GPSFixDTOWithSpeedWindTackAndLegType result = null; List competitorFixes = fixesAndTails.getFixes(competitorDTO); if (competitorFixes != null && !competitorFixes.isEmpty()) { - int i = Collections.binarySearch(competitorFixes, new GPSFixDTOWithSpeedWindTackAndLegType(date, null, null, (WindDTO) null, null, null, false), + int i = Collections.binarySearch(competitorFixes, + new GPSFixDTOWithSpeedWindTackAndLegType(date, null, null, (WindDTO) null, null, null, false), new Comparator() { - @Override - public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, GPSFixDTOWithSpeedWindTackAndLegType o2) { - return o1.timepoint.compareTo(o2.timepoint); - } - }); - if (i<0) { - i = -i-1; // no perfect match; i is now the insertion point + @Override + public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, + GPSFixDTOWithSpeedWindTackAndLegType o2) { + return o1.timepoint.compareTo(o2.timepoint); + } + }); + if (i < 0) { + i = -i - 1; // no perfect match; i is now the insertion point // if the insertion point is at the end, use last fix if (i >= competitorFixes.size()) { - result = competitorFixes.get(competitorFixes.size()-1); + result = competitorFixes.get(competitorFixes.size() - 1); } else if (i == 0) { // if the insertion point is at the beginning, use first fix result = competitorFixes.get(0); } else { - // competitorFixes must have at least two elements, and i points neither to the end nor the beginning; + // competitorFixes must have at least two elements, and i points neither to the end nor the + // beginning; // get the fix from i and i+1 whose timepoint is closer to date - final GPSFixDTOWithSpeedWindTackAndLegType fixBefore = competitorFixes.get(i-1); + final GPSFixDTOWithSpeedWindTackAndLegType fixBefore = competitorFixes.get(i - 1); final GPSFixDTOWithSpeedWindTackAndLegType fixAfter = competitorFixes.get(i); final GPSFixDTOWithSpeedWindTackAndLegType closer; - if (date.getTime() - fixBefore.timepoint.getTime() < fixAfter.timepoint.getTime() - date.getTime()) { + if (date.getTime() - fixBefore.timepoint.getTime() < fixAfter.timepoint.getTime() + - date.getTime()) { closer = fixBefore; } else { closer = fixAfter; } // now compute a weighted average depending on the time difference to "date" (see also bug 1924) - double factorForAfter = (double) (date.getTime()-fixBefore.timepoint.getTime()) / (double) (fixAfter.timepoint.getTime() - fixBefore.timepoint.getTime()); - double factorForBefore = 1-factorForAfter; - DegreePosition betweenPosition = new DegreePosition(factorForBefore*fixBefore.position.getLatDeg() + factorForAfter*fixAfter.position.getLatDeg(), - factorForBefore*fixBefore.position.getLngDeg() + factorForAfter*fixAfter.position.getLngDeg()); + double factorForAfter = (double) (date.getTime() - fixBefore.timepoint.getTime()) + / (double) (fixAfter.timepoint.getTime() - fixBefore.timepoint.getTime()); + double factorForBefore = 1 - factorForAfter; + DegreePosition betweenPosition = new DegreePosition( + factorForBefore * fixBefore.position.getLatDeg() + + factorForAfter * fixAfter.position.getLatDeg(), + factorForBefore * fixBefore.position.getLngDeg() + + factorForAfter * fixAfter.position.getLngDeg()); final double betweenBearing; if (fixBefore.speedWithBearing == null) { if (fixAfter.speedWithBearing == null) { @@ -2527,16 +2676,22 @@ public class RaceMap extends AbstractCompositeComponent impleme } else if (fixAfter.speedWithBearing == null) { betweenBearing = fixBefore.speedWithBearing.bearingInDegrees; } else { - betweenBearing = new ScalableBearing(new DegreeBearingImpl(fixBefore.speedWithBearing.bearingInDegrees)). - multiply(factorForBefore).add(new ScalableBearing(new DegreeBearingImpl(fixAfter.speedWithBearing.bearingInDegrees)). - multiply(factorForAfter)).divide(1).getDegrees(); + betweenBearing = new ScalableBearing( + new DegreeBearingImpl(fixBefore.speedWithBearing.bearingInDegrees)) + .multiply(factorForBefore) + .add(new ScalableBearing( + new DegreeBearingImpl(fixAfter.speedWithBearing.bearingInDegrees)) + .multiply(factorForAfter)) + .divide(1).getDegrees(); } - SpeedWithBearingDTO betweenSpeed = new SpeedWithBearingDTO( - factorForBefore*(fixBefore.speedWithBearing==null?0:fixBefore.speedWithBearing.speedInKnots) + - factorForAfter*(fixAfter.speedWithBearing==null?0:fixAfter.speedWithBearing.speedInKnots), + SpeedWithBearingDTO betweenSpeed = new SpeedWithBearingDTO(factorForBefore + * (fixBefore.speedWithBearing == null ? 0 : fixBefore.speedWithBearing.speedInKnots) + + factorForAfter + * (fixAfter.speedWithBearing == null ? 0 : fixAfter.speedWithBearing.speedInKnots), betweenBearing); - result = new GPSFixDTOWithSpeedWindTackAndLegType(date, betweenPosition, betweenSpeed, closer.degreesBoatToTheWind, - closer.tack, closer.legType, fixBefore.extrapolated || fixAfter.extrapolated); + result = new GPSFixDTOWithSpeedWindTackAndLegType(date, betweenPosition, betweenSpeed, + closer.degreesBoatToTheWind, closer.tack, closer.legType, + fixBefore.extrapolated || fixAfter.extrapolated); } } else { // perfect match @@ -2589,7 +2744,8 @@ public class RaceMap extends AbstractCompositeComponent impleme for (CompetitorDTO oneOfAllCompetitors : competitorSelection.getAllCompetitors()) { Polyline tail = fixesAndTails.getTail(oneOfAllCompetitors); if (tail != null) { - PolylineOptions newOptions = createTailStyle(oneOfAllCompetitors, displayHighlighted(oneOfAllCompetitors)); + PolylineOptions newOptions = createTailStyle(oneOfAllCompetitors, + displayHighlighted(oneOfAllCompetitors)); tail.setOptions(newOptions); } } @@ -2599,7 +2755,7 @@ public class RaceMap extends AbstractCompositeComponent impleme zoomMapToNewBounds(zoomSettings.getNewBounds(this)); } } - + @Override public void removedFromSelection(CompetitorDTO competitor) { if (isShowAnyHelperLines()) { @@ -2631,7 +2787,7 @@ public class RaceMap extends AbstractCompositeComponent impleme showCompetitorInfoOnMap(timer.getTime(), -1, competitorSelection.getSelectedFilteredCompetitors()); } } - //Trigger auto-zoom if needed + // Trigger auto-zoom if needed RaceMapZoomSettings zoomSettings = settings.getZoomSettings(); if (!zoomSettings.containsZoomType(ZoomTypes.NONE) && zoomSettings.isZoomToSelectedCompetitors()) { zoomMapToNewBounds(zoomSettings.getNewBounds(this)); @@ -2667,7 +2823,7 @@ public class RaceMap extends AbstractCompositeComponent impleme boolean maneuverTypeSelectionChanged = false; boolean requiresRedraw = false; boolean requiresUpdateCoordinateSystem = false; - + for (ManeuverType maneuverType : ManeuverType.values()) { if (newSettings.isShowManeuverType(maneuverType) != settings.isShowManeuverType(maneuverType)) { maneuverTypeSelectionChanged = true; @@ -2681,7 +2837,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } } if (newSettings.isShowDouglasPeuckerPoints() != settings.isShowDouglasPeuckerPoints()) { - if (!(timer.getPlayState() == PlayStates.Playing) && lastDouglasPeuckerResult != null && newSettings.isShowDouglasPeuckerPoints()) { + if (!(timer.getPlayState() == PlayStates.Playing) && lastDouglasPeuckerResult != null + && newSettings.isShowDouglasPeuckerPoints()) { removeAllMarkDouglasPeuckerpoints(); showMarkDouglasPeuckerPoints(lastDouglasPeuckerResult); } else if (!newSettings.isShowDouglasPeuckerPoints()) { @@ -2709,7 +2866,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (!newSettings.getHelpLinesSettings().equals(settings.getHelpLinesSettings())) { requiresRedraw = true; } - if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null){ + if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null) { estimatedDurationOverlay.removeFromParent(); } if (newSettings.isShowWindStreamletOverlay() != settings.isShowWindStreamletOverlay()) { @@ -2725,7 +2882,7 @@ public class RaceMap extends AbstractCompositeComponent impleme requiresUpdateCoordinateSystem = true; requiresRedraw = true; } - if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null){ + if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null) { estimatedDurationOverlay.removeFromParent(); } if (newSettings.getStartCountDownFontSizeScaling() != settings.getStartCountDownFontSizeScaling()) { @@ -2736,7 +2893,7 @@ public class RaceMap extends AbstractCompositeComponent impleme requiresRedraw = true; } this.settings = newSettings; - + if (requiresUpdateCoordinateSystem) { updateCoordinateSystemFromSettings(); } @@ -2748,7 +2905,7 @@ public class RaceMap extends AbstractCompositeComponent impleme private void showSimulationOverlay(boolean visible) { simulationOverlay.setVisible(visible); if (visible) { - simulationOverlay.updateLeg(Math.max(lastLegNumber,1), true, -1 /* ensure ui-update */); + simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), true, -1 /* ensure ui-update */); } } @@ -2761,11 +2918,13 @@ public class RaceMap extends AbstractCompositeComponent impleme if (selectedCompetitors == null || !selectedCompetitors.iterator().hasNext()) { competitors = forMap.getCompetitorsToShow(); } else { - competitors = isZoomOnlyToSelectedCompetitors(forMap) ? selectedCompetitors : forMap.getCompetitorsToShow(); + competitors = isZoomOnlyToSelectedCompetitors(forMap) ? selectedCompetitors + : forMap.getCompetitorsToShow(); } for (CompetitorDTO competitor : competitors) { try { - GPSFixDTOWithSpeedWindTackAndLegType competitorFix = forMap.getBoatFix(competitor, forMap.timer.getTime()); + GPSFixDTOWithSpeedWindTackAndLegType competitorFix = forMap.getBoatFix(competitor, + forMap.timer.getTime()); Position competitorPosition = competitorFix != null ? competitorFix.position : null; if (competitorPosition != null) { if (newBounds == null) { @@ -2776,25 +2935,30 @@ public class RaceMap extends AbstractCompositeComponent impleme } } catch (IndexOutOfBoundsException e) { // TODO can't this be predicted and the exception be avoided in the first place? - // Catch this in case the competitor has no GPS fixes at the current time (e.g. in race 'Finale 2' of STG) + // Catch this in case the competitor has no GPS fixes at the current time (e.g. in race 'Finale 2' + // of STG) } } return newBounds; } - + } - + public static class TailsBoundsCalculator extends LatLngBoundsCalculatorForSelected { @Override public LatLngBounds calculateNewBounds(RaceMap racemap) { LatLngBounds newBounds = null; - Iterable competitors = isZoomOnlyToSelectedCompetitors(racemap) ? racemap.competitorSelection.getSelectedCompetitors() : racemap.getCompetitorsToShow(); + Iterable competitors = isZoomOnlyToSelectedCompetitors(racemap) + ? racemap.competitorSelection.getSelectedCompetitors() + : racemap.getCompetitorsToShow(); for (CompetitorDTO competitor : competitors) { Polyline tail = racemap.fixesAndTails.getTail(competitor); LatLngBounds bounds = null; // TODO: Find a replacement for missing Polyline function getBounds() from v2 - // see also http://stackoverflow.com/questions/3284808/getting-the-bounds-of-a-polyine-in-google-maps-api-v3; - // optionally, consider providing a bounds cache with two sorted sets that organize the LatLng objects for O(1) bounds calculation and logarithmic add, ideally O(1) remove + // see also + // http://stackoverflow.com/questions/3284808/getting-the-bounds-of-a-polyine-in-google-maps-api-v3; + // optionally, consider providing a bounds cache with two sorted sets that organize the LatLng objects + // for O(1) bounds calculation and logarithmic add, ideally O(1) remove if (tail != null && tail.getPath().getLength() >= 1) { bounds = BoundsUtil.getAsBounds(tail.getPath().get(0)); for (int i = 1; i < tail.getPath().getLength(); i++) { @@ -2812,7 +2976,7 @@ public class RaceMap extends AbstractCompositeComponent impleme return newBounds; } } - + public static class CourseMarksBoundsCalculator implements LatLngBoundsCalculator { @Override public LatLngBounds calculateNewBounds(RaceMap forMap) { @@ -2869,17 +3033,17 @@ public class RaceMap extends AbstractCompositeComponent impleme map.triggerResize(); zoomMapToNewBounds(settings.getZoomSettings().getNewBounds(RaceMap.this)); } - // Adjust RaceMap headers to avoid overlapping based on the RaceMap width + // Adjust RaceMap headers to avoid overlapping based on the RaceMap width boolean isCompactHeader = this.getOffsetWidth() <= 600; getLeftHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); getRightHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); - + // Adjust combined wind and true north indicator panel indent, based on the RaceMap height if (topLeftControlsWrapperPanel.getParent() != null) { this.adjustLeftControlsIndent(); } } - + private void adjustLeftControlsIndent() { topLeftControlsWrapperPanel.getParent().setStyleName("TopLeftControlsWrapperPanelParentDiv"); String leftControlsIndentStyle = getLeftControlsIndentStyle(); @@ -2892,16 +3056,17 @@ public class RaceMap extends AbstractCompositeComponent impleme public void competitorsListChanged(Iterable competitors) { timeChanged(timer.getTime(), null); } - + @Override public void filteredCompetitorsListChanged(Iterable filteredCompetitors) { timeChanged(timer.getTime(), null); } - + @Override public void filterChanged(FilterSet> oldFilterSet, FilterSet> newFilterSet) { - // nothing to do; if the list of filtered competitors has changed, a separate call to filteredCompetitorsListChanged will occur + // nothing to do; if the list of filtered competitors has changed, a separate call to + // filteredCompetitorsListChanged will occur } @Override @@ -2911,7 +3076,7 @@ public class RaceMap extends AbstractCompositeComponent impleme options.setGeodesic(true); options.setStrokeOpacity(1.0); - switch(displayMode){ + switch (displayMode) { case DEFAULT: options.setStrokeColor(competitorSelection.getColor(competitor, raceIdentifier).getAsHtml()); options.setStrokeWeight(1); @@ -2929,7 +3094,7 @@ public class RaceMap extends AbstractCompositeComponent impleme options.setZindex(RaceMapOverlaysZIndexes.BOATTAILS_ZINDEX); return options; } - + @Override public Polyline createTail(final CompetitorDTO competitor, List points) { final BoatDTO boat = competitorSelection.getBoat(competitor); @@ -2973,7 +3138,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } return result; } - + private Image createSAPLogo() { ImageResource sapLogoResource = resources.sapLogoOverlay(); Image sapLogo = new Image(sapLogoResource); @@ -3001,42 +3166,42 @@ public class RaceMap extends AbstractCompositeComponent impleme private MapOptions getMapOptions(final boolean showMapControls, boolean windUp) { MapOptions mapOptions = MapOptions.newInstance(); - mapOptions.setScrollWheel(true); - mapOptions.setMapTypeControl(showMapControls && !windUp); - mapOptions.setPanControl(showMapControls); - mapOptions.setZoomControl(showMapControls); - mapOptions.setScaleControl(true); - if (windUp) { - mapOptions.setMinZoom(8); - } else { - mapOptions.setMinZoom(0); - } - MapTypeStyle[] mapTypeStyles = new MapTypeStyle[4]; - - // hide all transit lines including ferry lines - mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT); - // hide points of interest - mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI); - // simplify road display - mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD); - // set water color - mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, WATER_COLOR); - - MapTypeControlOptions mapTypeControlOptions = MapTypeControlOptions.newInstance(); - mapTypeControlOptions.setPosition(ControlPosition.BOTTOM_RIGHT); - mapOptions.setMapTypeControlOptions(mapTypeControlOptions); + mapOptions.setScrollWheel(true); + mapOptions.setMapTypeControl(showMapControls && !windUp); + mapOptions.setPanControl(showMapControls); + mapOptions.setZoomControl(showMapControls); + mapOptions.setScaleControl(true); + if (windUp) { + mapOptions.setMinZoom(8); + } else { + mapOptions.setMinZoom(0); + } + MapTypeStyle[] mapTypeStyles = new MapTypeStyle[4]; - mapOptions.setMapTypeStyles(mapTypeStyles); - // no need to try to position the scale control; it always ends up at the right bottom corner - mapOptions.setStreetViewControl(false); - if (showMapControls) { - ZoomControlOptions zoomControlOptions = ZoomControlOptions.newInstance(); - zoomControlOptions.setPosition(ControlPosition.RIGHT_TOP); - mapOptions.setZoomControlOptions(zoomControlOptions); - PanControlOptions panControlOptions = PanControlOptions.newInstance(); - panControlOptions.setPosition(ControlPosition.RIGHT_TOP); - mapOptions.setPanControlOptions(panControlOptions); - } + // hide all transit lines including ferry lines + mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT); + // hide points of interest + mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI); + // simplify road display + mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD); + // set water color + mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, WATER_COLOR); + + MapTypeControlOptions mapTypeControlOptions = MapTypeControlOptions.newInstance(); + mapTypeControlOptions.setPosition(ControlPosition.BOTTOM_RIGHT); + mapOptions.setMapTypeControlOptions(mapTypeControlOptions); + + mapOptions.setMapTypeStyles(mapTypeStyles); + // no need to try to position the scale control; it always ends up at the right bottom corner + mapOptions.setStreetViewControl(false); + if (showMapControls) { + ZoomControlOptions zoomControlOptions = ZoomControlOptions.newInstance(); + zoomControlOptions.setPosition(ControlPosition.RIGHT_TOP); + mapOptions.setZoomControlOptions(zoomControlOptions); + PanControlOptions panControlOptions = PanControlOptions.newInstance(); + panControlOptions.setPosition(ControlPosition.RIGHT_TOP); + mapOptions.setPanControlOptions(panControlOptions); + } return mapOptions; } @@ -3050,11 +3215,11 @@ public class RaceMap extends AbstractCompositeComponent impleme public Map getCourseMarkOverlays() { return courseMarkOverlays; } - + public CoordinateSystem getCoordinateSystem() { return coordinateSystem; } - + public void hideAllHelplines() { if (startLine != null) { startLine.setVisible(false); @@ -3080,7 +3245,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + public void showAllHelplinesToShow() { if (settings.getHelpLinesSettings().isShowAnyHelperLines()) { if (startLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE)) @@ -3089,8 +3254,8 @@ public class RaceMap extends AbstractCompositeComponent impleme finishLine.setVisible(true); if (advantageLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) advantageLine.setVisible(true); - if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null && - settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) { + if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null + && settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) { windwardStartLineMarkToFirstMarkLine.setVisible(true); leewardStartLineMarkToFirstMarkLine.setVisible(true); } @@ -3106,7 +3271,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + public void addCompetitorsForRaceDefinedListener(CompetitorsForRaceDefinedListener listener) { raceCompetitorSet.addCompetitorsForRaceDefinedListener(listener); } @@ -3119,15 +3284,16 @@ public class RaceMap extends AbstractCompositeComponent impleme public String getId() { return raceMapLifecycle.getComponentId(); } - + public RaceMapLifecycle getLifecycle() { return raceMapLifecycle; } + final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.middleManeuverAngle(); - } + @Override + public String getLineInfo() { + return stringMessages.middleManeuverAngle(); + } }; final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { @Override @@ -3186,7 +3352,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } } maneuverLossLinesMap.clear(); - for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap.entrySet()) { + for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap + .entrySet()) { e.getValue().removeFromMap(); } maneuverLossInfoOverlayMap.clear(); @@ -3201,18 +3368,20 @@ public class RaceMap extends AbstractCompositeComponent impleme final Set maneuverLossLines = new HashSet<>(); Bearing bearingBefore = maneuver.getManeuverLoss().getSpeedWithBearingBefore().getBearing(); Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.getManeuverLoss().getMiddleManeuverAngle()); - Distance extrapolationOfManeuverStartPoint = - maneuver.getManeuverLoss().getSpeedWithBearingBefore().travel(maneuver.getManeuverLoss().getManeuverDuration()); + Distance extrapolationOfManeuverStartPoint = maneuver.getManeuverLoss().getSpeedWithBearingBefore() + .travel(maneuver.getManeuverLoss().getManeuverDuration()); Position extrapolatedManeuverStartPosition = maneuver.getManeuverLoss().getManeuverStartPosition() .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); - Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.getManeuverLoss().getManeuverStartPosition() - .getIntersection(bearingBefore, maneuver.getPosition(), middleManeuverAngle); + Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.getManeuverLoss() + .getManeuverStartPosition().getIntersection(bearingBefore, maneuver.getPosition(), middleManeuverAngle); Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition() + .projectToLineThrough(intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, + middleManeuverAngle); String color = maneuver.getManeuverLoss().getDistanceLost().compareTo(Distance.NULL) > 0 - ? color = MANEUVERLOSSLINES_RED : MANEUVERLOSSLINES_GREEN; + ? color = MANEUVERLOSSLINES_RED + : MANEUVERLOSSLINES_GREEN; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, 1, 0.5)); @@ -3223,7 +3392,7 @@ public class RaceMap extends AbstractCompositeComponent impleme projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, 1, 0.3)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverEndPosition(), - projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, 1, 0.3)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 2, 1.0)); @@ -3231,7 +3400,8 @@ public class RaceMap extends AbstractCompositeComponent impleme maneuver.getType()); maneuverLossLinesMap.put(t, maneuverLossLines); StringBuilder sb = new StringBuilder(); - sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + sb.append(stringMessages.maneuverLoss() + ": " + + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + stringMessages.metersUnit() + '\n'); if (maneuver.getType() == ManeuverType.TACK) { sb.append(stringMessages.tackAngle() + ": "); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java index 9eb8a6ab5f5..8d65bd36dc5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java @@ -53,7 +53,7 @@ public class RaceMapLifecycle implements ComponentLifecycle { settings.isShowOnlySelectedCompetitors(), settings.isShowSelectedCompetitorsInfo(), settings.isShowWindStreamletColors(), settings.isShowWindStreamletOverlay(), settings.isShowSimulationOverlay(), settings.isShowMapControls(), settings.getManeuverTypesToShow(), - settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling()); + settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling(), settings.isShowManeuverLossVisualization()); } @Override @@ -65,6 +65,6 @@ public class RaceMapLifecycle implements ComponentLifecycle { settings.isShowOnlySelectedCompetitors(), settings.isShowSelectedCompetitorsInfo(), settings.isShowWindStreamletColors(), settings.isShowWindStreamletOverlay(), settings.isShowSimulationOverlay(), settings.isShowMapControls(), settings.getManeuverTypesToShow(), - settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling()); + settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling(), settings.isShowManeuverLossVisualization()); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java index ecf6a13ce1e..95549f248ba 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java @@ -60,6 +60,8 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { private BooleanSetting showMapControls; + private BooleanSetting showManeuverLossVisualization; + /** * If true, all map contents will be transformed to a water-only environment, rotating all directions / * bearings / headings so that an assumed average wind direction for the race is coming from the top of the map @@ -97,6 +99,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { showDouglasPeuckerPoints = new BooleanSetting("showDouglasPeuckerPoints", this, false); showEstimatedDuration = new BooleanSetting("showEstimatedDuration", this, false); startCountDownFontSizeScaling = new DoubleSetting("startCountDownFontSizeScaling", this, 1.0); + showManeuverLossVisualization = new BooleanSetting("showManeuverLossVisualization", this, true); } public RaceMapSettings() { @@ -107,7 +110,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { Distance buoyZoneRadius, Boolean showOnlySelectedCompetitors, Boolean showSelectedCompetitorsInfo, Boolean showWindStreamletColors, Boolean showWindStreamletOverlay, Boolean showSimulationOverlay, Boolean showMapControls, Collection maneuverTypesToShow, Boolean showDouglasPeuckerPoints, - Boolean showEstimatedDuration, Double startCountDownFontSizeScaling) { + Boolean showEstimatedDuration, Double startCountDownFontSizeScaling, Boolean showManeuverLossVisualization) { this.zoomSettings.init(zoomSettings); this.helpLinesSettings.init(helpLinesSettings); this.transparentHoverlines.setValue(transparentHoverlines); @@ -125,11 +128,12 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { this.showDouglasPeuckerPoints.setValue(showDouglasPeuckerPoints); this.showEstimatedDuration.setValue(showEstimatedDuration); this.startCountDownFontSizeScaling.setValue(startCountDownFontSizeScaling); + this.showManeuverLossVisualization.setValue(showManeuverLossVisualization); } - public static RaceMapSettings getDefaultWithShowMapControls(boolean showMapControlls) { + public static RaceMapSettings getDefaultWithShowMapControls(boolean showMapControls) { RaceMapSettings raceMapSetting = new RaceMapSettings(); - raceMapSetting.showMapControls.setValue(showMapControlls); + raceMapSetting.showMapControls.setValue(showMapControls); return raceMapSetting; } @@ -155,7 +159,9 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { /* maneuverTypesToShow */ getDefaultManeuvers(), /* showDouglasPeuckerPoints */ false, /* showEstimatedDuration*/ false, - /* startCountDownFontSizeScaling */ 1.0); + /* startCountDownFontSizeScaling */ 1.0, + /* showManeuverLossVisualization */ true); + } private static Set createHelpLineSettings(boolean showCourseGeometry) { @@ -197,7 +203,8 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { /* maneuverTypesToShow */ settings.getManeuverTypesToShow(), /* showDouglasPeuckerPoints */ settings.isShowDouglasPeuckerPoints(), /* showEstimatedDuration */ settings.isShowEstimatedDuration(), - /* startCountDownFontSizeScaling */ settings.getStartCountDownFontSizeScaling()); + /* startCountDownFontSizeScaling */ settings.getStartCountDownFontSizeScaling(), + /* showManeuverLossVisualization */ settings.isShowManeuverLossVisualization()); } /** @@ -271,6 +278,10 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { return startCountDownFontSizeScaling.getValue(); } + public boolean isShowManeuverLossVisualization() { + return showManeuverLossVisualization.getValue(); + } + public static RaceMapSettings createSettingsWithNewBuoyZoneRadius(RaceMapSettings settings, Distance newDefaultBuoyZoneRadius) { final RaceMapSettings newRaceMapSettings = new RaceMapSettings( settings.getZoomSettings(), settings.getHelpLinesSettings(), @@ -284,7 +295,8 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { settings.isShowSimulationOverlay(), settings.isShowMapControls(), settings.getManeuverTypesToShow(), settings.isShowDouglasPeuckerPoints(),settings.isShowEstimatedDuration(), - settings.getStartCountDownFontSizeScaling()); + settings.getStartCountDownFontSizeScaling(), + settings.isShowManeuverLossVisualization()); return newRaceMapSettings; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java index 0ee72988649..674b36b6b9a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java @@ -63,6 +63,8 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent disableOnlySelectedWhenAreFalse; private CheckBox showEstimatedDuration; + private CheckBox showManeuverLossVisualizationCheckBox; + public RaceMapSettingsDialogComponent(RaceMapSettings settings, StringMessages stringMessages, boolean isSimulationEnabled) { this.isSimulationEnabled = isSimulationEnabled; this.stringMessages = stringMessages; @@ -161,7 +163,7 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java index 9ab82b6f8f0..bfd1ccd3c8c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java @@ -81,7 +81,8 @@ public class StartAnalysisMode extends RaceBoardModeWithPerRaceCompetitors { defaultSettings.getManeuverTypesToShow(), defaultSettings.isShowDouglasPeuckerPoints(), defaultSettings.isShowEstimatedDuration(), - defaultSettings.getStartCountDownFontSizeScaling()); + defaultSettings.getStartCountDownFontSizeScaling(), + defaultSettings.isShowManeuverLossVisualization()); ((RaceBoardComponentContext) raceMap.getComponentContext()).addModesPatching(raceMap, additiveSettings, new OnSettingsReloadedCallback() { @Override public void onSettingsReloaded(RaceMapSettings patchedSettings) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java index bfb6d5ff1a6..e8621d61e5d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java @@ -77,7 +77,8 @@ public class WinningLanesMode extends RaceBoardModeWithPerRaceCompetitors { defaultSettings.getManeuverTypesToShow(), defaultSettings.isShowDouglasPeuckerPoints(), defaultSettings.isShowEstimatedDuration(), - defaultSettings.getStartCountDownFontSizeScaling()); + defaultSettings.getStartCountDownFontSizeScaling(), + defaultSettings.isShowManeuverLossVisualization()); ((RaceBoardComponentContext) raceMap.getComponentContext()).addModesPatching(raceMap, additiveSettings, new OnSettingsReloadedCallback() { From 028cb05a5c69b4b4bc8743a50ffe7c0918e26f02 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 10 Jul 2018 17:39:01 +0200 Subject: [PATCH 34/53] Revert "bug4572: added null-safety in SailingServiceImpl speedWithBearingBeforeManeuverLoss" This reverts commit ecac04d30c3818f2ef2522ace312f5703c331384. --- .../com/sap/sailing/gwt/ui/server/SailingServiceImpl.java | 5 ++--- 1 file changed, 2 insertions(+), 3 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index 56ec4202349..7c2d018b39c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -3632,9 +3632,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S final Date markPassingTimePoint = maneuver.isMarkPassing() ? maneuver.getMarkPassing().getTimePoint().asDate() : null; final NauticalSide markPassingSide = maneuver.isMarkPassing() ? maneuver.getToSide() : null; - final SpeedWithBearing speedWithBearingBeforeManeuverLoss = (maneuver.getManeuverLoss() == null - || maneuver.getManeuverLoss().getSpeedWithBearingBefore() == null) ? null - : maneuver.getManeuverLoss().getSpeedWithBearingBefore(); + final SpeedWithBearing speedWithBearingBeforeManeuverLoss = maneuver.getManeuverLoss() == null ? null + : maneuver.getManeuverLoss().getSpeedWithBearingBefore(); final Double middleManeuverAngle = maneuver.getManeuverLoss() == null ? null : maneuver.getManeuverLoss().getMiddleManeuverAngle().getDegrees(); final ManeuverLossDTO maneuverLoss = maneuver.getManeuverLoss() == null ? null : new ManeuverLossDTO(maneuver.getManeuverLoss().getManeuverStartPosition(), From 30657a19b62da41b5a0555c01f22f64283619ca9 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 09:38:01 +0200 Subject: [PATCH 35/53] Revert "bug4572: Added a showManeuverLossLinesVisualization in RaceMapSettings. Made it default true." This reverts commit ac0fc23ea834311d4250fa12e7a26f51ca179275. Reverting because of unintended formatting changes. --- .../startanalysis/card/StartAnalysisCard.java | 2 +- .../autoplay/client/utils/AutoplayHelper.java | 2 +- .../shared/racemap/CombinedWindPanel.java | 2 +- .../gwt/ui/client/shared/racemap/RaceMap.java | 1462 ++++++++--------- .../shared/racemap/RaceMapLifecycle.java | 4 +- .../shared/racemap/RaceMapSettings.java | 24 +- .../RaceMapSettingsDialogComponent.java | 11 +- .../racemap/TrueNorthIndicatorPanel.java | 2 +- .../EmbeddedMapAndWindChartEntryPoint.java | 2 +- .../gwt/ui/raceboard/StartAnalysisMode.java | 3 +- .../gwt/ui/raceboard/WinningLanesMode.java | 3 +- 11 files changed, 663 insertions(+), 854 deletions(-) diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java index 497dc402680..66858859563 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java @@ -163,7 +163,7 @@ public class StartAnalysisCard extends Composite implements HasWidgets, StartAna defaultRaceMapSettings.isShowWindStreamletOverlay(), defaultRaceMapSettings.isShowSimulationOverlay(), defaultRaceMapSettings.isShowMapControls(), defaultRaceMapSettings.getManeuverTypesToShow(), defaultRaceMapSettings.isShowDouglasPeuckerPoints(), defaultRaceMapSettings.isShowEstimatedDuration(), - defaultRaceMapSettings.getStartCountDownFontSizeScaling(), defaultRaceMapSettings.isShowManeuverLossVisualization()); + defaultRaceMapSettings.getStartCountDownFontSizeScaling()); RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingServiceAsync, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java index 2525da31353..bd37fc96661 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java @@ -284,7 +284,7 @@ public class AutoplayHelper { RaceMapZoomSettings autoFollowRace = new RaceMapZoomSettings(typesToConsiderOnZoom, true); RaceMapSettings settings = new RaceMapSettings(autoFollowRace, new RaceMapHelpLinesSettings(), false, 15, 100000l, false, RaceMapSettings.DEFAULT_BUOY_ZONE_RADIUS, false, true, false, false, false, false, - RaceMapSettings.getDefaultManeuvers(), false, false, /* startCountDownFontSizeScaling */ 1.5, false); + RaceMapSettings.getDefaultManeuvers(), false, false, /* startCountDownFontSizeScaling */ 1.5); RaceMapLifecycle raceMapLifecycle = new RaceMapLifecycle(StringMessages.INSTANCE); final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(currentLiveRace, competitorsAndTheirBoats); RaceCompetitorSelectionModel competitorSelectionProvider = new RaceCompetitorSelectionModel( diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java index 50cbefb7269..9d91e40a447 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java @@ -54,7 +54,7 @@ public class CombinedWindPanel extends FlowPanel { newShowStreamletsOverlaySetting, oldRaceMapSettings.isShowSimulationOverlay(), oldRaceMapSettings.isShowMapControls(), oldRaceMapSettings.getManeuverTypesToShow(), oldRaceMapSettings.isShowDouglasPeuckerPoints(), oldRaceMapSettings.isShowEstimatedDuration(), - oldRaceMapSettings.getStartCountDownFontSizeScaling(), oldRaceMapSettings.isShowManeuverLossVisualization()); + oldRaceMapSettings.getStartCountDownFontSizeScaling()); map.updateSettings(newRaceMapSettings); } }); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 7b46093cdb9..2b3617a5968 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -164,9 +164,8 @@ import com.sap.sse.gwt.client.shared.components.SettingsDialog; import com.sap.sse.gwt.client.shared.components.SettingsDialogComponent; import com.sap.sse.gwt.client.shared.settings.ComponentContext; -public class RaceMap extends AbstractCompositeComponent - implements TimeListener, CompetitorSelectionChangeListener, RaceTimesInfoProviderListener, TailFactory, - RequiresDataInitialization, RequiresResize, QuickRankProvider { +public class RaceMap extends AbstractCompositeComponent implements TimeListener, CompetitorSelectionChangeListener, + RaceTimesInfoProviderListener, TailFactory, RequiresDataInitialization, RequiresResize, QuickRankProvider { private static final String ADVANTAGE_LINE_COLOR = "#ff9900"; // orange private static final String FINISH_LINE_COLOR = "#000000"; private static final Color LOWLIGHTED_TAIL_COLOR = new RGBColor(200, 200, 200); @@ -177,22 +176,22 @@ public class RaceMap extends AbstractCompositeComponent public static final String GET_RACE_MAP_DATA_CATEGORY = "getRaceMapData"; public static final String GET_WIND_DATA_CATEGORY = "getWindData"; private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; - + private static final String COMPACT_HEADER_STYLE = "compactHeader"; public static final Color WATER_COLOR = new RGBColor(0, 67, 125); - + private AbsolutePanel rootPanel = new AbsolutePanel(); - + private MapWidget map; - + /** * Always valid, non-null. Must be used to map all coordinates, headings, bearings, and directions * displayed on the map, including the orientations of any canvases such as boat icons, wind displays etc. that are - * embedded in the map. The coordinate systems facilitates the possibility of transformed displays such as rotated - * and translated versions of the map, implementing the "wind-up" view. + * embedded in the map. The coordinate systems facilitates the possibility of transformed displays such as + * rotated and translated versions of the map, implementing the "wind-up" view. */ private DelegateCoordinateSystem coordinateSystem; - + private FlowPanel headerPanel; private AbsolutePanel panelForLeftHeaderLabels; private AbsolutePanel panelForRightHeaderLabels; @@ -213,18 +212,17 @@ public class RaceMap extends AbstractCompositeComponent private Polyline finishLine; /** - * Polyline for the advantage line (the leading line for the boats, orthogonal to the wind direction; touching the - * leading boat). + * Polyline for the advantage line (the leading line for the boats, orthogonal to the wind direction; touching the leading boat). */ private Polyline advantageLine; - + private AdvantageLineAnimator advantageTimer; - + /** * The windward of two Polylines representing a triangle between startline and first mark. */ private Polyline windwardStartLineMarkToFirstMarkLine; - + /** * The leeward of two Polylines representing a triangle between startline and first mark. */ @@ -233,12 +231,12 @@ public class RaceMap extends AbstractCompositeComponent private class AdvantageLineMouseOverMapHandler implements MouseOverMapHandler { private double trueWindAngle; private Date date; - + public AdvantageLineMouseOverMapHandler(double trueWindAngle, Date date) { this.trueWindAngle = trueWindAngle; this.date = date; } - + public void setTrueWindBearing(double trueWindAngle) { this.trueWindAngle = trueWindAngle; } @@ -249,43 +247,42 @@ public class RaceMap extends AbstractCompositeComponent @Override public void onEvent(MouseOverMapEvent event) { - map.setTitle(stringMessages.advantageLine() + " (from " - + new DegreeBearingImpl(Math.round(trueWindAngle)).reverse().getDegrees() + "deg" - + (date == null ? ")" : ", " + date) + ")"); + map.setTitle(stringMessages.advantageLine()+" (from "+new DegreeBearingImpl(Math.round(trueWindAngle)).reverse().getDegrees()+"deg"+ + (date == null ? ")" : ", "+ date) + ")"); } }; - + private AdvantageLineMouseOverMapHandler advantageLineMouseOverHandler; - + /** * Polylines for the course middle lines; keys are the two control points delimiting the leg for which the - * {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there are - * multiple legs using the same control points in different directions, using a {@link Set} makes this independent - * of the order of the two control points. If no course middle line is currently being shown for a pair of control - * points, the map will not contain a value for this pair. + * {@link Polyline} value shows the course middle line. As only one course middle line is shown even if there + * are multiple legs using the same control points in different directions, using a {@link Set} makes this + * independent of the order of the two control points. If no course middle line is currently being shown for + * a pair of control points, the map will not contain a value for this pair. */ private final Map, Polyline> courseMiddleLines; private final Map courseSidelines; - + /** - * When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the lines - * that show the tooltip text in a little info box linked to the line. When the line is removed by + * When the {@link HelpLineTypes#COURSEGEOMETRY} option is selected, little markers will be displayed on the + * lines that show the tooltip text in a little info box linked to the line. When the line is removed by * {@link #showOrRemoveOrUpdateLine(Polyline, boolean, Position, Position, LineInfoProvider, String)}, these - * overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is deactivated, - * all these overlays need to go. + * overlays need to be removed as well. Also, when the {@link HelpLineTypes#COURSEGEOMETRY} setting is + * deactivated, all these overlays need to go. */ private final Map infoOverlaysForLinesForCourseGeometry; - + /** * Wind data used to display the advantage line. Retrieved by a {@link GetWindInfoAction} execution and used in * {@link #showAdvantageLine(Iterable, Date)}. */ private WindInfoForRaceDTO lastCombinedWindTrackInfoDTO; - + /** - * Manages the cached set of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the boat positions as well as their - * graphical counterpart in the form of {@link Polyline}s. + * Manages the cached set of {@link GPSFixDTOWithSpeedWindTackAndLegType}s for the boat positions as well as their graphical counterpart in the + * form of {@link Polyline}s. */ private final FixesAndTails fixesAndTails; @@ -298,7 +295,7 @@ public class RaceMap extends AbstractCompositeComponent * html5 canvases used for competitor info display on the map */ private final CompetitorInfoOverlays competitorInfoOverlays; - + private SmallTransparentInfoOverlay countDownOverlay; /** @@ -311,12 +308,11 @@ public class RaceMap extends AbstractCompositeComponent * html5 canvas used to display course marks including buoy zones */ private final Map courseMarkOverlays; - + private final Map courseMarkClickHandlers; /** - * Maps from the {@link MarkDTO#getIdAsString() mark's ID converted to a string} to the corresponding - * {@link MarkDTO} + * Maps from the {@link MarkDTO#getIdAsString() mark's ID converted to a string} to the corresponding {@link MarkDTO} */ private final Map markDTOs; @@ -335,9 +331,9 @@ public class RaceMap extends AbstractCompositeComponent private Map> lastManeuverResult; private Map> lastDouglasPeuckerResult; - + private final RaceCompetitorSelectionProvider competitorSelection; - + private final RaceCompetitorSet raceCompetitorSet; /** @@ -348,7 +344,7 @@ public class RaceMap extends AbstractCompositeComponent private final Timer timer; private RaceTimesInfoDTO lastRaceTimesInfo; - + /** * 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, @@ -365,83 +361,81 @@ public class RaceMap extends AbstractCompositeComponent */ private int startedProcessingRequestID; - private RaceMapImageManager raceMapImageManager; + private RaceMapImageManager raceMapImageManager; private RaceMapSettings settings; private final RaceMapLifecycle raceMapLifecycle; - + private final StringMessages stringMessages; - + private boolean isMapInitialized; private Date lastTimeChangeBeforeInitialization; - + private int lastLegNumber; /** * The strategy for maintaining and delivering the "quick ranks" information. The provider will be informed about - * quick ranks received from a {@link RaceMapDataDTO#quickRanks} field but may choose to ignore this information, - * e.g., if it can assume that more current information about ranks and leg numbers is available from a - * {@link LeaderboardDTO}. + * quick ranks received from a {@link RaceMapDataDTO#quickRanks} field but may choose to ignore this information, e.g., + * if it can assume that more current information about ranks and leg numbers is available from a {@link LeaderboardDTO}. */ private QuickRanksDTOProvider quickRanksDTOProvider; - + private final CombinedWindPanel combinedWindPanel; private final TrueNorthIndicatorPanel trueNorthIndicatorPanel; private final FlowPanel topLeftControlsWrapperPanel; - + private final AsyncActionsExecutor asyncActionsExecutor; /** - * The map bounds as last received by map callbacks; used to determine whether to suppress the boat animation during - * zoom/pan + * The map bounds as last received by map callbacks; used to determine whether to suppress the boat animation during zoom/pan */ private LatLngBounds currentMapBounds; // bounds to which bounds-changed-handler compares - private int currentZoomLevel; // zoom-level to which bounds-changed-handler compares - - private boolean autoZoomIn = false; // flags auto-zoom-in in progress + private int currentZoomLevel; // zoom-level to which bounds-changed-handler compares + + private boolean autoZoomIn = false; // flags auto-zoom-in in progress private boolean autoZoomOut = false; // flags auto-zoom-out in progress - private int autoZoomLevel; // zoom-level to which auto-zoom-in/-out is zooming - LatLngBounds autoZoomLatLngBounds; // bounds to which auto-zoom-in/-out is panning&zooming - + private int autoZoomLevel; // zoom-level to which auto-zoom-in/-out is zooming + LatLngBounds autoZoomLatLngBounds; // bounds to which auto-zoom-in/-out is panning&zooming + private RaceSimulationOverlay simulationOverlay; private WindStreamletsRaceboardOverlay streamletOverlay; - + private final boolean isSimulationEnabled; - + private static final String GET_POLAR_CATEGORY = "getPolar"; - + /** * Tells about the availability of polar / VPP data for this race. If available, the simulation feature can be * offered to the user. */ private boolean hasPolar; - + private final RegattaAndRaceIdentifier raceIdentifier; - + /** - * When the user requests wind-up display this may happen at a point where no mark positions are known or when no - * wind direction is known yet. In this case, this flag will be set, and when wind information or course mark + * When the user requests wind-up display this may happen at a point where no mark positions are known or when + * no wind direction is known yet. In this case, this flag will be set, and when wind information or course mark * positions are received later, this flag is checked, and if set, a {@link #updateCoordinateSystemFromSettings()} * call is issued to make sure that the user's request for a new coordinate system is honored. */ private boolean requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown; - + /** - * Tells whether currently an auto-zoom is in progress; this is used particularly to keep the smooth CSS boat - * transitions active while auto-zooming whereas stopping them seems the better option for manual zooms. + * Tells whether currently an auto-zoom is in progress; this is used particularly to keep the smooth CSS boat transitions + * active while auto-zooming whereas stopping them seems the better option for manual zooms. */ private boolean autoZoomInProgress; - + /** - * Tells whether currently an orientation change is in progress; this is required handle map events during the - * configuration of the map during an orientation change. + * Tells whether currently an orientation change is in progress; this is required handle map events during the configuration of the map + * during an orientation change. */ private boolean orientationChangeInProgress; - + private final NumberFormat numberFormatOneDecimal = NumberFormat.getFormat("0.0"); private final NumberFormat numberFormatNoDecimal = NumberFormat.getFormat("0"); - + /** * The competitor for which the advantage line is currently showing. Should this competitor's quick rank change, or * should ranks be received while this field is {@code null}, the advantage line @@ -451,53 +445,48 @@ public class RaceMap extends AbstractCompositeComponent protected Label estimatedDurationOverlay; private RaceMapStyle raceMapStyle; private final boolean showHeaderPanel; - + /** Callback to set the visibility of the wind chart. */ private final Consumer showWindChartForProvider; private ManagedInfoWindow managedInfoWindow; - - /** The Map where for each competitor and each maneuver is stored if it is visualized or not. */ + + /** The Map where for each competitor and each maneuver is stored if it is visualized or not.*/ private Set> maneuverLossCheckBoxValueStore = new HashSet<>(); - - /** The Map where the polylines for the specific maneuver are stored. */ + + /** The Map where the polylines for the specific maneuver are stored.*/ private Map, Set> maneuverLossLinesMap = new HashMap<>(); - - /** The Map where the info overlays for a maneuver loss visualization are stored. */ + + /** The Map where the info overlays for a maneuver loss visualization are stored.*/ private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); private class AdvantageLineUpdater implements QuickRanksListener { @Override public void rankChanged(String competitorIdAsString, QuickRankDTO oldQuickRank, QuickRankDTO quickRank) { - if (advantageLineCompetitor == null - || (oldQuickRank != null - && advantageLineCompetitor.getIdAsString().equals(oldQuickRank.competitor.getIdAsString())) - || (quickRank != null - && advantageLineCompetitor.getIdAsString().equals(quickRank.competitor.getIdAsString()))) { - showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), - /* timeForPositionTransitionMillis */ -1 /* (no transition) */); + if (advantageLineCompetitor == null || + (oldQuickRank != null && advantageLineCompetitor.getIdAsString().equals(oldQuickRank.competitor.getIdAsString())) || + (quickRank != null && advantageLineCompetitor.getIdAsString().equals(quickRank.competitor.getIdAsString()))) { + showAdvantageLine(getCompetitorsToShow(), getTimer().getTime(), /* timeForPositionTransitionMillis */ -1 /* (no transition) */); } } } - + public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, - RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, - AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer timer, - RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, - StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, - boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider) { + RaceMapSettings raceMapSettings, + SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, + ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, + RaceCompetitorSet raceCompetitorSet, StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, + RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider) { this(parent, context, raceMapLifecycle, raceMapSettings, sailingService, asyncActionsExecutor, errorReporter, timer, competitorSelection, raceCompetitorSet, stringMessages, raceIdentifier, raceMapResources, - showHeaderPanel, quickRanksDTOProvider, visible -> { - }); + showHeaderPanel, quickRanksDTOProvider, visible -> {}); } - + public RaceMap(Component parent, ComponentContext context, RaceMapLifecycle raceMapLifecycle, - RaceMapSettings raceMapSettings, SailingServiceAsync sailingService, - AsyncActionsExecutor asyncActionsExecutor, ErrorReporter errorReporter, Timer timer, - RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, - StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, - boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, - Consumer showWindChartForProvider) { + RaceMapSettings raceMapSettings, + SailingServiceAsync sailingService, AsyncActionsExecutor asyncActionsExecutor, + ErrorReporter errorReporter, Timer timer, RaceCompetitorSelectionProvider competitorSelection, RaceCompetitorSet raceCompetitorSet, + StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, + RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, Consumer showWindChartForProvider) { super(parent, context); this.showHeaderPanel = showHeaderPanel; this.quickRanksDTOProvider = quickRanksDTOProvider; @@ -540,11 +529,9 @@ public class RaceMap extends AbstractCompositeComponent initializeData(settings.isShowMapControls(), showHeaderPanel); raceMapStyle = raceMapResources.raceMapStyle(); raceMapStyle.ensureInjected(); - combinedWindPanel = new CombinedWindPanel(this, raceMapImageManager, raceMapStyle, stringMessages, - coordinateSystem); + combinedWindPanel = new CombinedWindPanel(this, raceMapImageManager, raceMapStyle, stringMessages, coordinateSystem); combinedWindPanel.setVisible(false); - trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, raceMapStyle, stringMessages, - coordinateSystem); + trueNorthIndicatorPanel = new TrueNorthIndicatorPanel(this, raceMapImageManager, raceMapStyle, stringMessages, coordinateSystem); trueNorthIndicatorPanel.setVisible(true); topLeftControlsWrapperPanel = new FlowPanel(); topLeftControlsWrapperPanel.add(combinedWindPanel); @@ -555,22 +542,20 @@ public class RaceMap extends AbstractCompositeComponent initWidget(rootPanel); this.setSize("100%", "100%"); } - + public void setQuickRanksDTOProvider(QuickRanksDTOProvider newQuickRanksDTOProvider) { if (this.quickRanksDTOProvider != null) { this.quickRanksDTOProvider.moveListernersTo(newQuickRanksDTOProvider); } this.quickRanksDTOProvider = newQuickRanksDTOProvider; } - /** * The {@link WindDTO#dampenedTrueWindFromDeg} direction if {@link #lastCombinedWindTrackInfoDTO} has a * {@link WindSourceType#COMBINED} source which has at least one fix recorded; null otherwise. */ private Bearing getLastCombinedTrueWindFromDirection() { if (lastCombinedWindTrackInfoDTO != null) { - for (Entry e : lastCombinedWindTrackInfoDTO.windTrackInfoByWindSource - .entrySet()) { + for (Entry e : lastCombinedWindTrackInfoDTO.windTrackInfoByWindSource.entrySet()) { if (e.getKey().getType() == WindSourceType.COMBINED) { final List windFixes = e.getValue().windFixes; if (!windFixes.isEmpty()) { @@ -581,7 +566,7 @@ public class RaceMap extends AbstractCompositeComponent } return null; } - + /** * @return {@code true} if the map was redrawn by the call to this method */ @@ -594,8 +579,7 @@ public class RaceMap extends AbstractCompositeComponent if (centerOfCourse != null) { final Bearing lastCombinedTrueWindFromDirection = getLastCombinedTrueWindFromDirection(); if (lastCombinedTrueWindFromDirection != null) { - // new equator shall point 90deg right of the "from" wind direction to make wind come from top of - // map + // new equator shall point 90deg right of the "from" wind direction to make wind come from top of map coordinateSystem.setCoordinateSystem(new RotateAndTranslateCoordinateSystem(centerOfCourse, lastCombinedTrueWindFromDirection.add(new DegreeBearingImpl(90)))); if (map != null) { @@ -622,19 +606,17 @@ public class RaceMap extends AbstractCompositeComponent } coordinateSystem.setCoordinateSystem(new IdentityCoordinateSystem()); } - if (mapOptions != null) { // if no coordinate system change happened that affects an existing map, don't redraw + if (mapOptions != null) { // if no coordinate system change happened that affects an existing map, don't redraw fixesAndTails.clearTails(); redraw(); redrawn = true; - // zooming and setting options while the event loop is still working doesn't work reliably; defer until - // event loop returns + // zooming and setting options while the event loop is still working doesn't work reliably; defer until event loop returns Scheduler.get().scheduleDeferred(new ScheduledCommand() { @Override public void execute() { if (map != null) { map.setOptions(mapOptions); - // ensure zooming to what the settings tell, or defaults if what the settings tell isn't - // possible right now + // ensure zooming to what the settings tell, or defaults if what the settings tell isn't possible right now mapFirstZoomDone = false; trueNorthIndicatorPanel.redraw(); orientationChangeInProgress = false; @@ -654,122 +636,116 @@ public class RaceMap extends AbstractCompositeComponent loadLibraries.add(LoadLibrary.GEOMETRY); Runnable onLoad = new Runnable() { - @Override - public void run() { - MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false); - map = new MapWidget(mapOptions); - rootPanel.add(map, 0, 0); - if (showHeaderPanel) { - Image sapLogo = createSAPLogo(); - rootPanel.add(sapLogo); - } + @Override + public void run() { + MapOptions mapOptions = getMapOptions(showMapControls, /* wind up */ false); + map = new MapWidget(mapOptions); + rootPanel.add(map, 0, 0); + if (showHeaderPanel) { + Image sapLogo = createSAPLogo(); + rootPanel.add(sapLogo); + } + + map.setControls(ControlPosition.LEFT_TOP, topLeftControlsWrapperPanel); + adjustLeftControlsIndent(); - map.setControls(ControlPosition.LEFT_TOP, topLeftControlsWrapperPanel); - adjustLeftControlsIndent(); + RaceMap.this.raceMapImageManager.loadMapIcons(map); + map.setSize("100%", "100%"); + map.addZoomChangeHandler(new ZoomChangeMapHandler() { + @Override + public void onEvent(ZoomChangeMapEvent event) { + if (!autoZoomIn && !autoZoomOut && !orientationChangeInProgress) { + // stop automatic zoom after a manual zoom event; automatic zoom in zoomMapToNewBounds will restore old settings + final List emptyList = Collections.emptyList(); + RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, settings.getZoomSettings().isZoomToSelectedCompetitors()); + settings = new RaceMapSettings(settings, clearedZoomSettings); + } + // TODO bug489 when in wind-up mode, avoid zooming out too far; perhaps zoom back in if zoomed out too far + } + }); + map.addDragEndHandler(new DragEndMapHandler() { + @Override + public void onEvent(DragEndMapEvent event) { + // stop automatic zoom after a manual drag event + autoZoomIn = false; + autoZoomOut = false; + final List emptyList = Collections.emptyList(); + RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, settings.getZoomSettings().isZoomToSelectedCompetitors()); + settings = new RaceMapSettings(settings, clearedZoomSettings); + } + }); + map.addIdleHandler(new IdleMapHandler() { + @Override + public void onEvent(IdleMapEvent event) { + // the "idle"-event is raised at the end of map-animations + if (autoZoomIn) { + // finalize zoom-in that was started with panTo() in zoomMapToNewBounds() + map.setZoom(autoZoomLevel); + autoZoomIn = false; + } + if (autoZoomOut) { + // finalize zoom-out that was started with setZoom() in zoomMapToNewBounds() + map.panTo(autoZoomLatLngBounds.getCenter()); + autoZoomOut = false; + } + } + }); + map.addBoundsChangeHandler(new BoundsChangeMapHandler() { + @Override + public void onEvent(BoundsChangeMapEvent event) { + int newZoomLevel = map.getZoom(); + if (!isAutoZoomInProgress() && (newZoomLevel != currentZoomLevel)) { + removeTransitions(); + } + if ((streamletOverlay != null) && !map.getBounds().equals(currentMapBounds)) { + streamletOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); + } + if ((simulationOverlay != null) && !map.getBounds().equals(currentMapBounds)) { + simulationOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); + } + currentMapBounds = map.getBounds(); + currentZoomLevel = newZoomLevel; + headerPanel.getElement().getStyle().setWidth(map.getOffsetWidth(), Unit.PX); + } + }); + + // If there was a time change before the API was loaded, reset the time + if (lastTimeChangeBeforeInitialization != null) { + timeChanged(lastTimeChangeBeforeInitialization, null); + lastTimeChangeBeforeInitialization = null; + } + // Initialize streamlet canvas for wind visualization; it shouldn't be doing anything unless it's visible + streamletOverlay = new WindStreamletsRaceboardOverlay(getMap(), /* zIndex */ 0, + timer, raceIdentifier, sailingService, asyncActionsExecutor, stringMessages, coordinateSystem); + streamletOverlay.addToMap(); + if (settings.isShowWindStreamletOverlay()) { + streamletOverlay.setColors(settings.isShowWindStreamletColors()); + streamletOverlay.setVisible(true); + } - RaceMap.this.raceMapImageManager.loadMapIcons(map); - map.setSize("100%", "100%"); - map.addZoomChangeHandler(new ZoomChangeMapHandler() { - @Override - public void onEvent(ZoomChangeMapEvent event) { - if (!autoZoomIn && !autoZoomOut && !orientationChangeInProgress) { - // stop automatic zoom after a manual zoom event; automatic zoom in zoomMapToNewBounds will - // restore old settings - final List emptyList = Collections.emptyList(); - RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, - settings.getZoomSettings().isZoomToSelectedCompetitors()); - settings = new RaceMapSettings(settings, clearedZoomSettings); - } - // TODO bug489 when in wind-up mode, avoid zooming out too far; perhaps zoom back in if zoomed - // out too far - } - }); - map.addDragEndHandler(new DragEndMapHandler() { - @Override - public void onEvent(DragEndMapEvent event) { - // stop automatic zoom after a manual drag event - autoZoomIn = false; - autoZoomOut = false; - final List emptyList = Collections.emptyList(); - RaceMapZoomSettings clearedZoomSettings = new RaceMapZoomSettings(emptyList, - settings.getZoomSettings().isZoomToSelectedCompetitors()); - settings = new RaceMapSettings(settings, clearedZoomSettings); - } - }); - map.addIdleHandler(new IdleMapHandler() { - @Override - public void onEvent(IdleMapEvent event) { - // the "idle"-event is raised at the end of map-animations - if (autoZoomIn) { - // finalize zoom-in that was started with panTo() in zoomMapToNewBounds() - map.setZoom(autoZoomLevel); - autoZoomIn = false; - } - if (autoZoomOut) { - // finalize zoom-out that was started with setZoom() in zoomMapToNewBounds() - map.panTo(autoZoomLatLngBounds.getCenter()); - autoZoomOut = false; - } - } - }); - map.addBoundsChangeHandler(new BoundsChangeMapHandler() { - @Override - public void onEvent(BoundsChangeMapEvent event) { - int newZoomLevel = map.getZoom(); - if (!isAutoZoomInProgress() && (newZoomLevel != currentZoomLevel)) { - removeTransitions(); - } - if ((streamletOverlay != null) && !map.getBounds().equals(currentMapBounds)) { - streamletOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); - } - if ((simulationOverlay != null) && !map.getBounds().equals(currentMapBounds)) { - simulationOverlay.onBoundsChanged(newZoomLevel != currentZoomLevel); - } - currentMapBounds = map.getBounds(); - currentZoomLevel = newZoomLevel; - headerPanel.getElement().getStyle().setWidth(map.getOffsetWidth(), Unit.PX); - } - }); - - // If there was a time change before the API was loaded, reset the time - if (lastTimeChangeBeforeInitialization != null) { - timeChanged(lastTimeChangeBeforeInitialization, null); - lastTimeChangeBeforeInitialization = null; - } - // Initialize streamlet canvas for wind visualization; it shouldn't be doing anything unless it's - // visible - streamletOverlay = new WindStreamletsRaceboardOverlay(getMap(), /* zIndex */ 0, timer, raceIdentifier, - sailingService, asyncActionsExecutor, stringMessages, coordinateSystem); - streamletOverlay.addToMap(); - if (settings.isShowWindStreamletOverlay()) { - streamletOverlay.setColors(settings.isShowWindStreamletColors()); - streamletOverlay.setVisible(true); - } - - if (isSimulationEnabled) { - // determine availability of polar diagram - setHasPolar(); - // initialize simulation canvas - simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, - sailingService, stringMessages, asyncActionsExecutor, coordinateSystem); - simulationOverlay.addToMap(); - showSimulationOverlay(settings.isShowSimulationOverlay()); - } - if (showHeaderPanel) { - createHeaderPanel(map); - } - if (showMapControls) { - createSettingsButton(map); - } - // Data has been initialized - RaceMap.this.isMapInitialized = true; - RaceMap.this.redraw(); - trueNorthIndicatorPanel.redraw(); - showAdditionalControls(map); - RaceMap.this.managedInfoWindow = new ManagedInfoWindow(map); - } + if (isSimulationEnabled) { + // determine availability of polar diagram + setHasPolar(); + // initialize simulation canvas + simulationOverlay = new RaceSimulationOverlay(getMap(), /* zIndex */ 0, raceIdentifier, sailingService, stringMessages, asyncActionsExecutor, coordinateSystem); + simulationOverlay.addToMap(); + showSimulationOverlay(settings.isShowSimulationOverlay()); + } + if (showHeaderPanel) { + createHeaderPanel(map); + } + if (showMapControls) { + createSettingsButton(map); + } + // Data has been initialized + RaceMap.this.isMapInitialized = true; + RaceMap.this.redraw(); + trueNorthIndicatorPanel.redraw(); + showAdditionalControls(map); + RaceMap.this.managedInfoWindow = new ManagedInfoWindow(map); + } }; - LoadApi.go(onLoad, loadLibraries, sensor, GoogleMapAPIKey.V3_PARAMS); + LoadApi.go(onLoad, loadLibraries, sensor, GoogleMapAPIKey.V3_PARAMS); } /** @@ -798,13 +774,14 @@ public class RaceMap extends AbstractCompositeComponent } /** - * Creates a header panel where additional information can be displayed by using {@link #getLeftHeaderPanel()} or - * {@link #getRightHeaderPanel()}. + * Creates a header panel where additional information can be displayed by using + * {@link #getLeftHeaderPanel()} or {@link #getRightHeaderPanel()}. * - * This panel is transparent and configured in such a way that it moves other controls down by its height. To - * achieve the goal of not having added widgets transparent this widget consists of two parts: First one is the - * transparent panel and the second one is the panel for the controls. The controls then need to moved onto the - * panel by using CSS. + * This panel is transparent and configured in such a way that it moves other controls + * down by its height. To achieve the goal of not having added widgets transparent + * this widget consists of two parts: First one is the transparent panel and the + * second one is the panel for the controls. The controls then need to moved onto + * the panel by using CSS. */ private void createHeaderPanel(MapWidget map) { // we need a panel that does not have any transparency to have the @@ -824,7 +801,7 @@ public class RaceMap extends AbstractCompositeComponent // controls at RIGHT would not get the correct top setting map.setControls(ControlPosition.TOP_RIGHT, headerPanel); } - + private void createSettingsButton(MapWidget map) { final Component component = this; Button settingsButton = new Button(); @@ -861,11 +838,11 @@ public class RaceMap extends AbstractCompositeComponent public void redraw() { timeChanged(timer.getTime(), null); } - + Map getBoatOverlays() { return Collections.unmodifiableMap(boatOverlays); } - + protected RaceCompetitorSelectionProvider getCompetitorSelection() { return competitorSelection; } @@ -881,36 +858,33 @@ public class RaceMap extends AbstractCompositeComponent public MapWidget getMap() { return map; } - + public RaceSimulationOverlay getSimulationOverlay() { return simulationOverlay; } - + /** * @return the Panel where labels or other controls for the header can be positioned */ public AbsolutePanel getLeftHeaderPanel() { return panelForLeftHeaderLabels; } - + public AbsolutePanel getRightHeaderPanel() { return panelForRightHeaderLabels; } - + @Override - public void raceTimesInfosReceived(Map raceTimesInfos, - long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { - timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, - clientTimeWhenResponseWasReceived); + public void raceTimesInfosReceived(Map raceTimesInfos, long clientTimeWhenRequestWasSent, Date serverTimeDuringRequest, long clientTimeWhenResponseWasReceived) { + timer.adjustClientServerOffset(clientTimeWhenRequestWasSent, serverTimeDuringRequest, clientTimeWhenResponseWasReceived); this.lastRaceTimesInfo = raceTimesInfos.get(raceIdentifier); } /** - * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard - * contents}, null is used as time point. The condition for this is encapsulated in this method so - * others can find out. For example, when a time change is signaled due to local offset / delay adjustments, no - * additional call to {@link #loadCompleteLeaderboard(Date)} would be required as null will be passed - * in any case, not being affected by local time offsets. + * In {@link PlayModes#Live live mode}, when {@link #loadCompleteLeaderboard(Date) loading the leaderboard contents}, null + * is used as time point. The condition for this is encapsulated in this method so others can find out. For example, when a time change + * is signaled due to local offset / delay adjustments, no additional call to {@link #loadCompleteLeaderboard(Date)} would be required + * as null will be passed in any case, not being affected by local time offsets. */ private boolean useNullAsTimePoint() { return timer.getPlayMode() == PlayModes.Live; @@ -924,66 +898,57 @@ public class RaceMap extends AbstractCompositeComponent final Iterable competitorsToShow = getCompetitorsToShow(); if (race != null) { final long transitionTimeInMillis = calculateTimeForPositionTransitionInMillis(newTime, oldTime); - final com.sap.sse.common.Util.Triple, Map, Map> fromAndToAndOverlap = fixesAndTails - .computeFromAndTo(newTime, competitorsToShow, - settings.getEffectiveTailLengthInMilliseconds()); + final com.sap.sse.common.Util.Triple, Map, Map> fromAndToAndOverlap = + fixesAndTails.computeFromAndTo(newTime, competitorsToShow, settings.getEffectiveTailLengthInMilliseconds()); // Request map data update, possibly in two calls; see method details - callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers( - fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow); + callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers(fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow); // draw the wind into the map, get the combined wind List windSourceTypeNames = new ArrayList(); windSourceTypeNames.add(WindSourceType.EXPEDITION.name()); windSourceTypeNames.add(WindSourceType.WINDFINDER.name()); windSourceTypeNames.add(WindSourceType.COMBINED.name()); - GetWindInfoAction getWindInfoAction = new GetWindInfoAction(sailingService, race, newTime, 1000L, 1, - windSourceTypeNames, + GetWindInfoAction getWindInfoAction = new GetWindInfoAction(sailingService, race, newTime, 1000L, 1, windSourceTypeNames, /* onlyUpToNewestEvent==false means get us any data we can get by a best effort */ false); - asyncActionsExecutor.execute(getWindInfoAction, GET_WIND_DATA_CATEGORY, - new AsyncCallback() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError( - "Error obtaining wind information: " + caught.getMessage(), - true /* silentMode */); - } + asyncActionsExecutor.execute(getWindInfoAction, GET_WIND_DATA_CATEGORY, new AsyncCallback() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error obtaining wind information: " + caught.getMessage(), true /*silentMode */); + } - @Override - public void onSuccess(WindInfoForRaceDTO windInfo) { - List> windSourcesToShow = new ArrayList>(); - if (windInfo != null) { - lastCombinedWindTrackInfoDTO = windInfo; - showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); - for (WindSource windSource : windInfo.windTrackInfoByWindSource.keySet()) { - WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource - .get(windSource); - switch (windSource.getType()) { - case EXPEDITION: - case WINDFINDER: - // we filter out measured wind sources with very low confidence - if (windTrackInfoDTO.minWindConfidence > 0.0001) { - windSourcesToShow.add( - new com.sap.sse.common.Util.Pair( - windSource, windTrackInfoDTO)); - } - break; - case COMBINED: - showCombinedWindOnMap(windSource, windTrackInfoDTO); - if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { - updateCoordinateSystemFromSettings(); - } - break; - default: - // Which wind sources are requested is defined in a list above this - // action. So we throw here an exception to notice a missing source. - throw new UnsupportedOperationException( - "There is currently no support for the enum value '" - + windSource.getType() + "' in this method."); + @Override + public void onSuccess(WindInfoForRaceDTO windInfo) { + List> windSourcesToShow = new ArrayList>(); + if (windInfo != null) { + lastCombinedWindTrackInfoDTO = windInfo; + showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); + for (WindSource windSource : windInfo.windTrackInfoByWindSource.keySet()) { + WindTrackInfoDTO windTrackInfoDTO = windInfo.windTrackInfoByWindSource.get(windSource); + switch (windSource.getType()) { + case EXPEDITION: + case WINDFINDER: + // we filter out measured wind sources with very low confidence + if (windTrackInfoDTO.minWindConfidence > 0.0001) { + windSourcesToShow.add(new com.sap.sse.common.Util.Pair(windSource, windTrackInfoDTO)); } - } + break; + case COMBINED: + showCombinedWindOnMap(windSource, windTrackInfoDTO); + if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { + updateCoordinateSystemFromSettings(); + } + break; + default: + // Which wind sources are requested is defined in a list above this + // action. So we throw here an exception to notice a missing source. + throw new UnsupportedOperationException( + "There is currently no support for the enum value '" + + windSource.getType() + "' in this method."); } - showWindSensorsOnMap(windSourcesToShow); } - }); + } + showWindSensorsOnMap(windSourcesToShow); + } + }); } } } @@ -992,7 +957,6 @@ public class RaceMap extends AbstractCompositeComponent public WindInfoForRaceDTO getLastCombinedWindTrackInfoDTO() { return lastCombinedWindTrackInfoDTO; } - /** * Requests updates for map data and, when received, updates the map structures accordingly. *

@@ -1013,14 +977,12 @@ public class RaceMap extends AbstractCompositeComponent * current tail length setting the new tail segment does not overlap with the old one, requiring a full load of the * entire tail data for that competitor. For these non-overlapping requests, this method creates a * {@link GetBoatPositionsAction} request loading the entire tail required, but not quick ranks, sidelines and mark - * positions. Updating the results of this call is done in - * {@link #updateBoatPositions(Date, long, Map, Iterable, Map, boolean)}. + * positions. Updating the results of this call is done in {@link #updateBoatPositions(Date, long, Map, Iterable, Map, boolean)}. *

*/ private void callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers( final Triple, Map, Map> fromAndToAndOverlap, - RegattaAndRaceIdentifier race, final Date newTime, final long transitionTimeInMillis, - final Iterable competitorsToShow) { + RegattaAndRaceIdentifier race, final Date newTime, final long transitionTimeInMillis, final Iterable competitorsToShow) { final Map fromTimesForQuickCall = new HashMap<>(); final Map toTimesForQuickCall = new HashMap<>(); final Map fromTimesForNonOverlappingTailsCall = new HashMap<>(); @@ -1031,8 +993,7 @@ public class RaceMap extends AbstractCompositeComponent fromTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); toTimesForQuickCall.put(e.getKey(), fromAndToAndOverlap.getB().get(e.getKey())); } else { - // no overlap; add competitor to request with a zero-length interval asking only position at newTime, - // not the entire tail + // no overlap; add competitor to request with a zero-length interval asking only position at newTime, not the entire tail fromTimesForQuickCall.put(e.getKey(), newTime); toTimesForQuickCall.put(e.getKey(), newTime); fromTimesForNonOverlappingTailsCall.put(e.getKey(), fromAndToAndOverlap.getA().get(e.getKey())); @@ -1050,56 +1011,46 @@ public class RaceMap extends AbstractCompositeComponent // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for // all its entries so that the other response received for GetBoatPositionsAction will consider this an // overlap if it happens after this update. - asyncActionsExecutor.execute( - new GetRaceMapDataAction(sailingService, competitorsByIdAsString, race, - useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, - /* extrapolate */true, - (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null), - raceCompetitorSet - .getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID(), - newTime, settings.isShowEstimatedDuration()), - GET_RACE_MAP_DATA_CATEGORY, getRaceMapDataCallback(newTime, transitionTimeInMillis, - fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter)); - // next, if necessary, do the full thing; the two calls have different action classes, so throttling should not - // drop one for the other + asyncActionsExecutor.execute(new GetRaceMapDataAction(sailingService, competitorsByIdAsString, + race, useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, /* extrapolate */true, + (settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null), + raceCompetitorSet.getMd5OfIdsAsStringOfCompetitorParticipatingInRaceInAlphanumericOrderOfTheirID(), newTime, settings.isShowEstimatedDuration()), + GET_RACE_MAP_DATA_CATEGORY, + getRaceMapDataCallback(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter)); + // next, if necessary, do the full thing; the two calls have different action classes, so throttling should not drop one for the other if (!fromTimesForNonOverlappingTailsCall.keySet().isEmpty()) { - asyncActionsExecutor.execute( - new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, - toTimesForNonOverlappingTailsCall, /* extrapolate */ true), - GET_RACE_MAP_DATA_CATEGORY, new MarkedAsyncCallback<>(new AsyncCallback() { + asyncActionsExecutor.execute(new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, toTimesForNonOverlappingTailsCall, + /* extrapolate */ true), GET_RACE_MAP_DATA_CATEGORY, + new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable t) { - errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), - true /* silentMode */); + errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), true /*silentMode */); } - + @Override public void onSuccess(CompactBoatPositionsDTO result) { - // Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to - // updateBoatPositions for those - // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will - // contain true for - // all its entries so that the other response received for GetRaceMapDataAction will - // consider this an + // Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to updateBoatPositions for those + // entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for + // all its entries so that the other response received for GetRaceMapDataAction will consider this an // overlap if it happens after this update. - updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), - competitorsToShow, result.getBoatPositionsForCompetitors(competitorsByIdAsString), - /* updateTailsOnly */ true); + updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, + result.getBoatPositionsForCompetitors(competitorsByIdAsString), /* updateTailsOnly */ true); } })); } } - private AsyncCallback getRaceMapDataCallback(final Date newTime, final long transitionTimeInMillis, + private AsyncCallback getRaceMapDataCallback( + final Date newTime, + final long transitionTimeInMillis, final Map hasTailOverlapForCompetitor, final Iterable competitorsToShow, final int requestID) { return new MarkedAsyncCallback<>(new AsyncCallback() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining racemap data: " + caught.getMessage(), - true /* silentMode */); + errorReporter.reportError("Error obtaining racemap data: " + caught.getMessage(), true /*silentMode */); } - + @Override public void onSuccess(RaceMapDataDTO raceMapDataDTO) { if (map != null && raceMapDataDTO != null) { @@ -1108,23 +1059,20 @@ public class RaceMap extends AbstractCompositeComponent startedProcessingRequestID = requestID; if (raceMapDataDTO.raceCompetitorIdsAsStrings != null) { try { - raceCompetitorSet - .setIdsAsStringsOfCompetitorsInRace(raceMapDataDTO.raceCompetitorIdsAsStrings); + raceCompetitorSet.setIdsAsStringsOfCompetitorsInRace(raceMapDataDTO.raceCompetitorIdsAsStrings); } catch (Exception e) { - GWT.log("Error trying to update competitor set for race " + raceIdentifier.getRaceName() - + " in regatta " + raceIdentifier.getRegattaName(), e); + GWT.log("Error trying to update competitor set for race "+raceIdentifier.getRaceName()+ + " in regatta "+raceIdentifier.getRegattaName(), e); } } quickRanksDTOProvider.quickRanksReceivedFromServer(raceMapDataDTO.quickRanks); if (isSimulationEnabled && settings.isShowSimulationOverlay()) { lastLegNumber = raceMapDataDTO.coursePositions.currentLegNumber; - simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, - raceMapDataDTO.simulationResultVersion); + simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion); } // Do boat specific actions Map> boatData = raceMapDataDTO.boatPositions; - updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, - competitorsToShow, boatData, /* updateTailsOnly */ false); + updateBoatPositions(newTime, transitionTimeInMillis, hasTailOverlapForCompetitor, competitorsToShow, boatData, /* updateTailsOnly */ false); if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); } @@ -1134,7 +1082,7 @@ public class RaceMap extends AbstractCompositeComponent if (!maneuverLossLinesMap.isEmpty()) { removeManeuverLossLinesAndInfoOverlays(); } - + if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } @@ -1143,30 +1091,29 @@ public class RaceMap extends AbstractCompositeComponent showCourseSidelinesOnMap(raceMapDataDTO.courseSidelines); showStartAndFinishAndCourseMiddleLines(raceMapDataDTO.coursePositions); showStartLineToFirstMarkTriangle(raceMapDataDTO.coursePositions); - + // Rezoom the map LatLngBounds zoomToBounds = null; - if (!settings.getZoomSettings().containsZoomType(ZoomTypes.NONE)) { // Auto zoom if setting is - // not manual + if (!settings.getZoomSettings().containsZoomType(ZoomTypes.NONE)) { // Auto zoom if setting is not manual zoomToBounds = settings.getZoomSettings().getNewBounds(RaceMap.this); if (zoomToBounds == null && !mapFirstZoomDone) { - zoomToBounds = getDefaultZoomBounds(); // the user-specified zoom couldn't find what it - // was looking for; try defaults once + zoomToBounds = getDefaultZoomBounds(); // the user-specified zoom couldn't find what it was looking for; try defaults once } } else if (!mapFirstZoomDone) { // Zoom once to the marks if marks exist zoomToBounds = new CourseMarksBoundsCalculator().calculateNewBounds(RaceMap.this); if (zoomToBounds == null) { - zoomToBounds = getDefaultZoomBounds(); // use default zoom, e.g., + zoomToBounds = getDefaultZoomBounds(); // use default zoom, e.g., } /* - * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that the map was - * just zoomed to the bounds of the marks, it was not a zoom or pan triggered by the user. - * As a consequence the mapZoomedOrPannedSinceLastRaceSelection option has to reset again. + * Reset the mapZoomedOrPannedSinceLastRaceSelection: In spite of the fact that + * the map was just zoomed to the bounds of the marks, it was not a zoom or pan + * triggered by the user. As a consequence the + * mapZoomedOrPannedSinceLastRaceSelection option has to reset again. */ } zoomMapToNewBounds(zoomToBounds); mapFirstZoomDone = true; - + updateEstimatedDuration(raceMapDataDTO.estimatedDuration); } } else { @@ -1183,13 +1130,13 @@ public class RaceMap extends AbstractCompositeComponent if (estimatedDurationOverlay == null) { estimatedDurationOverlay = new Label(""); estimatedDurationOverlay.setStyleName(raceMapStyle.estimatedDuration()); - if (showHeaderPanel) { + if(showHeaderPanel) { estimatedDurationOverlay.addStyleName(raceMapStyle.estimatedDurationWithHeader()); } map.setControls(ControlPosition.TOP_CENTER, estimatedDurationOverlay); } - estimatedDurationOverlay.setText(stringMessages.estimatedDuration() + " " - + DateAndTimeFormatterUtil.formatElapsedTime(estimatedDuration.asMillis())); + estimatedDurationOverlay.setText(stringMessages.estimatedDuration() + + " " + DateAndTimeFormatterUtil.formatElapsedTime(estimatedDuration.asMillis())); } @@ -1197,18 +1144,17 @@ public class RaceMap extends AbstractCompositeComponent * @param hasTailOverlapForCompetitor * if for a competitor whose fixes are provided in fixesForCompetitors this holds * false, any fixes previously stored for that competitor are removed, and the tail is - * deleted from the map (see {@link #removeTail(CompetitorWithBoatDTO)}); the new fixes are then added to - * the {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). - * If this map holds true, {@link #mergeFixes(CompetitorWithBoatDTO, List, long)} is used to - * merge the new fixes from fixesForCompetitors into the {@link #fixes} collection, and the - * tail is left unchanged. NOTE: When a non-overlapping set of fixes is updated - * (false), this map's record for the competitor is UPDATED to true - * after the tail deletion and {@link #fixes} replacement has taken place. This helps in cases where this - * update is only one of two into which an original request was split (one quick update of the tail's - * head and another one for the longer tail itself), such that the second request that uses the - * same map will be considered having an overlap now, not leading to a replacement of the - * previous update originating from the same request. See also - * {@link FixesAndTails#updateFixes(Map, Map, TailFactory, long)}. + * deleted from the map (see {@link #removeTail(CompetitorWithBoatDTO)}); the new fixes are then added to the + * {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). If + * this map holds true, {@link #mergeFixes(CompetitorWithBoatDTO, List, long)} is used to merge the + * new fixes from fixesForCompetitors into the {@link #fixes} collection, and the tail is + * left unchanged. NOTE: When a non-overlapping set of fixes is updated (false), this + * map's record for the competitor is UPDATED to true after the tail deletion and + * {@link #fixes} replacement has taken place. This helps in cases where this update is only one of two + * into which an original request was split (one quick update of the tail's head and another one for the + * longer tail itself), such that the second request that uses the same map will be considered + * having an overlap now, not leading to a replacement of the previous update originating from the same + * request. See also {@link FixesAndTails#updateFixes(Map, Map, TailFactory, long)}. * @param updateTailsOnly * if true, only the tails are updated according to boatData and * hasTailOverlapForCompetitor, but the advantage line is not updated, and neither are the @@ -1219,15 +1165,13 @@ public class RaceMap extends AbstractCompositeComponent */ private void updateBoatPositions(final Date newTime, final long transitionTimeInMillis, final Map hasTailOverlapForCompetitor, - final Iterable competitorsToShow, - Map> boatData, boolean updateTailsOnly) { + final Iterable competitorsToShow, Map> boatData, boolean updateTailsOnly) { fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis); showBoatsOnMap(newTime, transitionTimeInMillis, /* re-calculate; it could have changed since the asynchronous request was made: */ getCompetitorsToShow(), updateTailsOnly); if (!updateTailsOnly) { - showCompetitorInfoOnMap(newTime, transitionTimeInMillis, - competitorSelection.getSelectedFilteredCompetitors()); + showCompetitorInfoOnMap(newTime, transitionTimeInMillis, competitorSelection.getSelectedFilteredCompetitors()); // even though the wind data is retrieved by a separate call, re-draw the advantage line because it needs to // adjust to new boat positions showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis); @@ -1235,13 +1179,13 @@ public class RaceMap extends AbstractCompositeComponent } private void showCourseSidelinesOnMap(List sidelinesDTOs) { - if (map != null && sidelinesDTOs != null) { + if (map != null && sidelinesDTOs != null ) { Map toRemoveSidelines = new HashMap(courseSidelines); for (SidelineDTO sidelineDTO : sidelinesDTOs) { if (sidelineDTO.getMarks().size() == 2) { // right now we only support sidelines with 2 marks Polygon sideline = courseSidelines.get(sidelineDTO); LatLng[] sidelinePoints = new LatLng[sidelineDTO.getMarks().size()]; - int i = 0; + int i=0; for (MarkDTO sidelineMark : sidelineDTO.getMarks()) { sidelinePoints[i] = coordinateSystem.toLatLng(sidelineMark.position); i++; @@ -1254,7 +1198,7 @@ public class RaceMap extends AbstractCompositeComponent options.setStrokeOpacity(1.0); options.setFillColor(null); options.setFillOpacity(1.0); - + sideline = Polygon.newInstance(options); MVCArray pointsAsArray = MVCArray.newInstance(sidelinePoints); sideline.setPath(pointsAsArray); @@ -1288,29 +1232,25 @@ public class RaceMap extends AbstractCompositeComponent } } } - + private void showCourseMarksOnMap(CoursePositionsDTO courseDTO, long transitionTimeInMillis) { if (map != null && courseDTO != null) { WaypointDTO endWaypointForCurrentLegNumber = null; - if (courseDTO.currentLegNumber > 0 && courseDTO.currentLegNumber <= courseDTO.totalLegsCount) { + if(courseDTO.currentLegNumber > 0 && courseDTO.currentLegNumber <= courseDTO.totalLegsCount) { endWaypointForCurrentLegNumber = courseDTO.getEndWaypointForLegNumber(courseDTO.currentLegNumber); } - - Map toRemoveCourseMarks = new HashMap( - courseMarkOverlays); + + Map toRemoveCourseMarks = new HashMap(courseMarkOverlays); if (courseDTO.marks != null) { for (MarkDTO markDTO : courseDTO.marks) { boolean isSelected = false; - if (endWaypointForCurrentLegNumber != null - && Util.contains(endWaypointForCurrentLegNumber.controlPoint.getMarks(), markDTO)) { + if (endWaypointForCurrentLegNumber != null && Util.contains(endWaypointForCurrentLegNumber.controlPoint.getMarks(), markDTO)) { isSelected = true; } CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTO.getIdAsString()); if (courseMarkOverlay == null) { - courseMarkOverlay = new CourseMarkOverlay(map, RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, - markDTO, coordinateSystem, courseDTO); - courseMarkOverlay - .setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); + courseMarkOverlay = new CourseMarkOverlay(map, RaceMapOverlaysZIndexes.COURSEMARK_ZINDEX, markDTO, coordinateSystem, courseDTO); + courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); courseMarkOverlay.setBuoyZoneRadius(settings.getBuoyZoneRadius()); courseMarkOverlay.setSelected(isSelected); courseMarkOverlays.put(markDTO.getIdAsString(), courseMarkOverlay); @@ -1319,8 +1259,7 @@ public class RaceMap extends AbstractCompositeComponent courseMarkOverlay.addToMap(); } else { courseMarkOverlay.setMarkPosition(markDTO.position, transitionTimeInMillis); - courseMarkOverlay - .setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); + courseMarkOverlay.setShowBuoyZone(settings.getHelpLinesSettings().isVisible(HelpLineTypes.BUOYZONE)); courseMarkOverlay.setBuoyZoneRadius(settings.getBuoyZoneRadius()); courseMarkOverlay.setSelected(isSelected); courseMarkOverlay.setCourse(courseDTO); @@ -1337,10 +1276,10 @@ public class RaceMap extends AbstractCompositeComponent } } } - + /** - * Based on the mark positions in {@link #courseMarkOverlays}' values this method determines the center of gravity - * of these marks' {@link CourseMarkOverlay#getPosition() positions}. + * Based on the mark positions in {@link #courseMarkOverlays}' values this method determines the center of gravity of these marks' + * {@link CourseMarkOverlay#getPosition() positions}. */ private Position getCenterOfCourse() { ScalablePosition center = null; @@ -1364,18 +1303,16 @@ public class RaceMap extends AbstractCompositeComponent } } - private void showWindSensorsOnMap( - List> windSensorsList) { + private void showWindSensorsOnMap(List> windSensorsList) { if (map != null) { Set toRemoveWindSources = new HashSet(windSensorOverlays.keySet()); for (com.sap.sse.common.Util.Pair windSourcePair : windSensorsList) { - WindSource windSource = windSourcePair.getA(); + WindSource windSource = windSourcePair.getA(); WindTrackInfoDTO windTrackInfoDTO = windSourcePair.getB(); WindSensorOverlay windSensorOverlay = windSensorOverlays.get(windSource); if (windSensorOverlay == null) { - windSensorOverlay = createWindSensorOverlay(RaceMapOverlaysZIndexes.WINDSENSOR_ZINDEX, windSource, - windTrackInfoDTO); + windSensorOverlay = createWindSensorOverlay(RaceMapOverlaysZIndexes.WINDSENSOR_ZINDEX, windSource, windTrackInfoDTO); windSensorOverlays.put(windSource, windSensorOverlay); windSensorOverlay.addToMap(); } else { @@ -1393,13 +1330,11 @@ public class RaceMap extends AbstractCompositeComponent } } - private void showCompetitorInfoOnMap(final Date newTime, final long timeForPositionTransitionMillis, - final Iterable competitorsToShow) { + private void showCompetitorInfoOnMap(final Date newTime, final long timeForPositionTransitionMillis, final Iterable competitorsToShow) { if (map != null) { if (settings.isShowSelectedCompetitorsInfo()) { Set toRemoveCompetorInfoOverlaysIdsAsStrings = new HashSet<>(); - Util.addAll(competitorInfoOverlays.getCompetitorIdsAsStrings(), - toRemoveCompetorInfoOverlaysIdsAsStrings); + Util.addAll(competitorInfoOverlays.getCompetitorIdsAsStrings(), toRemoveCompetorInfoOverlaysIdsAsStrings); for (CompetitorDTO competitorDTO : competitorsToShow) { if (fixesAndTails.hasFixesFor(competitorDTO)) { GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = getBoatFix(competitorDTO, newTime); @@ -1407,13 +1342,11 @@ public class RaceMap extends AbstractCompositeComponent CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(competitorDTO); final Integer rank = getRank(competitorDTO); if (competitorInfoOverlay == null) { - competitorInfoOverlay = competitorInfoOverlays.createCompetitorInfoOverlay( - RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO, lastBoatFix, rank, - timeForPositionTransitionMillis); + competitorInfoOverlay = competitorInfoOverlays.createCompetitorInfoOverlay(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO, + lastBoatFix, rank, timeForPositionTransitionMillis); competitorInfoOverlay.addToMap(); } else { - competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix, - timeForPositionTransitionMillis); + competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix, timeForPositionTransitionMillis); } toRemoveCompetorInfoOverlaysIdsAsStrings.remove(competitorDTO.getIdAsString()); } @@ -1459,7 +1392,7 @@ public class RaceMap extends AbstractCompositeComponent } return timeForPositionTransitionMillisSmoothed; } - + /** * @param updateTailsOnly * if false, tails of competitors not in competitorsToShow are removed from the @@ -1485,17 +1418,14 @@ public class RaceMap extends AbstractCompositeComponent if (!fixesAndTails.hasTail(competitorDTO)) { fixesAndTails.createTailAndUpdateIndices(competitorDTO, tailsFromTime, tailsToTime, this); } else { - fixesAndTails.updateTail(competitorDTO, tailsFromTime, tailsToTime, - (int) (timeForPositionTransitionMillis == -1 ? -1 - : timeForPositionTransitionMillis / 2)); + fixesAndTails.updateTail(competitorDTO, tailsFromTime, tailsToTime, (int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2)); if (!updateTailsOnly) { competitorDTOsOfUnusedTails.remove(competitorDTO); } PolylineOptions newOptions = createTailStyle(competitorDTO, displayHighlighted(competitorDTO)); fixesAndTails.getTail(competitorDTO).setOptions(newOptions); } - boolean usedExistingBoatCanvas = updateBoatCanvasForCompetitor(competitorDTO, newTime, - timeForPositionTransitionMillis); + boolean usedExistingBoatCanvas = updateBoatCanvasForCompetitor(competitorDTO, newTime, timeForPositionTransitionMillis); if (usedExistingBoatCanvas && !updateTailsOnly) { competitorDTOsOfUnusedBoatCanvases.remove(competitorDTO); } @@ -1520,34 +1450,32 @@ public class RaceMap extends AbstractCompositeComponent * necessary if greater distances are allowed. */ public LatLng calculatePositionAlongRhumbline(LatLng position, double bearingDeg, Distance distance) { - double distanceRad = distance.getCentralAngleRad(); + double distanceRad = distance.getCentralAngleRad(); double lat1 = position.getLatitude() / 180. * Math.PI; double lon1 = position.getLongitude() / 180. * Math.PI; double bearingRad = bearingDeg / 180. * Math.PI; - double lat2 = Math.asin( - Math.sin(lat1) * Math.cos(distanceRad) + Math.cos(lat1) * Math.sin(distanceRad) * Math.cos(bearingRad)); - double lon2 = lon1 + Math.atan2(Math.sin(bearingRad) * Math.sin(distanceRad) * Math.cos(lat1), - Math.cos(distanceRad) - Math.sin(lat1) * Math.sin(lat2)); - lon2 = (lon2 + 3 * Math.PI) % (2 * Math.PI) - Math.PI; // normalize to -180..+180� + double lat2 = Math.asin(Math.sin(lat1) * Math.cos(distanceRad) + + Math.cos(lat1) * Math.sin(distanceRad) * Math.cos(bearingRad)); + double lon2 = lon1 + Math.atan2(Math.sin(bearingRad)*Math.sin(distanceRad)*Math.cos(lat1), + Math.cos(distanceRad)-Math.sin(lat1)*Math.sin(lat2)); + lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalize to -180..+180� // position is already in LatLng space, so no mapping through coordinateSystem is required here - return LatLng.newInstance(lat2 / Math.PI * 180., lon2 / Math.PI * 180.); + return LatLng.newInstance(lat2 / Math.PI * 180., lon2 / Math.PI * 180.); } - + /** - * Returns a pair whose first component is the leg number (one-based) of the competitor returned as the second - * component. The competitor returned currently has the best ranking in the quick ranks provided by the - * {@link #quickRanksDTOProvider}. + * Returns a pair whose first component is the leg number (one-based) of the competitor returned as the second component. + * The competitor returned currently has the best ranking in the quick ranks provided by the {@link #quickRanksDTOProvider}. */ private com.sap.sse.common.Util.Pair getBestVisibleCompetitorWithOneBasedLegNumber( Iterable competitorsToShow) { CompetitorDTO leadingCompetitorDTO = null; int legOfLeaderCompetitor = -1; int bestOneBasedRank = Integer.MAX_VALUE; - for (QuickRankDTO competitorFromBestToWorstAndOneBasedLegNumber : quickRanksDTOProvider.getQuickRanks() - .values()) { - if (Util.contains(competitorsToShow, competitorFromBestToWorstAndOneBasedLegNumber.competitor) - && competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased != 0 - && competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank < bestOneBasedRank) { + for (QuickRankDTO competitorFromBestToWorstAndOneBasedLegNumber : quickRanksDTOProvider.getQuickRanks().values()) { + if (Util.contains(competitorsToShow, competitorFromBestToWorstAndOneBasedLegNumber.competitor) && + competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased != 0 && + competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank < bestOneBasedRank) { leadingCompetitorDTO = competitorFromBestToWorstAndOneBasedLegNumber.competitor; legOfLeaderCompetitor = competitorFromBestToWorstAndOneBasedLegNumber.legNumberOneBased; bestOneBasedRank = competitorFromBestToWorstAndOneBasedLegNumber.oneBasedRank; @@ -1556,22 +1484,18 @@ public class RaceMap extends AbstractCompositeComponent } } } - return leadingCompetitorDTO == null ? null - : new com.sap.sse.common.Util.Pair(legOfLeaderCompetitor, leadingCompetitorDTO); + return leadingCompetitorDTO == null ? null : + new com.sap.sse.common.Util.Pair(legOfLeaderCompetitor, leadingCompetitorDTO); } - final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with - // the visible area of the map; bug 616 - - private void showAdvantageLine(Iterable competitorsToShow, Date date, - long timeForPositionTransitionMillis) { + final static Distance advantageLineLength = new MeterDistance(1000); // TODO this should probably rather scale with the visible area of the map; bug 616 + private void showAdvantageLine(Iterable competitorsToShow, Date date, long timeForPositionTransitionMillis) { if (map != null && lastRaceTimesInfo != null && !quickRanksDTOProvider.getQuickRanks().isEmpty() && lastCombinedWindTrackInfoDTO != null) { boolean drawAdvantageLine = false; if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) { // find competitor with highest rank - com.sap.sse.common.Util.Pair visibleLeaderInfo = getBestVisibleCompetitorWithOneBasedLegNumber( - competitorsToShow); + com.sap.sse.common.Util.Pair visibleLeaderInfo = getBestVisibleCompetitorWithOneBasedLegNumber(competitorsToShow); // the boat fix may be null; may mean that no positions were loaded yet for the leading visible boat; // don't show anything GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = null; @@ -1579,7 +1503,8 @@ public class RaceMap extends AbstractCompositeComponent boolean isLegTypeKnown = false; WindTrackInfoDTO windDataForLegMiddle = null; LegInfoDTO legInfoDTO = null; - if (visibleLeaderInfo != null && visibleLeaderInfo.getA() > 0 + if (visibleLeaderInfo != null + && visibleLeaderInfo.getA() > 0 && visibleLeaderInfo.getA() <= lastRaceTimesInfo.getLegInfos().size() // get wind at middle of leg for leading visible competitor && (windDataForLegMiddle = lastCombinedWindTrackInfoDTO @@ -1592,8 +1517,7 @@ public class RaceMap extends AbstractCompositeComponent } lastBoatFix = getBoatFix(visibleLeaderInfo.getB(), date); } - if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null - && lastBoatFix.speedWithBearing != null) { + if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null && lastBoatFix.speedWithBearing != null) { BoatDTO boat = competitorSelection.getBoat(visibleLeaderInfo.getB()); Distance distanceFromBoatPositionInKm = boat.getBoatClass().getHullLength(); // one hull length // implement and use Position.translateRhumb() @@ -1619,7 +1543,7 @@ public class RaceMap extends AbstractCompositeComponent if (rotatedBearingDeg2 < 0.0) { rotatedBearingDeg2 += 360.0; } - break; + break; } case REACHING: { rotatedBearingDeg1 = legInfoDTO.legBearingInDegrees + 90.0; @@ -1630,7 +1554,7 @@ public class RaceMap extends AbstractCompositeComponent if (rotatedBearingDeg2 < 0.0) { rotatedBearingDeg2 += 360.0; } - break; + break; } } MVCArray nextPath = MVCArray.newInstance(); @@ -1645,7 +1569,7 @@ public class RaceMap extends AbstractCompositeComponent options.setStrokeColor(ADVANTAGE_LINE_COLOR); options.setStrokeWeight(1); options.setStrokeOpacity(0.5); - + advantageLine = Polyline.newInstance(options); advantageTimer = new AdvantageLineAnimator(advantageLine); MVCArray pointsAsArray = MVCArray.newInstance(); @@ -1654,7 +1578,7 @@ public class RaceMap extends AbstractCompositeComponent advantageLine.setPath(pointsAsArray); advantageLine.setMap(map); Hoverline advantageHoverline = new Hoverline(advantageLine, options, this); - + advantageLineMouseOverHandler = new AdvantageLineMouseOverMapHandler( bearingOfCombinedWindInDeg, new Date(windFix.measureTimepoint)); advantageLine.addMouseOverHandler(advantageLineMouseOverHandler); @@ -1667,8 +1591,7 @@ public class RaceMap extends AbstractCompositeComponent } else { nextPath.push(advantageLinePos1); nextPath.push(advantageLinePos2); - advantageTimer.setNextPositionAndTransitionMillis(nextPath, - timeForPositionTransitionMillis); + advantageTimer.setNextPositionAndTransitionMillis(nextPath, timeForPositionTransitionMillis); if (advantageLineMouseOverHandler != null) { advantageLineMouseOverHandler.setTrueWindBearing(bearingOfCombinedWindInDeg); advantageLineMouseOverHandler.setDate(new Date(windFix.measureTimepoint)); @@ -1688,11 +1611,11 @@ public class RaceMap extends AbstractCompositeComponent } } } - + private final StringBuilder windwardStartLineMarkToFirstMarkLineText = new StringBuilder(); private final StringBuilder leewardStartLineMarkToFirstMarkLineText = new StringBuilder(); - - private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO) { + + private void showStartLineToFirstMarkTriangle(final CoursePositionsDTO courseDTO){ final List startMarkPositions = courseDTO.getStartMarkPositions(); if (startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1) { final Position windwardStartLinePosition = startMarkPositions.get(0); @@ -1700,10 +1623,12 @@ public class RaceMap extends AbstractCompositeComponent final Position firstMarkPosition = courseDTO.waypointPositions.get(1); windwardStartLineMarkToFirstMarkLineText.replace(0, windwardStartLineMarkToFirstMarkLineText.length(), stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(windwardStartLinePosition.getDistance(firstMarkPosition).getMeters()))); + .format(windwardStartLinePosition.getDistance(firstMarkPosition) + .getMeters()))); leewardStartLineMarkToFirstMarkLineText.replace(0, leewardStartLineMarkToFirstMarkLineText.length(), stringMessages.startLineToFirstMarkTriangle(numberFormatOneDecimal - .format(leewardStartLinePosition.getDistance(firstMarkPosition).getMeters()))); + .format(leewardStartLinePosition.getDistance(firstMarkPosition) + .getMeters()))); final LineInfoProvider windwardStartLineMarkToFirstMarkLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { @@ -1716,16 +1641,12 @@ public class RaceMap extends AbstractCompositeComponent return leewardStartLineMarkToFirstMarkLineText.toString(); } }; - windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* - * showLine - */ + windwardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(windwardStartLineMarkToFirstMarkLine, /* showLine */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider, "grey", 1, 1.0); - leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* - * showLine - */ + leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider, @@ -1738,135 +1659,112 @@ public class RaceMap extends AbstractCompositeComponent final LineInfoProvider startLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.startLine() + startLineAdvantageText; + return stringMessages.startLine()+startLineAdvantageText; } }; final LineInfoProvider finishLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { - return stringMessages.finishLine() + finishLineAdvantageText; + return stringMessages.finishLine()+finishLineAdvantageText; } }; private void showStartAndFinishAndCourseMiddleLines(final CoursePositionsDTO courseDTO) { - if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null - && !courseDTO.course.waypoints.isEmpty()) { + if (map != null && courseDTO != null && courseDTO.course != null && courseDTO.course.waypoints != null && + !courseDTO.course.waypoints.isEmpty()) { // draw the start line final WaypointDTO startWaypoint = courseDTO.course.waypoints.get(0); updateCountdownCanvas(startWaypoint); - final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 - : courseDTO.getStartMarkPositions().size(); - final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 - : courseDTO.getFinishMarkPositions().size(); - final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null - : courseDTO.getStartMarkPositions().get(0); - final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null - : courseDTO.getStartMarkPositions().get(1); + final int numberOfStartWaypointMarks = courseDTO.getStartMarkPositions() == null ? 0 : courseDTO.getStartMarkPositions().size(); + final int numberOfFinishWaypointMarks = courseDTO.getFinishMarkPositions() == null ? 0 : courseDTO.getFinishMarkPositions().size(); + final Position startLineLeftPosition = numberOfStartWaypointMarks == 0 ? null : courseDTO.getStartMarkPositions().get(0); + final Position startLineRightPosition = numberOfStartWaypointMarks < 2 ? null : courseDTO.getStartMarkPositions().get(1); if (courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { - startLineAdvantageText.replace(0, startLineAdvantageText.length(), - " " + stringMessages.lineAngleToWindAndAdvantage( - numberFormatOneDecimal.format(courseDTO.startLineLengthInMeters), - numberFormatOneDecimal.format(Math.abs( - courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), - courseDTO.startLineAdvantageousSide.name().charAt(0) - + courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), - numberFormatOneDecimal.format(courseDTO.startLineAdvantageInMeters))); + startLineAdvantageText.replace(0, startLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( + numberFormatOneDecimal.format(courseDTO.startLineLengthInMeters), + numberFormatOneDecimal.format(Math.abs(courseDTO.startLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), + courseDTO.startLineAdvantageousSide.name().charAt(0)+courseDTO.startLineAdvantageousSide.name().substring(1).toLowerCase(), + numberFormatOneDecimal.format(courseDTO.startLineAdvantageInMeters))); } else { startLineAdvantageText.delete(0, startLineAdvantageText.length()); } - final boolean showStartLineBasedOnCurrentLeg = numberOfStartWaypointMarks == 2 - && courseDTO.currentLegNumber <= 1; - final boolean showFinishLineBasedOnCurrentLeg = numberOfFinishWaypointMarks == 2 - && courseDTO.currentLegNumber == courseDTO.totalLegsCount; + final boolean showStartLineBasedOnCurrentLeg = numberOfStartWaypointMarks == 2 && courseDTO.currentLegNumber <= 1; + final boolean showFinishLineBasedOnCurrentLeg = numberOfFinishWaypointMarks == 2 && courseDTO.currentLegNumber == courseDTO.totalLegsCount; // show the line when STARTLINE is selected and the current leg is around the start leg, - // or when COURSEGEOMETRY is selected and the finish line isn't equal and wouldn't be shown at the same time - // based on the current leg. - // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will not be - // displayed if + // or when COURSEGEOMETRY is selected and the finish line isn't equal and wouldn't be shown at the same time based on the current leg. + // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will not be displayed if // based on the race progress the finish line is to be preferred, so only the finish line will be shown. - final boolean reallyShowStartLine = (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) - && showStartLineBasedOnCurrentLeg) - || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) - && (!showFinishLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO))); + final boolean reallyShowStartLine = + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE) && showStartLineBasedOnCurrentLeg) || + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && + (!showFinishLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO))); // show the line when FINISHLINE is selected and the current leg is the last leg, // or when COURSEGEOMETRY is selected and the start line isn't equal or the current leg is the last leg. - // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will be - // displayed unless + // With this, if COURSEGEOMETRY is selected and start and finish line are equal, the start line will be displayed unless // the finish line should take precedence based on race progress. - final boolean reallyShowFinishLine = showFinishLineBasedOnCurrentLeg - && (!showStartLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO)) - && (settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) - && showFinishLineBasedOnCurrentLeg) - || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) - && (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); - startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, startLineLeftPosition, - startLineRightPosition, startLineInfoProvider, "#ffffff", 1, 1.0); + final boolean reallyShowFinishLine = showFinishLineBasedOnCurrentLeg && + (!showStartLineBasedOnCurrentLeg || !startLineEqualsFinishLine(courseDTO)) && + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) && showFinishLineBasedOnCurrentLeg) || + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && + (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); + startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, + startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff", 1, 1.0); // draw the finish line - final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null - : courseDTO.getFinishMarkPositions().get(0); - final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null - : courseDTO.getFinishMarkPositions().get(1); + final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0); + final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null : courseDTO.getFinishMarkPositions().get(1); if (courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind != null) { - finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), - " " + stringMessages.lineAngleToWindAndAdvantage( - numberFormatOneDecimal.format(courseDTO.finishLineLengthInMeters), - numberFormatOneDecimal.format(Math.abs( - courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), - courseDTO.finishLineAdvantageousSide.name().charAt(0) - + courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), - numberFormatOneDecimal.format(courseDTO.finishLineAdvantageInMeters))); + finishLineAdvantageText.replace(0, finishLineAdvantageText.length(), " "+stringMessages.lineAngleToWindAndAdvantage( + numberFormatOneDecimal.format(courseDTO.finishLineLengthInMeters), + numberFormatOneDecimal.format(Math.abs(courseDTO.finishLineAngleFromPortToStarboardWhenApproachingLineToCombinedWind)), + courseDTO.finishLineAdvantageousSide.name().charAt(0)+courseDTO.finishLineAdvantageousSide.name().substring(1).toLowerCase(), + numberFormatOneDecimal.format(courseDTO.finishLineAdvantageInMeters))); } else { finishLineAdvantageText.delete(0, finishLineAdvantageText.length()); } - finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, finishLineLeftPosition, - finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR, 1, 1.0); + finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, + finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR, 1, 1.0); // the control point pairs for which we already decided whether or not // to show a course middle line for; values tell whether to show the line and for which zero-based // start waypoint index to do so; when for an equal control point pair multiple decisions with different // outcome are made, a decision to show the line overrules the decision to not show it (OR-semantics) final Map, Pair> keysAlreadyHandled = new HashMap<>(); - for (int zeroBasedIndexOfStartWaypoint = 0; zeroBasedIndexOfStartWaypoint < courseDTO.waypointPositions - .size() - 1; zeroBasedIndexOfStartWaypoint++) { + for (int zeroBasedIndexOfStartWaypoint = 0; zeroBasedIndexOfStartWaypoint key = getCourseMiddleLinesKey(courseDTO, zeroBasedIndexOfStartWaypoint); - boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() - || settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) - || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE) - && courseDTO.currentLegNumber > 0 - && courseDTO.currentLegNumber - 1 == zeroBasedIndexOfStartWaypoint); + boolean showCourseMiddleLine = keysAlreadyHandled.containsKey(key) && keysAlreadyHandled.get(key).getA() || + settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) || + (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEMIDDLELINE) + && courseDTO.currentLegNumber > 0 + && courseDTO.currentLegNumber-1 == zeroBasedIndexOfStartWaypoint); keysAlreadyHandled.put(key, new Pair<>(showCourseMiddleLine, zeroBasedIndexOfStartWaypoint)); } Set> keysToConsider = new HashSet<>(keysAlreadyHandled.keySet()); keysToConsider.addAll(courseMiddleLines.keySet()); for (final Set key : keysToConsider) { - final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) - ? keysAlreadyHandled.get(key).getB() - : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter + final int zeroBasedIndexOfStartWaypoint = keysAlreadyHandled.containsKey(key) ? + keysAlreadyHandled.get(key).getB() : 0; // if not handled, the line will be removed, so the waypoint index doesn't matter final Pair showLineAndZeroBasedIndexOfStartWaypoint = keysAlreadyHandled.get(key); - final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null - && showLineAndZeroBasedIndexOfStartWaypoint.getA(); - courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), - zeroBasedIndexOfStartWaypoint, showCourseMiddleLine)); + final boolean showCourseMiddleLine = showLineAndZeroBasedIndexOfStartWaypoint != null && showLineAndZeroBasedIndexOfStartWaypoint.getA(); + courseMiddleLines.put(key, showOrRemoveCourseMiddleLine(courseDTO, courseMiddleLines.get(key), zeroBasedIndexOfStartWaypoint, showCourseMiddleLine)); } } } private boolean startLineEqualsFinishLine(CoursePositionsDTO courseDTO) { final List waypoints; - return courseDTO != null && courseDTO.course != null && (waypoints = courseDTO.course.waypoints) != null - && waypoints.get(0).controlPoint.equals(waypoints.get(waypoints.size() - 1).controlPoint); + return courseDTO != null && courseDTO.course != null && + (waypoints = courseDTO.course.waypoints) != null && + waypoints.get(0).controlPoint.equals(waypoints.get(waypoints.size()-1).controlPoint); } /** - * Given a zero-based index into courseDTO's {@link RaceCourseDTO#waypoints waypoints list} that - * denotes the start waypoint of the leg in question, returns a key that can be used for the - * {@link #courseMiddleLines} map, consisting of a set that holds the two {@link ControlPointDTO}s representing the - * start and finish control point of that leg. + * Given a zero-based index into courseDTO's {@link RaceCourseDTO#waypoints waypoints list} that denotes the start + * waypoint of the leg in question, returns a key that can be used for the {@link #courseMiddleLines} map, consisting of a set + * that holds the two {@link ControlPointDTO}s representing the start and finish control point of that leg. */ private Set getCourseMiddleLinesKey(final CoursePositionsDTO courseDTO, final int zeroBasedIndexOfStartWaypoint) { ControlPointDTO startControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint).controlPoint; - ControlPointDTO endControlPoint = courseDTO.course.waypoints - .get(zeroBasedIndexOfStartWaypoint + 1).controlPoint; + ControlPointDTO endControlPoint = courseDTO.course.waypoints.get(zeroBasedIndexOfStartWaypoint+1).controlPoint; final Set key = new HashSet<>(); key.add(startControlPoint); key.add(endControlPoint); @@ -1882,28 +1780,26 @@ public class RaceMap extends AbstractCompositeComponent * @return null if the line is not shown; the polyline object representing the line being displayed * otherwise */ - private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, - Polyline lineToShowOrRemoveOrUpdate, final int zeroBasedIndexOfStartWaypoint, final boolean showLine) { + private Polyline showOrRemoveCourseMiddleLine(final CoursePositionsDTO courseDTO, Polyline lineToShowOrRemoveOrUpdate, + final int zeroBasedIndexOfStartWaypoint, final boolean showLine) { final Position position1DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint); - final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint + 1); + final Position position2DTO = courseDTO.waypointPositions.get(zeroBasedIndexOfStartWaypoint+1); final LineInfoProvider lineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { final StringBuilder sb = new StringBuilder(); sb.append(stringMessages.courseMiddleLine()); sb.append('\n'); - sb.append(numberFormatNoDecimal.format(Math.abs(position1DTO.getDistance(position2DTO).getMeters())) - + stringMessages.metersUnit()); + sb.append(numberFormatNoDecimal.format( + Math.abs(position1DTO.getDistance(position2DTO).getMeters()))+stringMessages.metersUnit()); if (lastCombinedWindTrackInfoDTO != null) { - final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO - .getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint); - if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null - && !windTrackAtLegMiddle.windFixes.isEmpty()) { + final WindTrackInfoDTO windTrackAtLegMiddle = lastCombinedWindTrackInfoDTO.getCombinedWindOnLegMiddle(zeroBasedIndexOfStartWaypoint); + if (windTrackAtLegMiddle != null && windTrackAtLegMiddle.windFixes != null && !windTrackAtLegMiddle.windFixes.isEmpty()) { WindDTO windAtLegMiddle = windTrackAtLegMiddle.windFixes.get(0); final double legBearingDeg = position1DTO.getBearingGreatCircle(position2DTO).getDegrees(); - final String diff = numberFormatOneDecimal - .format(Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg - legBearingDeg), - Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg - legBearingDeg))); + final String diff = numberFormatOneDecimal.format( + Math.min(Math.abs(windAtLegMiddle.dampenedTrueWindBearingDeg-legBearingDeg), + Math.abs(windAtLegMiddle.dampenedTrueWindFromDeg-legBearingDeg))); sb.append(", "); sb.append(stringMessages.degreesToWind(diff)); } @@ -1911,14 +1807,13 @@ public class RaceMap extends AbstractCompositeComponent return sb.toString(); } }; - return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, - lineInfoProvider, "#2268a0", 1, 1.0); + return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, lineInfoProvider, "#2268a0", 1, 1.0); } private interface LineInfoProvider { String getLineInfo(); } - + /** * @param showLine * tells whether or not to show the line; if the lineToShowOrRemoveOrUpdate references a @@ -1947,8 +1842,7 @@ public class RaceMap extends AbstractCompositeComponent lineToShowOrRemoveOrUpdate = Polyline.newInstance(options); lineToShowOrRemoveOrUpdate.setPath(pointsAsArray); lineToShowOrRemoveOrUpdate.setMap(map); - Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, - this); + Hoverline lineToShowOrRemoveOrUpdateHoverline = new Hoverline(lineToShowOrRemoveOrUpdate, options, this); lineToShowOrRemoveOrUpdate.addMouseOverHandler(new MouseOverMapHandler() { @Override public void onEvent(MouseOverMapEvent event) { @@ -1966,8 +1860,7 @@ public class RaceMap extends AbstractCompositeComponent pointsAsArray.removeAt(1); pointsAsArray.removeAt(0); } - adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, - lineInfoProvider); + adjustInfoOverlayForVisibleLine(lineToShowOrRemoveOrUpdate, position1DTO, position2DTO, lineInfoProvider); pointsAsArray.insertAt(0, courseMiddleLinePoint1); pointsAsArray.insertAt(1, courseMiddleLinePoint2); } else { @@ -1982,8 +1875,7 @@ public class RaceMap extends AbstractCompositeComponent } private void adjustInfoOverlayForRemovedLine(Polyline lineToShowOrRemoveOrUpdate) { - SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry - .remove(lineToShowOrRemoveOrUpdate); + SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); if (infoOverlay != null) { infoOverlay.removeFromMap(); } @@ -1994,10 +1886,9 @@ public class RaceMap extends AbstractCompositeComponent SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.get(lineToShowOrRemoveOrUpdate); if (getSettings().getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) { if (infoOverlay == null) { - infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, - lineInfoProvider.getLineInfo(), coordinateSystem); + infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, lineInfoProvider.getLineInfo(), coordinateSystem); infoOverlaysForLinesForCourseGeometry.put(lineToShowOrRemoveOrUpdate, infoOverlay); - infoOverlay.addToMap(); + infoOverlay.addToMap(); } else { infoOverlay.setInfoText(lineInfoProvider.getLineInfo()); } @@ -2006,29 +1897,28 @@ public class RaceMap extends AbstractCompositeComponent infoOverlay.draw(); } else { if (infoOverlay != null) { - infoOverlay.removeFromMap(); - infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); + infoOverlay.removeFromMap(); + infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); } } } - + /** - * If, according to {@link #lastRaceTimesInfo} and {@link #timer} the race is still in the pre-start phase, show a - * {@link SmallTransparentInfoOverlay} at the start line that shows the count down. + * If, according to {@link #lastRaceTimesInfo} and {@link #timer} the race is + * still in the pre-start phase, show a {@link SmallTransparentInfoOverlay} at the + * start line that shows the count down. */ private void updateCountdownCanvas(WaypointDTO startWaypoint) { - if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null - || Util.isEmpty(startWaypoint.controlPoint.getMarks()) || lastRaceTimesInfo == null - || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { + if (!settings.isShowSelectedCompetitorsInfo() || startWaypoint == null || Util.isEmpty(startWaypoint.controlPoint.getMarks()) + || lastRaceTimesInfo == null || lastRaceTimesInfo.startOfRace == null || timer.getTime().after(lastRaceTimesInfo.startOfRace)) { if (countDownOverlay != null) { countDownOverlay.removeFromMap(); countDownOverlay = null; } } else { long timeToStartInMs = lastRaceTimesInfo.startOfRace.getTime() - timer.getTime().getTime(); - String countDownText = timeToStartInMs > 1000 - ? stringMessages.timeToStart(DateAndTimeFormatterUtil.formatElapsedTime(timeToStartInMs)) - : stringMessages.start(); + String countDownText = timeToStartInMs > 1000 ? stringMessages.timeToStart(DateAndTimeFormatterUtil + .formatElapsedTime(timeToStartInMs)) : stringMessages.start(); if (countDownOverlay == null) { countDownOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, countDownText, coordinateSystem, getSettings().getStartCountDownFontSizeScaling()); @@ -2036,8 +1926,7 @@ public class RaceMap extends AbstractCompositeComponent } else { countDownOverlay.setInfoText(countDownText); } - countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, - /* transition time */ -1); + countDownOverlay.setPosition(startWaypoint.controlPoint.getMarks().iterator().next().position, /* transition time */ -1); countDownOverlay.draw(); } } @@ -2056,8 +1945,7 @@ public class RaceMap extends AbstractCompositeComponent int MAX_ZOOM = 20; // maximum zoom-level that should be automatically selected double LOG2 = Math.log(2.0); double deltaLng = bounds.getNorthEast().getLongitude() - bounds.getSouthWest().getLongitude(); - double deltaLat = getMercatorLatitude(bounds.getNorthEast().getLatitude()) - - getMercatorLatitude(bounds.getSouthWest().getLatitude()); + double deltaLat = getMercatorLatitude(bounds.getNorthEast().getLatitude()) - getMercatorLatitude(bounds.getSouthWest().getLatitude()); if ((deltaLng == 0) && (deltaLat == 0)) { return MAX_ZOOM; } @@ -2065,28 +1953,27 @@ public class RaceMap extends AbstractCompositeComponent deltaLng += 360; } int zoomLng = (int) Math.floor(Math.log(map.getDiv().getClientWidth() * 360 / deltaLng / GLOBE_PXSIZE) / LOG2); - int zoomLat = (int) Math - .floor(Math.log(map.getDiv().getClientHeight() * 2 * Math.PI / deltaLat / GLOBE_PXSIZE) / LOG2); + int zoomLat = (int) Math.floor(Math.log(map.getDiv().getClientHeight() * 2 * Math.PI / deltaLat / GLOBE_PXSIZE) / LOG2); return Math.min(Math.min(zoomLat, zoomLng), MAX_ZOOM); } - + private void zoomMapToNewBounds(LatLngBounds newBounds) { if (newBounds != null) { LatLngBounds currentMapBounds; - if (map.getBounds() == null || !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds) + if (map.getBounds() == null + || !BoundsUtil.contains((currentMapBounds = map.getBounds()), newBounds) || graticuleAreaRatio(currentMapBounds, newBounds) > 10) { // only change bounds if the new bounds don't fit into the current map zoom - Iterable oldZoomTypesToConsiderSettings = settings.getZoomSettings() - .getTypesToConsiderOnZoom(); + Iterable oldZoomTypesToConsiderSettings = settings.getZoomSettings().getTypesToConsiderOnZoom(); setAutoZoomInProgress(true); autoZoomLatLngBounds = newBounds; - int newZoomLevel = getZoomLevel(autoZoomLatLngBounds); + int newZoomLevel = getZoomLevel(autoZoomLatLngBounds); if (newZoomLevel != map.getZoom()) { // distinguish between zoom-in and zoom-out, because the sequence of panTo() and setZoom() // appears different on the screen due to map-animations // following sequences keep the selected boats allways visible: - // zoom-in : 1. panTo(), 2. setZoom() - // zoom-out: 1. setZoom(), 2. panTo() + // zoom-in : 1. panTo(), 2. setZoom() + // zoom-out: 1. setZoom(), 2. panTo() autoZoomIn = newZoomLevel > map.getZoom(); autoZoomOut = !autoZoomIn; autoZoomLevel = newZoomLevel; @@ -2099,14 +1986,13 @@ public class RaceMap extends AbstractCompositeComponent } else { map.panTo(autoZoomLatLngBounds.getCenter()); } - RaceMapZoomSettings restoredZoomSettings = new RaceMapZoomSettings(oldZoomTypesToConsiderSettings, - settings.getZoomSettings().isZoomToSelectedCompetitors()); + RaceMapZoomSettings restoredZoomSettings = new RaceMapZoomSettings(oldZoomTypesToConsiderSettings, settings.getZoomSettings().isZoomToSelectedCompetitors()); settings = new RaceMapSettings(settings, restoredZoomSettings); setAutoZoomInProgress(false); } } } - + private double graticuleAreaRatio(LatLngBounds containing, LatLngBounds contained) { assert BoundsUtil.contains(containing, contained); double containingAreaRatio = getGraticuleArea(containing) / getGraticuleArea(contained); @@ -2114,29 +2000,26 @@ public class RaceMap extends AbstractCompositeComponent } /** - * A much simplified "area" calculation for a {@link Bounds} object, multiplying the differences in latitude and - * longitude degrees. The result therefore is in the order of magnitude of 60*60 square nautical miles. + * A much simplified "area" calculation for a {@link Bounds} object, multiplying the differences in latitude and longitude degrees. + * The result therefore is in the order of magnitude of 60*60 square nautical miles. */ private double getGraticuleArea(LatLngBounds bounds) { - return ((BoundsUtil.isCrossesDateLine(bounds) ? bounds.getNorthEast().getLongitude() + 360 - : bounds.getNorthEast().getLongitude()) - bounds.getSouthWest().getLongitude()) - * (bounds.getNorthEast().getLatitude() - bounds.getSouthWest().getLatitude()); + return ((BoundsUtil.isCrossesDateLine(bounds) ? bounds.getNorthEast().getLongitude()+360 : bounds.getNorthEast().getLongitude())-bounds.getSouthWest().getLongitude()) * + (bounds.getNorthEast().getLatitude() - bounds.getSouthWest().getLatitude()); } private void setAutoZoomInProgress(boolean autoZoomInProgress) { this.autoZoomInProgress = autoZoomInProgress; } - + boolean isAutoZoomInProgress() { return autoZoomInProgress; } - + /** - * @param timeForPositionTransitionMillis - * use -1 to not animate the position transition, e.g., during map zoom or non-play + * @param timeForPositionTransitionMillis use -1 to not animate the position transition, e.g., during map zoom or non-play */ - private boolean updateBoatCanvasForCompetitor(CompetitorDTO competitorDTO, Date date, - long timeForPositionTransitionMillis) { + private boolean updateBoatCanvasForCompetitor(CompetitorDTO competitorDTO, Date date, long timeForPositionTransitionMillis) { boolean hasTimeJumped = timeForPositionTransitionMillis > 3 * timer.getRefreshInterval(); if (hasTimeJumped) { timeForPositionTransitionMillis = -1; @@ -2146,8 +2029,7 @@ public class RaceMap extends AbstractCompositeComponent if (lastBoatFix != null) { BoatOverlay boatOverlay = boatOverlays.get(competitorDTO); if (boatOverlay == null) { - boatOverlay = createBoatOverlay(RaceMapOverlaysZIndexes.BOATS_ZINDEX, competitorDTO, - displayHighlighted(competitorDTO)); + boatOverlay = createBoatOverlay(RaceMapOverlaysZIndexes.BOATS_ZINDEX, competitorDTO, displayHighlighted(competitorDTO)); boatOverlays.put(competitorDTO, boatOverlay); boatOverlay.setDisplayMode(displayHighlighted(competitorDTO)); boatOverlay.setBoatFix(lastBoatFix, timeForPositionTransitionMillis); @@ -2174,47 +2056,48 @@ public class RaceMap extends AbstractCompositeComponent return DisplayMode.DEFAULT; } } - } else { + } + else{ return competitorSelection.isSelected(competitorDTO) ? DisplayMode.SELECTED : DisplayMode.DEFAULT; } - + } - - private boolean isSomeOtherCompetitorSelected() { + + private boolean isSomeOtherCompetitorSelected(){ return Util.size(competitorSelection.getSelectedCompetitors()) > 0; } - + private class CourseMarkInfoWindowClickHandler implements ClickMapHandler { private final MarkDTO markDTO; private final CourseMarkOverlay courseMarkOverlay; - + public CourseMarkInfoWindowClickHandler(MarkDTO markDTO, CourseMarkOverlay courseMarkOverlay) { this.markDTO = markDTO; this.courseMarkOverlay = courseMarkOverlay; } - + @Override public void onEvent(ClickMapEvent event) { LatLng latlng = courseMarkOverlay.getMarkLatLngPosition(); showMarkInfoWindow(markDTO, latlng); } } - + private void registerCourseMarkInfoWindowClickHandler(final String markDTOIdAsString) { final CourseMarkOverlay courseMarkOverlay = courseMarkOverlays.get(markDTOIdAsString); - courseMarkClickHandlers.put(markDTOIdAsString, courseMarkOverlay.addClickHandler( - new CourseMarkInfoWindowClickHandler(markDTOs.get(markDTOIdAsString), courseMarkOverlay))); + courseMarkClickHandlers.put(markDTOIdAsString, + courseMarkOverlay.addClickHandler(new CourseMarkInfoWindowClickHandler(markDTOs.get(markDTOIdAsString), courseMarkOverlay))); } - + public void registerAllCourseMarkInfoWindowClickHandlers() { for (String markDTOIdAsString : markDTOs.keySet()) { registerCourseMarkInfoWindowClickHandler(markDTOIdAsString); } } - + public void unregisterAllCourseMarkInfoWindowClickHandlers() { Iterator> iterator = courseMarkClickHandlers.entrySet().iterator(); - while (iterator.hasNext()) { + while(iterator.hasNext()) { Entry handler = iterator.next(); handler.getValue().removeHandler(); iterator.remove(); @@ -2223,8 +2106,7 @@ public class RaceMap extends AbstractCompositeComponent private BoatOverlay createBoatOverlay(int zIndex, final CompetitorDTO competitorDTO, DisplayMode displayMode) { final BoatDTO boatOfCompetitor = competitorSelection.getBoat(competitorDTO); - final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, boatOfCompetitor, - competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); + final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, boatOfCompetitor, competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); boatCanvas.setDisplayMode(displayMode); boatCanvas.addClickHandler(event -> { GPSFixDTOWithSpeedWindTackAndLegType latestFixForCompetitor = getBoatFix(competitorDTO, timer.getTime()); @@ -2249,10 +2131,8 @@ public class RaceMap extends AbstractCompositeComponent return boatCanvas; } - protected WindSensorOverlay createWindSensorOverlay(int zIndex, final WindSource windSource, - final WindTrackInfoDTO windTrackInfoDTO) { - final WindSensorOverlay windSensorOverlay = new WindSensorOverlay(map, zIndex, raceMapImageManager, - stringMessages, coordinateSystem); + protected WindSensorOverlay createWindSensorOverlay(int zIndex, final WindSource windSource, final WindTrackInfoDTO windTrackInfoDTO) { + final WindSensorOverlay windSensorOverlay = new WindSensorOverlay(map, zIndex, raceMapImageManager, stringMessages, coordinateSystem); windSensorOverlay.setWindInfo(windTrackInfoDTO, windSource); windSensorOverlay.addClickHandler(new ClickMapHandler() { @Override @@ -2303,14 +2183,12 @@ public class RaceMap extends AbstractCompositeComponent flowPanel.add(value); return flowPanel; } - + private Widget getInfoWindowContent(MarkDTO markDTO) { VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.mark(), markDTO.getName())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), - markDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); - vPanel.add( - createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsSignedDecimalDegrees())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), markDTO.position.getAsSignedDecimalDegrees())); return vPanel; } @@ -2345,15 +2223,9 @@ public class RaceMap extends AbstractCompositeComponent } if (maneuver.getManeuverLoss() != null) { Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), - numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " - + stringMessages.metersUnit()); + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), - maneuver.getType()); - if (!maneuverLossCheckBoxValueStore.contains(t) && settings.isShowManeuverLossVisualization()) { - maneuverLossCheckBoxValueStore.add(t); - visualizeManeuverLoss(maneuver, true, competitor); - } + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.contains(t)); maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { @Override @@ -2372,12 +2244,13 @@ public class RaceMap extends AbstractCompositeComponent vPanel.add(hPanel); } if (maneuver.getMarkPassingTimePoint() != null) { - vPanel.add(createInfoWindowLabelAndValue(stringMessages.markPassing(), - DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getMarkPassingTimePoint()) - + (maneuver.getMarkPassingSide() == null ? "" - : " (" + (maneuver.getMarkPassingSide() == NauticalSide.PORT - ? stringMessages.portSide() - : stringMessages.starboardSide()) + ")"))); + vPanel.add( + createInfoWindowLabelAndValue(stringMessages.markPassing(), + DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getMarkPassingTimePoint()) + + (maneuver.getMarkPassingSide() == null ? "" + : " (" + (maneuver.getMarkPassingSide() == NauticalSide.PORT + ? stringMessages.portSide() : stringMessages.starboardSide()) + + ")"))); } return vPanel; } @@ -2387,14 +2260,11 @@ public class RaceMap extends AbstractCompositeComponent final VerticalPanel vPanel = new VerticalPanel(); final Anchor windSourceNameAnchor = new Anchor(WindSourceTypeFormatter.format(windSource, stringMessages)); vPanel.add(createInfoWindowLabelWithWidget(stringMessages.windSource(), windSourceNameAnchor)); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.wind(), - Math.round(windDTO.dampenedTrueWindFromDeg) + " " + stringMessages.degreesShort())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.windSpeed(), - numberFormatOneDecimal.format(windDTO.dampenedTrueWindSpeedInKnots))); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.wind(), Math.round(windDTO.dampenedTrueWindFromDeg) + " " + stringMessages.degreesShort())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.windSpeed(), numberFormatOneDecimal.format(windDTO.dampenedTrueWindSpeedInKnots))); final MillisecondsTimePoint timePoint = new MillisecondsTimePoint(windDTO.measureTimepoint); vPanel.add(createInfoWindowLabelAndValue(stringMessages.time(), timePoint.asDate().toString())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), - windDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.position(), windDTO.position.getAsDegreesAndDecimalMinutesWithCardinalPoints())); final Label positionInDecimalDegreesLabel = new Label(windDTO.position.getAsSignedDecimalDegrees()); positionInDecimalDegreesLabel.setWordWrap(false); positionInDecimalDegreesLabel.getElement().getStyle().setFloat(Style.Float.LEFT); @@ -2434,8 +2304,7 @@ public class RaceMap extends AbstractCompositeComponent private Widget getInfoWindowContent(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType lastFix) { final VerticalPanel vPanel = new VerticalPanel(); vPanel.add(createInfoWindowLabelAndValue(stringMessages.competitor(), competitorDTO.getName())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), - competitorSelection.getBoat(competitorDTO).getSailId())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.sailNumber(), competitorSelection.getBoat(competitorDTO).getSailId())); final Integer rank = getRank(competitorDTO); if (rank != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.rank(), String.valueOf(rank))); @@ -2446,10 +2315,8 @@ public class RaceMap extends AbstractCompositeComponent speedWithBearing = new SpeedWithBearingDTO(0, 0); } vPanel.add(createInfoWindowLabelAndValue(stringMessages.speed(), - NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " " - + stringMessages.knotsUnit())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), - (int) speedWithBearing.bearingInDegrees + " " + stringMessages.degreesShort())); + NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " "+stringMessages.knotsUnit())); + vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), (int) speedWithBearing.bearingInDegrees + " "+stringMessages.degreesShort())); if (lastFix.degreesBoatToTheWind != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.degreesBoatToTheWind(), (int) Math.abs(lastFix.degreesBoatToTheWind) + " " + stringMessages.degreesShort())); @@ -2466,13 +2333,11 @@ public class RaceMap extends AbstractCompositeComponent new AsyncCallback>>() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), - true /* silentMode */); + errorReporter.reportError("Error obtaining douglas positions: " + caught.getMessage(), true /*silentMode */); } @Override - public void onSuccess( - Map> result) { + public void onSuccess(Map> result) { lastDouglasPeuckerResult = result; if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); @@ -2484,12 +2349,10 @@ public class RaceMap extends AbstractCompositeComponent } } }); - sailingService.getManeuvers(race, timeRange, - new AsyncCallback>>() { + sailingService.getManeuvers(race, timeRange, new AsyncCallback>>() { @Override public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), - true /* silentMode */); + errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), true /*silentMode */); } @Override @@ -2512,34 +2375,31 @@ public class RaceMap extends AbstractCompositeComponent /** * @return the {@link CompetitorSelectionProvider#getSelectedCompetitors()} if * {@link RaceMapSettings#isShowOnlySelectedCompetitors() only selected competitors are to be shown}, the - * {@link CompetitorSelectionProvider#getFilteredCompetitors() filtered competitors} otherwise. In both - * cases, if we have {@link RaceCompetitorSet information about the competitors participating} in this map's - * race, the result set is reduced to those, no matter if other regatta participants would otherwise have - * been in the result set + * {@link CompetitorSelectionProvider#getFilteredCompetitors() filtered competitors} otherwise. In both cases, + * if we have {@link RaceCompetitorSet information about the competitors participating} in this map's race, + * the result set is reduced to those, no matter if other regatta participants would otherwise have been in + * the result set */ private Iterable getCompetitorsToShow() { final Set result = new HashSet<>(); Iterable selection = competitorSelection.getSelectedCompetitors(); - final Set raceCompetitorIdsAsString = raceCompetitorSet - .getIdsOfCompetitorsParticipatingInRaceAsStrings(); + final Set raceCompetitorIdsAsString = raceCompetitorSet.getIdsOfCompetitorsParticipatingInRaceAsStrings(); if (!settings.isShowOnlySelectedCompetitors() || Util.isEmpty(selection)) { for (final CompetitorDTO filteredCompetitor : competitorSelection.getFilteredCompetitors()) { - if (raceCompetitorIdsAsString == null - || raceCompetitorIdsAsString.contains(filteredCompetitor.getIdAsString())) { + if (raceCompetitorIdsAsString == null || raceCompetitorIdsAsString.contains(filteredCompetitor.getIdAsString())) { result.add(filteredCompetitor); } } } else { for (final CompetitorDTO selectedCompetitor : selection) { - if (raceCompetitorIdsAsString == null - || raceCompetitorIdsAsString.contains(selectedCompetitor.getIdAsString())) { + if (raceCompetitorIdsAsString == null || raceCompetitorIdsAsString.contains(selectedCompetitor.getIdAsString())) { result.add(selectedCompetitor); } } } return result; } - + protected void removeAllMarkDouglasPeuckerpoints() { if (douglasMarkers != null) { for (Marker marker : douglasMarkers) { @@ -2558,8 +2418,7 @@ public class RaceMap extends AbstractCompositeComponent } } - private void showMarkDouglasPeuckerPoints( - Map> gpsFixPointMapForCompetitors) { + private void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { douglasMarkers = new HashSet(); if (map != null && gpsFixPointMapForCompetitors != null) { Set keySet = gpsFixPointMapForCompetitors.keySet(); @@ -2570,7 +2429,7 @@ public class RaceMap extends AbstractCompositeComponent for (GPSFixDTOWithSpeedWindTackAndLegType fix : gpsFix) { LatLng latLng = coordinateSystem.toLatLng(fix.position); MarkerOptions options = MarkerOptions.newInstance(); - options.setTitle(fix.timepoint + ": " + fix.position + ", " + fix.speedWithBearing.toString()); + options.setTitle(fix.timepoint+": "+fix.position+", "+fix.speedWithBearing.toString()); Marker marker = Marker.newInstance(options); marker.setPosition(latLng); douglasMarkers.add(marker); @@ -2599,8 +2458,8 @@ public class RaceMap extends AbstractCompositeComponent private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { LatLng latLng = coordinateSystem.toLatLng(maneuver.getPosition()); - Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor.get( - new Pair(maneuver.getType(), ManeuverColor.getManeuverColor(maneuver))); + Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor + .get(new Pair(maneuver.getType(), ManeuverColor.getManeuverColor(maneuver))); MarkerOptions options = MarkerOptions.newInstance(); options.setIcon(maneuverMarker.getIcon_MarkerImage()); Marker marker = Marker.newInstance(options); @@ -2627,45 +2486,37 @@ public class RaceMap extends AbstractCompositeComponent GPSFixDTOWithSpeedWindTackAndLegType result = null; List competitorFixes = fixesAndTails.getFixes(competitorDTO); if (competitorFixes != null && !competitorFixes.isEmpty()) { - int i = Collections.binarySearch(competitorFixes, - new GPSFixDTOWithSpeedWindTackAndLegType(date, null, null, (WindDTO) null, null, null, false), + int i = Collections.binarySearch(competitorFixes, new GPSFixDTOWithSpeedWindTackAndLegType(date, null, null, (WindDTO) null, null, null, false), new Comparator() { - @Override - public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, - GPSFixDTOWithSpeedWindTackAndLegType o2) { - return o1.timepoint.compareTo(o2.timepoint); - } - }); - if (i < 0) { - i = -i - 1; // no perfect match; i is now the insertion point + @Override + public int compare(GPSFixDTOWithSpeedWindTackAndLegType o1, GPSFixDTOWithSpeedWindTackAndLegType o2) { + return o1.timepoint.compareTo(o2.timepoint); + } + }); + if (i<0) { + i = -i-1; // no perfect match; i is now the insertion point // if the insertion point is at the end, use last fix if (i >= competitorFixes.size()) { - result = competitorFixes.get(competitorFixes.size() - 1); + result = competitorFixes.get(competitorFixes.size()-1); } else if (i == 0) { // if the insertion point is at the beginning, use first fix result = competitorFixes.get(0); } else { - // competitorFixes must have at least two elements, and i points neither to the end nor the - // beginning; + // competitorFixes must have at least two elements, and i points neither to the end nor the beginning; // get the fix from i and i+1 whose timepoint is closer to date - final GPSFixDTOWithSpeedWindTackAndLegType fixBefore = competitorFixes.get(i - 1); + final GPSFixDTOWithSpeedWindTackAndLegType fixBefore = competitorFixes.get(i-1); final GPSFixDTOWithSpeedWindTackAndLegType fixAfter = competitorFixes.get(i); final GPSFixDTOWithSpeedWindTackAndLegType closer; - if (date.getTime() - fixBefore.timepoint.getTime() < fixAfter.timepoint.getTime() - - date.getTime()) { + if (date.getTime() - fixBefore.timepoint.getTime() < fixAfter.timepoint.getTime() - date.getTime()) { closer = fixBefore; } else { closer = fixAfter; } // now compute a weighted average depending on the time difference to "date" (see also bug 1924) - double factorForAfter = (double) (date.getTime() - fixBefore.timepoint.getTime()) - / (double) (fixAfter.timepoint.getTime() - fixBefore.timepoint.getTime()); - double factorForBefore = 1 - factorForAfter; - DegreePosition betweenPosition = new DegreePosition( - factorForBefore * fixBefore.position.getLatDeg() - + factorForAfter * fixAfter.position.getLatDeg(), - factorForBefore * fixBefore.position.getLngDeg() - + factorForAfter * fixAfter.position.getLngDeg()); + double factorForAfter = (double) (date.getTime()-fixBefore.timepoint.getTime()) / (double) (fixAfter.timepoint.getTime() - fixBefore.timepoint.getTime()); + double factorForBefore = 1-factorForAfter; + DegreePosition betweenPosition = new DegreePosition(factorForBefore*fixBefore.position.getLatDeg() + factorForAfter*fixAfter.position.getLatDeg(), + factorForBefore*fixBefore.position.getLngDeg() + factorForAfter*fixAfter.position.getLngDeg()); final double betweenBearing; if (fixBefore.speedWithBearing == null) { if (fixAfter.speedWithBearing == null) { @@ -2676,22 +2527,16 @@ public class RaceMap extends AbstractCompositeComponent } else if (fixAfter.speedWithBearing == null) { betweenBearing = fixBefore.speedWithBearing.bearingInDegrees; } else { - betweenBearing = new ScalableBearing( - new DegreeBearingImpl(fixBefore.speedWithBearing.bearingInDegrees)) - .multiply(factorForBefore) - .add(new ScalableBearing( - new DegreeBearingImpl(fixAfter.speedWithBearing.bearingInDegrees)) - .multiply(factorForAfter)) - .divide(1).getDegrees(); + betweenBearing = new ScalableBearing(new DegreeBearingImpl(fixBefore.speedWithBearing.bearingInDegrees)). + multiply(factorForBefore).add(new ScalableBearing(new DegreeBearingImpl(fixAfter.speedWithBearing.bearingInDegrees)). + multiply(factorForAfter)).divide(1).getDegrees(); } - SpeedWithBearingDTO betweenSpeed = new SpeedWithBearingDTO(factorForBefore - * (fixBefore.speedWithBearing == null ? 0 : fixBefore.speedWithBearing.speedInKnots) - + factorForAfter - * (fixAfter.speedWithBearing == null ? 0 : fixAfter.speedWithBearing.speedInKnots), + SpeedWithBearingDTO betweenSpeed = new SpeedWithBearingDTO( + factorForBefore*(fixBefore.speedWithBearing==null?0:fixBefore.speedWithBearing.speedInKnots) + + factorForAfter*(fixAfter.speedWithBearing==null?0:fixAfter.speedWithBearing.speedInKnots), betweenBearing); - result = new GPSFixDTOWithSpeedWindTackAndLegType(date, betweenPosition, betweenSpeed, - closer.degreesBoatToTheWind, closer.tack, closer.legType, - fixBefore.extrapolated || fixAfter.extrapolated); + result = new GPSFixDTOWithSpeedWindTackAndLegType(date, betweenPosition, betweenSpeed, closer.degreesBoatToTheWind, + closer.tack, closer.legType, fixBefore.extrapolated || fixAfter.extrapolated); } } else { // perfect match @@ -2744,8 +2589,7 @@ public class RaceMap extends AbstractCompositeComponent for (CompetitorDTO oneOfAllCompetitors : competitorSelection.getAllCompetitors()) { Polyline tail = fixesAndTails.getTail(oneOfAllCompetitors); if (tail != null) { - PolylineOptions newOptions = createTailStyle(oneOfAllCompetitors, - displayHighlighted(oneOfAllCompetitors)); + PolylineOptions newOptions = createTailStyle(oneOfAllCompetitors, displayHighlighted(oneOfAllCompetitors)); tail.setOptions(newOptions); } } @@ -2755,7 +2599,7 @@ public class RaceMap extends AbstractCompositeComponent zoomMapToNewBounds(zoomSettings.getNewBounds(this)); } } - + @Override public void removedFromSelection(CompetitorDTO competitor) { if (isShowAnyHelperLines()) { @@ -2787,7 +2631,7 @@ public class RaceMap extends AbstractCompositeComponent showCompetitorInfoOnMap(timer.getTime(), -1, competitorSelection.getSelectedFilteredCompetitors()); } } - // Trigger auto-zoom if needed + //Trigger auto-zoom if needed RaceMapZoomSettings zoomSettings = settings.getZoomSettings(); if (!zoomSettings.containsZoomType(ZoomTypes.NONE) && zoomSettings.isZoomToSelectedCompetitors()) { zoomMapToNewBounds(zoomSettings.getNewBounds(this)); @@ -2823,7 +2667,7 @@ public class RaceMap extends AbstractCompositeComponent boolean maneuverTypeSelectionChanged = false; boolean requiresRedraw = false; boolean requiresUpdateCoordinateSystem = false; - + for (ManeuverType maneuverType : ManeuverType.values()) { if (newSettings.isShowManeuverType(maneuverType) != settings.isShowManeuverType(maneuverType)) { maneuverTypeSelectionChanged = true; @@ -2837,8 +2681,7 @@ public class RaceMap extends AbstractCompositeComponent } } if (newSettings.isShowDouglasPeuckerPoints() != settings.isShowDouglasPeuckerPoints()) { - if (!(timer.getPlayState() == PlayStates.Playing) && lastDouglasPeuckerResult != null - && newSettings.isShowDouglasPeuckerPoints()) { + if (!(timer.getPlayState() == PlayStates.Playing) && lastDouglasPeuckerResult != null && newSettings.isShowDouglasPeuckerPoints()) { removeAllMarkDouglasPeuckerpoints(); showMarkDouglasPeuckerPoints(lastDouglasPeuckerResult); } else if (!newSettings.isShowDouglasPeuckerPoints()) { @@ -2866,7 +2709,7 @@ public class RaceMap extends AbstractCompositeComponent if (!newSettings.getHelpLinesSettings().equals(settings.getHelpLinesSettings())) { requiresRedraw = true; } - if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null) { + if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null){ estimatedDurationOverlay.removeFromParent(); } if (newSettings.isShowWindStreamletOverlay() != settings.isShowWindStreamletOverlay()) { @@ -2882,7 +2725,7 @@ public class RaceMap extends AbstractCompositeComponent requiresUpdateCoordinateSystem = true; requiresRedraw = true; } - if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null) { + if (!newSettings.isShowEstimatedDuration() && estimatedDurationOverlay != null){ estimatedDurationOverlay.removeFromParent(); } if (newSettings.getStartCountDownFontSizeScaling() != settings.getStartCountDownFontSizeScaling()) { @@ -2893,7 +2736,7 @@ public class RaceMap extends AbstractCompositeComponent requiresRedraw = true; } this.settings = newSettings; - + if (requiresUpdateCoordinateSystem) { updateCoordinateSystemFromSettings(); } @@ -2905,7 +2748,7 @@ public class RaceMap extends AbstractCompositeComponent private void showSimulationOverlay(boolean visible) { simulationOverlay.setVisible(visible); if (visible) { - simulationOverlay.updateLeg(Math.max(lastLegNumber, 1), true, -1 /* ensure ui-update */); + simulationOverlay.updateLeg(Math.max(lastLegNumber,1), true, -1 /* ensure ui-update */); } } @@ -2918,13 +2761,11 @@ public class RaceMap extends AbstractCompositeComponent if (selectedCompetitors == null || !selectedCompetitors.iterator().hasNext()) { competitors = forMap.getCompetitorsToShow(); } else { - competitors = isZoomOnlyToSelectedCompetitors(forMap) ? selectedCompetitors - : forMap.getCompetitorsToShow(); + competitors = isZoomOnlyToSelectedCompetitors(forMap) ? selectedCompetitors : forMap.getCompetitorsToShow(); } for (CompetitorDTO competitor : competitors) { try { - GPSFixDTOWithSpeedWindTackAndLegType competitorFix = forMap.getBoatFix(competitor, - forMap.timer.getTime()); + GPSFixDTOWithSpeedWindTackAndLegType competitorFix = forMap.getBoatFix(competitor, forMap.timer.getTime()); Position competitorPosition = competitorFix != null ? competitorFix.position : null; if (competitorPosition != null) { if (newBounds == null) { @@ -2935,30 +2776,25 @@ public class RaceMap extends AbstractCompositeComponent } } catch (IndexOutOfBoundsException e) { // TODO can't this be predicted and the exception be avoided in the first place? - // Catch this in case the competitor has no GPS fixes at the current time (e.g. in race 'Finale 2' - // of STG) + // Catch this in case the competitor has no GPS fixes at the current time (e.g. in race 'Finale 2' of STG) } } return newBounds; } - + } - + public static class TailsBoundsCalculator extends LatLngBoundsCalculatorForSelected { @Override public LatLngBounds calculateNewBounds(RaceMap racemap) { LatLngBounds newBounds = null; - Iterable competitors = isZoomOnlyToSelectedCompetitors(racemap) - ? racemap.competitorSelection.getSelectedCompetitors() - : racemap.getCompetitorsToShow(); + Iterable competitors = isZoomOnlyToSelectedCompetitors(racemap) ? racemap.competitorSelection.getSelectedCompetitors() : racemap.getCompetitorsToShow(); for (CompetitorDTO competitor : competitors) { Polyline tail = racemap.fixesAndTails.getTail(competitor); LatLngBounds bounds = null; // TODO: Find a replacement for missing Polyline function getBounds() from v2 - // see also - // http://stackoverflow.com/questions/3284808/getting-the-bounds-of-a-polyine-in-google-maps-api-v3; - // optionally, consider providing a bounds cache with two sorted sets that organize the LatLng objects - // for O(1) bounds calculation and logarithmic add, ideally O(1) remove + // see also http://stackoverflow.com/questions/3284808/getting-the-bounds-of-a-polyine-in-google-maps-api-v3; + // optionally, consider providing a bounds cache with two sorted sets that organize the LatLng objects for O(1) bounds calculation and logarithmic add, ideally O(1) remove if (tail != null && tail.getPath().getLength() >= 1) { bounds = BoundsUtil.getAsBounds(tail.getPath().get(0)); for (int i = 1; i < tail.getPath().getLength(); i++) { @@ -2976,7 +2812,7 @@ public class RaceMap extends AbstractCompositeComponent return newBounds; } } - + public static class CourseMarksBoundsCalculator implements LatLngBoundsCalculator { @Override public LatLngBounds calculateNewBounds(RaceMap forMap) { @@ -3033,17 +2869,17 @@ public class RaceMap extends AbstractCompositeComponent map.triggerResize(); zoomMapToNewBounds(settings.getZoomSettings().getNewBounds(RaceMap.this)); } - // Adjust RaceMap headers to avoid overlapping based on the RaceMap width + // Adjust RaceMap headers to avoid overlapping based on the RaceMap width boolean isCompactHeader = this.getOffsetWidth() <= 600; getLeftHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); getRightHeaderPanel().setStyleName(COMPACT_HEADER_STYLE, isCompactHeader); - + // Adjust combined wind and true north indicator panel indent, based on the RaceMap height if (topLeftControlsWrapperPanel.getParent() != null) { this.adjustLeftControlsIndent(); } } - + private void adjustLeftControlsIndent() { topLeftControlsWrapperPanel.getParent().setStyleName("TopLeftControlsWrapperPanelParentDiv"); String leftControlsIndentStyle = getLeftControlsIndentStyle(); @@ -3056,17 +2892,16 @@ public class RaceMap extends AbstractCompositeComponent public void competitorsListChanged(Iterable competitors) { timeChanged(timer.getTime(), null); } - + @Override public void filteredCompetitorsListChanged(Iterable filteredCompetitors) { timeChanged(timer.getTime(), null); } - + @Override public void filterChanged(FilterSet> oldFilterSet, FilterSet> newFilterSet) { - // nothing to do; if the list of filtered competitors has changed, a separate call to - // filteredCompetitorsListChanged will occur + // nothing to do; if the list of filtered competitors has changed, a separate call to filteredCompetitorsListChanged will occur } @Override @@ -3076,7 +2911,7 @@ public class RaceMap extends AbstractCompositeComponent options.setGeodesic(true); options.setStrokeOpacity(1.0); - switch (displayMode) { + switch(displayMode){ case DEFAULT: options.setStrokeColor(competitorSelection.getColor(competitor, raceIdentifier).getAsHtml()); options.setStrokeWeight(1); @@ -3094,7 +2929,7 @@ public class RaceMap extends AbstractCompositeComponent options.setZindex(RaceMapOverlaysZIndexes.BOATTAILS_ZINDEX); return options; } - + @Override public Polyline createTail(final CompetitorDTO competitor, List points) { final BoatDTO boat = competitorSelection.getBoat(competitor); @@ -3138,7 +2973,7 @@ public class RaceMap extends AbstractCompositeComponent } return result; } - + private Image createSAPLogo() { ImageResource sapLogoResource = resources.sapLogoOverlay(); Image sapLogo = new Image(sapLogoResource); @@ -3166,42 +3001,42 @@ public class RaceMap extends AbstractCompositeComponent private MapOptions getMapOptions(final boolean showMapControls, boolean windUp) { MapOptions mapOptions = MapOptions.newInstance(); - mapOptions.setScrollWheel(true); - mapOptions.setMapTypeControl(showMapControls && !windUp); - mapOptions.setPanControl(showMapControls); - mapOptions.setZoomControl(showMapControls); - mapOptions.setScaleControl(true); - if (windUp) { - mapOptions.setMinZoom(8); - } else { - mapOptions.setMinZoom(0); - } - MapTypeStyle[] mapTypeStyles = new MapTypeStyle[4]; + mapOptions.setScrollWheel(true); + mapOptions.setMapTypeControl(showMapControls && !windUp); + mapOptions.setPanControl(showMapControls); + mapOptions.setZoomControl(showMapControls); + mapOptions.setScaleControl(true); + if (windUp) { + mapOptions.setMinZoom(8); + } else { + mapOptions.setMinZoom(0); + } + MapTypeStyle[] mapTypeStyles = new MapTypeStyle[4]; + + // hide all transit lines including ferry lines + mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT); + // hide points of interest + mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI); + // simplify road display + mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD); + // set water color + mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, WATER_COLOR); + + MapTypeControlOptions mapTypeControlOptions = MapTypeControlOptions.newInstance(); + mapTypeControlOptions.setPosition(ControlPosition.BOTTOM_RIGHT); + mapOptions.setMapTypeControlOptions(mapTypeControlOptions); - // hide all transit lines including ferry lines - mapTypeStyles[0] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.TRANSIT); - // hide points of interest - mapTypeStyles[1] = GoogleMapStyleHelper.createHiddenStyle(MapTypeStyleFeatureType.POI); - // simplify road display - mapTypeStyles[2] = GoogleMapStyleHelper.createSimplifiedStyle(MapTypeStyleFeatureType.ROAD); - // set water color - mapTypeStyles[3] = GoogleMapStyleHelper.createColorStyle(MapTypeStyleFeatureType.WATER, WATER_COLOR); - - MapTypeControlOptions mapTypeControlOptions = MapTypeControlOptions.newInstance(); - mapTypeControlOptions.setPosition(ControlPosition.BOTTOM_RIGHT); - mapOptions.setMapTypeControlOptions(mapTypeControlOptions); - - mapOptions.setMapTypeStyles(mapTypeStyles); - // no need to try to position the scale control; it always ends up at the right bottom corner - mapOptions.setStreetViewControl(false); - if (showMapControls) { - ZoomControlOptions zoomControlOptions = ZoomControlOptions.newInstance(); - zoomControlOptions.setPosition(ControlPosition.RIGHT_TOP); - mapOptions.setZoomControlOptions(zoomControlOptions); - PanControlOptions panControlOptions = PanControlOptions.newInstance(); - panControlOptions.setPosition(ControlPosition.RIGHT_TOP); - mapOptions.setPanControlOptions(panControlOptions); - } + mapOptions.setMapTypeStyles(mapTypeStyles); + // no need to try to position the scale control; it always ends up at the right bottom corner + mapOptions.setStreetViewControl(false); + if (showMapControls) { + ZoomControlOptions zoomControlOptions = ZoomControlOptions.newInstance(); + zoomControlOptions.setPosition(ControlPosition.RIGHT_TOP); + mapOptions.setZoomControlOptions(zoomControlOptions); + PanControlOptions panControlOptions = PanControlOptions.newInstance(); + panControlOptions.setPosition(ControlPosition.RIGHT_TOP); + mapOptions.setPanControlOptions(panControlOptions); + } return mapOptions; } @@ -3215,11 +3050,11 @@ public class RaceMap extends AbstractCompositeComponent public Map getCourseMarkOverlays() { return courseMarkOverlays; } - + public CoordinateSystem getCoordinateSystem() { return coordinateSystem; } - + public void hideAllHelplines() { if (startLine != null) { startLine.setVisible(false); @@ -3245,7 +3080,7 @@ public class RaceMap extends AbstractCompositeComponent } } } - + public void showAllHelplinesToShow() { if (settings.getHelpLinesSettings().isShowAnyHelperLines()) { if (startLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINE)) @@ -3254,8 +3089,8 @@ public class RaceMap extends AbstractCompositeComponent finishLine.setVisible(true); if (advantageLine != null && settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) advantageLine.setVisible(true); - if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null - && settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) { + if (windwardStartLineMarkToFirstMarkLine != null && leewardStartLineMarkToFirstMarkLine != null && + settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) { windwardStartLineMarkToFirstMarkLine.setVisible(true); leewardStartLineMarkToFirstMarkLine.setVisible(true); } @@ -3271,7 +3106,7 @@ public class RaceMap extends AbstractCompositeComponent } } } - + public void addCompetitorsForRaceDefinedListener(CompetitorsForRaceDefinedListener listener) { raceCompetitorSet.addCompetitorsForRaceDefinedListener(listener); } @@ -3284,16 +3119,15 @@ public class RaceMap extends AbstractCompositeComponent public String getId() { return raceMapLifecycle.getComponentId(); } - + public RaceMapLifecycle getLifecycle() { return raceMapLifecycle; } - final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.middleManeuverAngle(); - } + @Override + public String getLineInfo() { + return stringMessages.middleManeuverAngle(); + } }; final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { @Override @@ -3352,8 +3186,7 @@ public class RaceMap extends AbstractCompositeComponent } } maneuverLossLinesMap.clear(); - for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap - .entrySet()) { + for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap.entrySet()) { e.getValue().removeFromMap(); } maneuverLossInfoOverlayMap.clear(); @@ -3368,20 +3201,18 @@ public class RaceMap extends AbstractCompositeComponent final Set maneuverLossLines = new HashSet<>(); Bearing bearingBefore = maneuver.getManeuverLoss().getSpeedWithBearingBefore().getBearing(); Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.getManeuverLoss().getMiddleManeuverAngle()); - Distance extrapolationOfManeuverStartPoint = maneuver.getManeuverLoss().getSpeedWithBearingBefore() - .travel(maneuver.getManeuverLoss().getManeuverDuration()); + Distance extrapolationOfManeuverStartPoint = + maneuver.getManeuverLoss().getSpeedWithBearingBefore().travel(maneuver.getManeuverLoss().getManeuverDuration()); Position extrapolatedManeuverStartPosition = maneuver.getManeuverLoss().getManeuverStartPosition() .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); - Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.getManeuverLoss() - .getManeuverStartPosition().getIntersection(bearingBefore, maneuver.getPosition(), middleManeuverAngle); + Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.getManeuverLoss().getManeuverStartPosition() + .getIntersection(bearingBefore, maneuver.getPosition(), middleManeuverAngle); Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition() - .projectToLineThrough(intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, - middleManeuverAngle); + Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); String color = maneuver.getManeuverLoss().getDistanceLost().compareTo(Distance.NULL) > 0 - ? color = MANEUVERLOSSLINES_RED - : MANEUVERLOSSLINES_GREEN; + ? color = MANEUVERLOSSLINES_RED : MANEUVERLOSSLINES_GREEN; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, 1, 0.5)); @@ -3392,7 +3223,7 @@ public class RaceMap extends AbstractCompositeComponent projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, 1, 0.3)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverEndPosition(), - projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, 1, 0.3)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 2, 1.0)); @@ -3400,8 +3231,7 @@ public class RaceMap extends AbstractCompositeComponent maneuver.getType()); maneuverLossLinesMap.put(t, maneuverLossLines); StringBuilder sb = new StringBuilder(); - sb.append(stringMessages.maneuverLoss() + ": " - + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + stringMessages.metersUnit() + '\n'); if (maneuver.getType() == ManeuverType.TACK) { sb.append(stringMessages.tackAngle() + ": "); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java index 8d65bd36dc5..9eb8a6ab5f5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java @@ -53,7 +53,7 @@ public class RaceMapLifecycle implements ComponentLifecycle { settings.isShowOnlySelectedCompetitors(), settings.isShowSelectedCompetitorsInfo(), settings.isShowWindStreamletColors(), settings.isShowWindStreamletOverlay(), settings.isShowSimulationOverlay(), settings.isShowMapControls(), settings.getManeuverTypesToShow(), - settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling(), settings.isShowManeuverLossVisualization()); + settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling()); } @Override @@ -65,6 +65,6 @@ public class RaceMapLifecycle implements ComponentLifecycle { settings.isShowOnlySelectedCompetitors(), settings.isShowSelectedCompetitorsInfo(), settings.isShowWindStreamletColors(), settings.isShowWindStreamletOverlay(), settings.isShowSimulationOverlay(), settings.isShowMapControls(), settings.getManeuverTypesToShow(), - settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling(), settings.isShowManeuverLossVisualization()); + settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling()); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java index 95549f248ba..ecf6a13ce1e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java @@ -60,8 +60,6 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { private BooleanSetting showMapControls; - private BooleanSetting showManeuverLossVisualization; - /** * If true, all map contents will be transformed to a water-only environment, rotating all directions / * bearings / headings so that an assumed average wind direction for the race is coming from the top of the map @@ -99,7 +97,6 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { showDouglasPeuckerPoints = new BooleanSetting("showDouglasPeuckerPoints", this, false); showEstimatedDuration = new BooleanSetting("showEstimatedDuration", this, false); startCountDownFontSizeScaling = new DoubleSetting("startCountDownFontSizeScaling", this, 1.0); - showManeuverLossVisualization = new BooleanSetting("showManeuverLossVisualization", this, true); } public RaceMapSettings() { @@ -110,7 +107,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { Distance buoyZoneRadius, Boolean showOnlySelectedCompetitors, Boolean showSelectedCompetitorsInfo, Boolean showWindStreamletColors, Boolean showWindStreamletOverlay, Boolean showSimulationOverlay, Boolean showMapControls, Collection maneuverTypesToShow, Boolean showDouglasPeuckerPoints, - Boolean showEstimatedDuration, Double startCountDownFontSizeScaling, Boolean showManeuverLossVisualization) { + Boolean showEstimatedDuration, Double startCountDownFontSizeScaling) { this.zoomSettings.init(zoomSettings); this.helpLinesSettings.init(helpLinesSettings); this.transparentHoverlines.setValue(transparentHoverlines); @@ -128,12 +125,11 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { this.showDouglasPeuckerPoints.setValue(showDouglasPeuckerPoints); this.showEstimatedDuration.setValue(showEstimatedDuration); this.startCountDownFontSizeScaling.setValue(startCountDownFontSizeScaling); - this.showManeuverLossVisualization.setValue(showManeuverLossVisualization); } - public static RaceMapSettings getDefaultWithShowMapControls(boolean showMapControls) { + public static RaceMapSettings getDefaultWithShowMapControls(boolean showMapControlls) { RaceMapSettings raceMapSetting = new RaceMapSettings(); - raceMapSetting.showMapControls.setValue(showMapControls); + raceMapSetting.showMapControls.setValue(showMapControlls); return raceMapSetting; } @@ -159,9 +155,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { /* maneuverTypesToShow */ getDefaultManeuvers(), /* showDouglasPeuckerPoints */ false, /* showEstimatedDuration*/ false, - /* startCountDownFontSizeScaling */ 1.0, - /* showManeuverLossVisualization */ true); - + /* startCountDownFontSizeScaling */ 1.0); } private static Set createHelpLineSettings(boolean showCourseGeometry) { @@ -203,8 +197,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { /* maneuverTypesToShow */ settings.getManeuverTypesToShow(), /* showDouglasPeuckerPoints */ settings.isShowDouglasPeuckerPoints(), /* showEstimatedDuration */ settings.isShowEstimatedDuration(), - /* startCountDownFontSizeScaling */ settings.getStartCountDownFontSizeScaling(), - /* showManeuverLossVisualization */ settings.isShowManeuverLossVisualization()); + /* startCountDownFontSizeScaling */ settings.getStartCountDownFontSizeScaling()); } /** @@ -278,10 +271,6 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { return startCountDownFontSizeScaling.getValue(); } - public boolean isShowManeuverLossVisualization() { - return showManeuverLossVisualization.getValue(); - } - public static RaceMapSettings createSettingsWithNewBuoyZoneRadius(RaceMapSettings settings, Distance newDefaultBuoyZoneRadius) { final RaceMapSettings newRaceMapSettings = new RaceMapSettings( settings.getZoomSettings(), settings.getHelpLinesSettings(), @@ -295,8 +284,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { settings.isShowSimulationOverlay(), settings.isShowMapControls(), settings.getManeuverTypesToShow(), settings.isShowDouglasPeuckerPoints(),settings.isShowEstimatedDuration(), - settings.getStartCountDownFontSizeScaling(), - settings.isShowManeuverLossVisualization()); + settings.getStartCountDownFontSizeScaling()); return newRaceMapSettings; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java index 674b36b6b9a..0ee72988649 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java @@ -63,8 +63,6 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent disableOnlySelectedWhenAreFalse; private CheckBox showEstimatedDuration; - private CheckBox showManeuverLossVisualizationCheckBox; - public RaceMapSettingsDialogComponent(RaceMapSettings settings, StringMessages stringMessages, boolean isSimulationEnabled) { this.isSimulationEnabled = isSimulationEnabled; this.stringMessages = stringMessages; @@ -163,7 +161,7 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java index bfd1ccd3c8c..9ab82b6f8f0 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java @@ -81,8 +81,7 @@ public class StartAnalysisMode extends RaceBoardModeWithPerRaceCompetitors { defaultSettings.getManeuverTypesToShow(), defaultSettings.isShowDouglasPeuckerPoints(), defaultSettings.isShowEstimatedDuration(), - defaultSettings.getStartCountDownFontSizeScaling(), - defaultSettings.isShowManeuverLossVisualization()); + defaultSettings.getStartCountDownFontSizeScaling()); ((RaceBoardComponentContext) raceMap.getComponentContext()).addModesPatching(raceMap, additiveSettings, new OnSettingsReloadedCallback() { @Override public void onSettingsReloaded(RaceMapSettings patchedSettings) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java index e8621d61e5d..bfb6d5ff1a6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java @@ -77,8 +77,7 @@ public class WinningLanesMode extends RaceBoardModeWithPerRaceCompetitors { defaultSettings.getManeuverTypesToShow(), defaultSettings.isShowDouglasPeuckerPoints(), defaultSettings.isShowEstimatedDuration(), - defaultSettings.getStartCountDownFontSizeScaling(), - defaultSettings.isShowManeuverLossVisualization()); + defaultSettings.getStartCountDownFontSizeScaling()); ((RaceBoardComponentContext) raceMap.getComponentContext()).addModesPatching(raceMap, additiveSettings, new OnSettingsReloadedCallback() { From 3c8a8a2246e1a41e759673707bdcbe94c03b5115 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 09:57:47 +0200 Subject: [PATCH 36/53] bug4572: Reverted formatting issues on RaceMap. Added constants for different line options. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 43 ++++++++++++------- 1 file changed, 28 insertions(+), 15 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 2b3617a5968..ac292e31e16 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -166,17 +166,24 @@ import com.sap.sse.gwt.client.shared.settings.ComponentContext; public class RaceMap extends AbstractCompositeComponent implements TimeListener, CompetitorSelectionChangeListener, RaceTimesInfoProviderListener, TailFactory, RequiresDataInitialization, RequiresResize, QuickRankProvider { + /* Line colors */ private static final String ADVANTAGE_LINE_COLOR = "#ff9900"; // orange + private static final String START_LINE_COLOR = "#ffffff"; private static final String FINISH_LINE_COLOR = "#000000"; private static final Color LOWLIGHTED_TAIL_COLOR = new RGBColor(200, 200, 200); private static final String MANEUVERLOSSLINES_PROJECTEDLINES_COLOR = "#ffff00"; private static final String MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR = "#ffffff"; private static final String MANEUVERLOSSLINES_RED = "#ff0000"; private static final String MANEUVERLOSSLINES_GREEN = "#00ff00"; + /* Line opacities and stroke weights */ + private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; + private static final double STANDARD_LINE_OPACITY = 1.0; + private static final double LOWlIGHTED_LINE_OPACITY = 0.5; + private static final int STANDARD_LINE_STROKEWEIGHT = 1; + private static final int HIGHLIGHTED_LINE_STROKEWEIGHT = 2; + public static final String GET_RACE_MAP_DATA_CATEGORY = "getRaceMapData"; public static final String GET_WIND_DATA_CATEGORY = "getWindData"; - private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; - private static final String COMPACT_HEADER_STYLE = "compactHeader"; public static final Color WATER_COLOR = new RGBColor(0, 67, 125); @@ -1645,12 +1652,12 @@ public class RaceMap extends AbstractCompositeComponent impleme (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, windwardStartLinePosition, firstMarkPosition, windwardStartLineMarkToFirstMarkLineInfoProvider, - "grey", 1, 1.0); + "grey", STANDARD_LINE_STROKEWEIGHT, STANDARD_LINE_OPACITY); leewardStartLineMarkToFirstMarkLine = showOrRemoveOrUpdateLine(leewardStartLineMarkToFirstMarkLine, /* showLine */ (settings.getHelpLinesSettings().isVisible(HelpLineTypes.STARTLINETOFIRSTMARKTRIANGLE)) && startMarkPositions.size() > 1 && courseDTO.waypointPositions.size() > 1, leewardStartLinePosition, firstMarkPosition, leewardStartLineMarkToFirstMarkLineInfoProvider, - "grey", 1, 1.0); + "grey", STANDARD_LINE_STROKEWEIGHT, STANDARD_LINE_OPACITY); } } @@ -1707,8 +1714,9 @@ public class RaceMap extends AbstractCompositeComponent impleme (settings.getHelpLinesSettings().isVisible(HelpLineTypes.FINISHLINE) && showFinishLineBasedOnCurrentLeg) || (settings.getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY) && (!startLineEqualsFinishLine(courseDTO) || showFinishLineBasedOnCurrentLeg)); - startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, - startLineLeftPosition, startLineRightPosition, startLineInfoProvider, "#ffffff", 1, 1.0); + startLine = showOrRemoveOrUpdateLine(startLine, reallyShowStartLine, startLineLeftPosition, + startLineRightPosition, startLineInfoProvider, START_LINE_COLOR, STANDARD_LINE_STROKEWEIGHT, + STANDARD_LINE_OPACITY); // draw the finish line final Position finishLineLeftPosition = numberOfFinishWaypointMarks == 0 ? null : courseDTO.getFinishMarkPositions().get(0); final Position finishLineRightPosition = numberOfFinishWaypointMarks < 2 ? null : courseDTO.getFinishMarkPositions().get(1); @@ -1721,8 +1729,9 @@ public class RaceMap extends AbstractCompositeComponent impleme } else { finishLineAdvantageText.delete(0, finishLineAdvantageText.length()); } - finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, - finishLineLeftPosition, finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR, 1, 1.0); + finishLine = showOrRemoveOrUpdateLine(finishLine, reallyShowFinishLine, finishLineLeftPosition, + finishLineRightPosition, finishLineInfoProvider, FINISH_LINE_COLOR, STANDARD_LINE_STROKEWEIGHT, + STANDARD_LINE_STROKEWEIGHT); // the control point pairs for which we already decided whether or not // to show a course middle line for; values tell whether to show the line and for which zero-based // start waypoint index to do so; when for an equal control point pair multiple decisions with different @@ -1807,7 +1816,8 @@ public class RaceMap extends AbstractCompositeComponent impleme return sb.toString(); } }; - return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, lineInfoProvider, "#2268a0", 1, 1.0); + return showOrRemoveOrUpdateLine(lineToShowOrRemoveOrUpdate, showLine, position1DTO, position2DTO, + lineInfoProvider, "#2268a0", STANDARD_LINE_STROKEWEIGHT, STANDARD_LINE_OPACITY); } private interface LineInfoProvider { @@ -3215,18 +3225,21 @@ public class RaceMap extends AbstractCompositeComponent impleme ? color = MANEUVERLOSSLINES_RED : MANEUVERLOSSLINES_GREEN; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, - middleManeuverAngleLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, 1, 0.5)); + middleManeuverAngleLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, + STANDARD_LINE_STROKEWEIGHT, LOWlIGHTED_LINE_OPACITY)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverStartPosition(), extrapolatedManeuverStartPosition, - extrapolatedLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, 1, 0.5)); + extrapolatedLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, + LOWlIGHTED_LINE_OPACITY)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, - MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, 1, 0.3)); + MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_TAIL_OPACITY)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverEndPosition(), - projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, - MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, 1, 0.3)); + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, + MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_TAIL_OPACITY)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, - projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, 2, 1.0)); + projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, HIGHLIGHTED_LINE_STROKEWEIGHT, + STANDARD_LINE_OPACITY)); Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); maneuverLossLinesMap.put(t, maneuverLossLines); From 57a7393febe252809f10759342f73727e1226061 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 10:17:31 +0200 Subject: [PATCH 37/53] bug4572: introduced showManeuverLossVisualization in RaceMapSettings. --- .../startanalysis/card/StartAnalysisCard.java | 2 +- .../autoplay/client/utils/AutoplayHelper.java | 2 +- .../shared/racemap/CombinedWindPanel.java | 2 +- .../shared/racemap/RaceMapLifecycle.java | 6 ++++-- .../shared/racemap/RaceMapSettings.java | 19 +++++++++++++++---- .../RaceMapSettingsDialogComponent.java | 7 ++++++- .../racemap/TrueNorthIndicatorPanel.java | 2 +- .../EmbeddedMapAndWindChartEntryPoint.java | 3 ++- .../gwt/ui/raceboard/StartAnalysisMode.java | 3 ++- .../gwt/ui/raceboard/WinningLanesMode.java | 3 ++- 10 files changed, 35 insertions(+), 14 deletions(-) diff --git a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java index 66858859563..497dc402680 100644 --- a/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java +++ b/java/com.sap.sailing.dashboards.gwt/src/main/java/com/sap/sailing/dashboards/gwt/client/widgets/startanalysis/card/StartAnalysisCard.java @@ -163,7 +163,7 @@ public class StartAnalysisCard extends Composite implements HasWidgets, StartAna defaultRaceMapSettings.isShowWindStreamletOverlay(), defaultRaceMapSettings.isShowSimulationOverlay(), defaultRaceMapSettings.isShowMapControls(), defaultRaceMapSettings.getManeuverTypesToShow(), defaultRaceMapSettings.isShowDouglasPeuckerPoints(), defaultRaceMapSettings.isShowEstimatedDuration(), - defaultRaceMapSettings.getStartCountDownFontSizeScaling()); + defaultRaceMapSettings.getStartCountDownFontSizeScaling(), defaultRaceMapSettings.isShowManeuverLossVisualization()); RaceTimesInfoProvider raceTimesInfoProvider = new RaceTimesInfoProvider(sailingServiceAsync, diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java index bd37fc96661..b03b79dc71f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/autoplay/client/utils/AutoplayHelper.java @@ -284,7 +284,7 @@ public class AutoplayHelper { RaceMapZoomSettings autoFollowRace = new RaceMapZoomSettings(typesToConsiderOnZoom, true); RaceMapSettings settings = new RaceMapSettings(autoFollowRace, new RaceMapHelpLinesSettings(), false, 15, 100000l, false, RaceMapSettings.DEFAULT_BUOY_ZONE_RADIUS, false, true, false, false, false, false, - RaceMapSettings.getDefaultManeuvers(), false, false, /* startCountDownFontSizeScaling */ 1.5); + RaceMapSettings.getDefaultManeuvers(), false, false, /* startCountDownFontSizeScaling */ 1.5, /* showManeuverLossVisualization */ false); RaceMapLifecycle raceMapLifecycle = new RaceMapLifecycle(StringMessages.INSTANCE); final CompetitorColorProvider colorProvider = new CompetitorColorProviderImpl(currentLiveRace, competitorsAndTheirBoats); RaceCompetitorSelectionModel competitorSelectionProvider = new RaceCompetitorSelectionModel( diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java index 9d91e40a447..50cbefb7269 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CombinedWindPanel.java @@ -54,7 +54,7 @@ public class CombinedWindPanel extends FlowPanel { newShowStreamletsOverlaySetting, oldRaceMapSettings.isShowSimulationOverlay(), oldRaceMapSettings.isShowMapControls(), oldRaceMapSettings.getManeuverTypesToShow(), oldRaceMapSettings.isShowDouglasPeuckerPoints(), oldRaceMapSettings.isShowEstimatedDuration(), - oldRaceMapSettings.getStartCountDownFontSizeScaling()); + oldRaceMapSettings.getStartCountDownFontSizeScaling(), oldRaceMapSettings.isShowManeuverLossVisualization()); map.updateSettings(newRaceMapSettings); } }); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java index 9eb8a6ab5f5..b5aa864dc75 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapLifecycle.java @@ -53,7 +53,8 @@ public class RaceMapLifecycle implements ComponentLifecycle { settings.isShowOnlySelectedCompetitors(), settings.isShowSelectedCompetitorsInfo(), settings.isShowWindStreamletColors(), settings.isShowWindStreamletOverlay(), settings.isShowSimulationOverlay(), settings.isShowMapControls(), settings.getManeuverTypesToShow(), - settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling()); + settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), + settings.getStartCountDownFontSizeScaling(), settings.isShowManeuverLossVisualization()); } @Override @@ -65,6 +66,7 @@ public class RaceMapLifecycle implements ComponentLifecycle { settings.isShowOnlySelectedCompetitors(), settings.isShowSelectedCompetitorsInfo(), settings.isShowWindStreamletColors(), settings.isShowWindStreamletOverlay(), settings.isShowSimulationOverlay(), settings.isShowMapControls(), settings.getManeuverTypesToShow(), - settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), settings.getStartCountDownFontSizeScaling()); + settings.isShowDouglasPeuckerPoints(), settings.isShowEstimatedDuration(), + settings.getStartCountDownFontSizeScaling(), settings.isShowManeuverLossVisualization()); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java index ecf6a13ce1e..287603353ae 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java @@ -60,6 +60,8 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { private BooleanSetting showMapControls; + private BooleanSetting showManeuverLossVisualization; + /** * If true, all map contents will be transformed to a water-only environment, rotating all directions / * bearings / headings so that an assumed average wind direction for the race is coming from the top of the map @@ -97,6 +99,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { showDouglasPeuckerPoints = new BooleanSetting("showDouglasPeuckerPoints", this, false); showEstimatedDuration = new BooleanSetting("showEstimatedDuration", this, false); startCountDownFontSizeScaling = new DoubleSetting("startCountDownFontSizeScaling", this, 1.0); + showManeuverLossVisualization = new BooleanSetting("showManeuverLossVisualization", this, true); } public RaceMapSettings() { @@ -107,7 +110,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { Distance buoyZoneRadius, Boolean showOnlySelectedCompetitors, Boolean showSelectedCompetitorsInfo, Boolean showWindStreamletColors, Boolean showWindStreamletOverlay, Boolean showSimulationOverlay, Boolean showMapControls, Collection maneuverTypesToShow, Boolean showDouglasPeuckerPoints, - Boolean showEstimatedDuration, Double startCountDownFontSizeScaling) { + Boolean showEstimatedDuration, Double startCountDownFontSizeScaling, Boolean showManeuverLossVisualization) { this.zoomSettings.init(zoomSettings); this.helpLinesSettings.init(helpLinesSettings); this.transparentHoverlines.setValue(transparentHoverlines); @@ -125,6 +128,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { this.showDouglasPeuckerPoints.setValue(showDouglasPeuckerPoints); this.showEstimatedDuration.setValue(showEstimatedDuration); this.startCountDownFontSizeScaling.setValue(startCountDownFontSizeScaling); + this.showManeuverLossVisualization.setValue(showManeuverLossVisualization); } public static RaceMapSettings getDefaultWithShowMapControls(boolean showMapControlls) { @@ -155,7 +159,8 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { /* maneuverTypesToShow */ getDefaultManeuvers(), /* showDouglasPeuckerPoints */ false, /* showEstimatedDuration*/ false, - /* startCountDownFontSizeScaling */ 1.0); + /* startCountDownFontSizeScaling */ 1.0, + /* showManeuverLossVisualization */ true); } private static Set createHelpLineSettings(boolean showCourseGeometry) { @@ -197,7 +202,8 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { /* maneuverTypesToShow */ settings.getManeuverTypesToShow(), /* showDouglasPeuckerPoints */ settings.isShowDouglasPeuckerPoints(), /* showEstimatedDuration */ settings.isShowEstimatedDuration(), - /* startCountDownFontSizeScaling */ settings.getStartCountDownFontSizeScaling()); + /* startCountDownFontSizeScaling */ settings.getStartCountDownFontSizeScaling(), + /* showManeuverLossLineVisualization */ settings.isShowManeuverLossVisualization()); } /** @@ -271,6 +277,10 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { return startCountDownFontSizeScaling.getValue(); } + public boolean isShowManeuverLossVisualization() { + return showManeuverLossVisualization.getValue(); + } + public static RaceMapSettings createSettingsWithNewBuoyZoneRadius(RaceMapSettings settings, Distance newDefaultBuoyZoneRadius) { final RaceMapSettings newRaceMapSettings = new RaceMapSettings( settings.getZoomSettings(), settings.getHelpLinesSettings(), @@ -284,7 +294,8 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { settings.isShowSimulationOverlay(), settings.isShowMapControls(), settings.getManeuverTypesToShow(), settings.isShowDouglasPeuckerPoints(),settings.isShowEstimatedDuration(), - settings.getStartCountDownFontSizeScaling()); + settings.getStartCountDownFontSizeScaling(), + settings.isShowManeuverLossVisualization()); return newRaceMapSettings; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java index 0ee72988649..1e376565dc2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettingsDialogComponent.java @@ -54,6 +54,7 @@ public class RaceMapSettingsDialogComponent implements SettingsDialogComponent() { @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java index 9ab82b6f8f0..bfd1ccd3c8c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/StartAnalysisMode.java @@ -81,7 +81,8 @@ public class StartAnalysisMode extends RaceBoardModeWithPerRaceCompetitors { defaultSettings.getManeuverTypesToShow(), defaultSettings.isShowDouglasPeuckerPoints(), defaultSettings.isShowEstimatedDuration(), - defaultSettings.getStartCountDownFontSizeScaling()); + defaultSettings.getStartCountDownFontSizeScaling(), + defaultSettings.isShowManeuverLossVisualization()); ((RaceBoardComponentContext) raceMap.getComponentContext()).addModesPatching(raceMap, additiveSettings, new OnSettingsReloadedCallback() { @Override public void onSettingsReloaded(RaceMapSettings patchedSettings) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java index bfb6d5ff1a6..e8621d61e5d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/WinningLanesMode.java @@ -77,7 +77,8 @@ public class WinningLanesMode extends RaceBoardModeWithPerRaceCompetitors { defaultSettings.getManeuverTypesToShow(), defaultSettings.isShowDouglasPeuckerPoints(), defaultSettings.isShowEstimatedDuration(), - defaultSettings.getStartCountDownFontSizeScaling()); + defaultSettings.getStartCountDownFontSizeScaling(), + defaultSettings.isShowManeuverLossVisualization()); ((RaceBoardComponentContext) raceMap.getComponentContext()).addModesPatching(raceMap, additiveSettings, new OnSettingsReloadedCallback() { From 38a7c6d65e82470d726018445b685f3227346689 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 10:37:30 +0200 Subject: [PATCH 38/53] bug4572: added the view logic for the isShowManeuverLoss setting on RaceMap --- .../com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index ac292e31e16..09b19acfe08 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2236,6 +2236,10 @@ public class RaceMap extends AbstractCompositeComponent impleme numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + if (settings.isShowManeuverLossVisualization() && !maneuverLossCheckBoxValueStore.contains(t)) { + maneuverLossCheckBoxValueStore.add(t); + visualizeManeuverLoss(maneuver, true, competitor); + } maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.contains(t)); maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { @Override From 9d9698ae5b1a9ac1ed2db3ab00fb0e6c067f7ad0 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 14:56:21 +0200 Subject: [PATCH 39/53] bug4572: made showManeuverLossVisualization default false in RaceMapSettings. --- .../sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java | 6 +++--- .../gwt/ui/client/shared/racemap/RaceMapSettings.java | 4 ++-- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 09b19acfe08..2e7724d0473 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -178,7 +178,7 @@ public class RaceMap extends AbstractCompositeComponent impleme /* Line opacities and stroke weights */ private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; private static final double STANDARD_LINE_OPACITY = 1.0; - private static final double LOWlIGHTED_LINE_OPACITY = 0.5; + private static final double LOWLIGHTED_LINE_OPACITY = 0.5; private static final int STANDARD_LINE_STROKEWEIGHT = 1; private static final int HIGHLIGHTED_LINE_STROKEWEIGHT = 2; @@ -3230,11 +3230,11 @@ public class RaceMap extends AbstractCompositeComponent impleme maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, middleManeuverAngleLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, - STANDARD_LINE_STROKEWEIGHT, LOWlIGHTED_LINE_OPACITY)); + STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_LINE_OPACITY)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverStartPosition(), extrapolatedManeuverStartPosition, extrapolatedLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, - LOWlIGHTED_LINE_OPACITY)); + LOWLIGHTED_LINE_OPACITY)); maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_TAIL_OPACITY)); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java index 287603353ae..640af2904ad 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapSettings.java @@ -99,7 +99,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { showDouglasPeuckerPoints = new BooleanSetting("showDouglasPeuckerPoints", this, false); showEstimatedDuration = new BooleanSetting("showEstimatedDuration", this, false); startCountDownFontSizeScaling = new DoubleSetting("startCountDownFontSizeScaling", this, 1.0); - showManeuverLossVisualization = new BooleanSetting("showManeuverLossVisualization", this, true); + showManeuverLossVisualization = new BooleanSetting("showManeuverLossVisualization", this, false); } public RaceMapSettings() { @@ -160,7 +160,7 @@ public class RaceMapSettings extends AbstractGenericSerializableSettings { /* showDouglasPeuckerPoints */ false, /* showEstimatedDuration*/ false, /* startCountDownFontSizeScaling */ 1.0, - /* showManeuverLossVisualization */ true); + /* showManeuverLossVisualization */ false); } private static Set createHelpLineSettings(boolean showCourseGeometry) { From 778b192121fec389f9429aaa51bcae08af43b801 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 15:51:20 +0200 Subject: [PATCH 40/53] bug4572: changed logic of maneuverLoss visualization setting and display only lines when markers are shown. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 18 ++++++++++++++---- 1 file changed, 14 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 2e7724d0473..ff39713209d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -2236,10 +2236,6 @@ public class RaceMap extends AbstractCompositeComponent impleme numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); - if (settings.isShowManeuverLossVisualization() && !maneuverLossCheckBoxValueStore.contains(t)) { - maneuverLossCheckBoxValueStore.add(t); - visualizeManeuverLoss(maneuver, true, competitor); - } maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.contains(t)); maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { @Override @@ -2486,6 +2482,12 @@ public class RaceMap extends AbstractCompositeComponent impleme }); maneuverMarkers.add(marker); marker.setMap(map); + /* maneuverloss visualization */ + if (settings.isShowManeuverLossVisualization() && maneuver.getManeuverLoss() != null) { + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + maneuverLossCheckBoxValueStore.add(t); + visualizeManeuverLoss(maneuver, true, competitor); + } } /** @@ -3225,6 +3227,14 @@ public class RaceMap extends AbstractCompositeComponent impleme intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + /* maneuver end extrapolation line, test start */ + Position help1 = maneuver.getManeuverLoss().getManeuverEndPosition().getIntersection( + new DegreeBearingImpl(maneuver.getSpeedWithBearingAfter().bearingInDegrees), + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, help1, + maneuver.getManeuverLoss().getManeuverEndPosition(), null, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, + STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_LINE_OPACITY)); + /* test end */ String color = maneuver.getManeuverLoss().getDistanceLost().compareTo(Distance.NULL) > 0 ? color = MANEUVERLOSSLINES_RED : MANEUVERLOSSLINES_GREEN; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, From 99559164a46e844a77ad276c52a04d8369ae3033 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 16:01:39 +0200 Subject: [PATCH 41/53] bug4572: Added a bearingAtManeuverEndPositionLine to the maneuver loss visualization. --- .../sailing/gwt/ui/client/StringMessages.java | 1 + .../gwt/ui/client/StringMessages.properties | 1 + .../ui/client/StringMessages_de.properties | 1 + .../gwt/ui/client/shared/racemap/RaceMap.java | 23 ++++++++++++------- 4 files changed, 18 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java index 7c89c6b209f..0fc6a559d91 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages.java @@ -2086,6 +2086,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, String extrapolatedManeuverStartPosition(); String projectedExtrapolatedManeuverStartPosition(); String projectedManeuverEndPosition(); + String bearingAtManeuverEndPosition(); String gender(); String manage2SailEventIdBox(); String manage2SailEventURLBox(); 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 ac2e5efed49..9ee76dd2096 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 @@ -2081,6 +2081,7 @@ middleManeuverAngle=Middle maneuver angle extrapolatedManeuverStartPosition=Extrapolated Maneuver Start Position projectedExtrapolatedManeuverStartPosition=Projection of the extrapolated maneuver start position on middle maneuver angle line projectedManeuverEndPosition=Projection of the maneuver end position on middle maneuver angle line +bearingAtManeuverEndPosition=Bearing at maneuver end position gender=Gender manage2SailEventIdBox=Manage2Sail Event ID manage2SailEventURLBox=Manage2Sail EventURL (json) 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 8bf0d72999f..35cf20640a5 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 @@ -2076,6 +2076,7 @@ middleManeuverAngle=Manöverwinkel-Halbierende extrapolatedManeuverStartPosition=Extrapolierte Manöver Anfangsposition projectedExtrapolatedManeuverStartPosition=Projektion der extrapolierten Manöver Startposition auf die Manöverwinkel-Halbierende projectedManeuverEndPosition=Projektion der Manöverendposition auf die Manöverwinkel-Halbierende +bearingAtManeuverEndPosition=Anliegender Kurs an Manöverendposition gender=Geschlecht manage2SailEventId=Manage2Sail Event ID manage2SailEventURL=Manage2Sail Event URL (json) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index ff39713209d..03ba2dd0c63 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -3169,6 +3169,13 @@ public class RaceMap extends AbstractCompositeComponent impleme return stringMessages.maneuverLoss(); } }; + + final LineInfoProvider bearingAtManeuverEndPositionLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.bearingAtManeuverEndPosition(); + } + }; /** * Visualizes the Maneuver Loss, creates the corresponding polylines and Info Overlays calling @@ -3227,14 +3234,14 @@ public class RaceMap extends AbstractCompositeComponent impleme intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - /* maneuver end extrapolation line, test start */ - Position help1 = maneuver.getManeuverLoss().getManeuverEndPosition().getIntersection( - new DegreeBearingImpl(maneuver.getSpeedWithBearingAfter().bearingInDegrees), - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, help1, - maneuver.getManeuverLoss().getManeuverEndPosition(), null, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, - STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_LINE_OPACITY)); - /* test end */ + Position startOfBearingAtManeuverEndPositionLineOnMiddleManeuverAngleLine = maneuver.getManeuverLoss() + .getManeuverEndPosition() + .getIntersection(new DegreeBearingImpl(maneuver.getSpeedWithBearingAfter().bearingInDegrees), + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, + startOfBearingAtManeuverEndPositionLineOnMiddleManeuverAngleLine, + maneuver.getManeuverLoss().getManeuverEndPosition(), bearingAtManeuverEndPositionLineInfoProvider, + MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_LINE_OPACITY)); String color = maneuver.getManeuverLoss().getDistanceLost().compareTo(Distance.NULL) > 0 ? color = MANEUVERLOSSLINES_RED : MANEUVERLOSSLINES_GREEN; maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, From 0a74014e16f35288acd9e5495d5e50bbdb449f8f Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 16:39:51 +0200 Subject: [PATCH 42/53] bug4572: Maneuver loss lines get removed correct with the maneuver markers. Introduced a custom transparency for SmallTransparentInfoOverlays, made the maneuver loss info overlays slightly more transparent than the normal info overlays. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 8 ++++---- .../racemap/SmallTransparentInfoOverlay.java | 19 ++++++++++++++++--- 2 files changed, 20 insertions(+), 7 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 03ba2dd0c63..37cd5e5b398 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -15,6 +15,7 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; +import com.google.gwt.canvas.dom.client.CssColor; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; @@ -1085,11 +1086,8 @@ public class RaceMap extends AbstractCompositeComponent impleme } if (maneuverMarkers != null) { removeAllManeuverMarkers(); - } - if (!maneuverLossLinesMap.isEmpty()) { removeManeuverLossLinesAndInfoOverlays(); } - if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } @@ -2370,6 +2368,7 @@ public class RaceMap extends AbstractCompositeComponent impleme lastManeuverResult = result; if (maneuverMarkers != null) { removeAllManeuverMarkers(); + removeManeuverLossLinesAndInfoOverlays(); } if (!(timer.getPlayState() == PlayStates.Playing)) { showManeuvers(result); @@ -3276,8 +3275,9 @@ public class RaceMap extends AbstractCompositeComponent impleme } sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.getDirectionChangeInDegrees())) + stringMessages.degreesUnit()); + CssColor greyWithTransparency = CssColor.make("rgba(255,255,255,0.5)"); SmallTransparentInfoOverlay maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, - RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem); + RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem, greyWithTransparency); maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, maneuver.getManeuverLoss().getDistanceLost().scale(0.5)), /* transition time */ -1); maneuverLossInfoOverlay.draw(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java index 48275f195e0..def29842b66 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java @@ -35,12 +35,25 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 { private double cornerRadius; private final double fontScalingFactor; + + /* Background colors */ + private static final CssColor GREYTRANSPARENT_COLOR = CssColor.make("rgba(255,255,255,0.75)"); + private final CssColor greyWithCustomTransparency; public SmallTransparentInfoOverlay(MapWidget map, int zIndex, String infoText, CoordinateSystem coordinateSystem) { - this(map, zIndex, infoText, coordinateSystem, /* fontScalingFactor */ 1.0); + this(map, zIndex, infoText, coordinateSystem, /* fontScalingFactor */ 1.0, GREYTRANSPARENT_COLOR); } public SmallTransparentInfoOverlay(MapWidget map, int zIndex, String infoText, CoordinateSystem coordinateSystem, double fontScalingFactor) { + this(map, zIndex, infoText, coordinateSystem, fontScalingFactor, GREYTRANSPARENT_COLOR); + } + + public SmallTransparentInfoOverlay(MapWidget map, int zIndex, String infoText, CoordinateSystem coordinateSystem, CssColor cssColor) { + this(map, zIndex, infoText, coordinateSystem, /* fontScalingFactor */ 1.0, cssColor); + } + + public SmallTransparentInfoOverlay(MapWidget map, int zIndex, String infoText, CoordinateSystem coordinateSystem, + double fontScalingFactor, CssColor cssColor) { super(map, zIndex, coordinateSystem); this.infoText = infoText; this.fontScalingFactor = fontScalingFactor; @@ -55,6 +68,7 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 { getCanvas().setCoordinateSpaceWidth(canvasWidth); getCanvas().setCoordinateSpaceHeight(canvasHeight); } + greyWithCustomTransparency = cssColor; } @Override @@ -67,7 +81,6 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 { if (mapProjection != null && position != null) { LatLng latLngPosition = coordinateSystem.toLatLng(position); Context2d context2d = getCanvas().getContext2d(); - CssColor grayTransparentColor = CssColor.make("rgba(255,255,255,0.75)"); final int fontSizeInPx = (int) (fontScalingFactor*(12+2*Math.max(0, map.getZoom()-15))); final int LINE_HEIGHT = fontSizeInPx+LINE_SPACING; context2d.setFont(""+fontSizeInPx+"px Roboto, Arial, sans-serif"); @@ -88,7 +101,7 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 { getCanvas().setHeight(String.valueOf(canvasHeight)); getCanvas().setCoordinateSpaceHeight(canvasHeight); // Change origin and dimensions to match true size (a stroke makes the shape a bit larger) - context2d.setFillStyle(grayTransparentColor); + context2d.setFillStyle(greyWithCustomTransparency); drawRoundedRect(context2d, cornerRadius / 2, cornerRadius / 2, infoBoxWidth - cornerRadius, infoBoxHeight - cornerRadius, cornerRadius); // this translation is important for drawing lines with a real line width of 1 pixel From 14eebf7960727cdbc659f111b88a35051217c30d Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 16:58:51 +0200 Subject: [PATCH 43/53] bug4572: made the "pole" of the smalltransparent info overlay in the same color as the fill color. --- .../ui/client/shared/racemap/SmallTransparentInfoOverlay.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/client/shared/racemap/SmallTransparentInfoOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java index def29842b66..f73521b29d6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/SmallTransparentInfoOverlay.java @@ -106,7 +106,7 @@ public class SmallTransparentInfoOverlay extends CanvasOverlayV3 { - cornerRadius, cornerRadius); // this translation is important for drawing lines with a real line width of 1 pixel context2d.translate(-0.5, -0.5); - context2d.setStrokeStyle("gray"); + context2d.setStrokeStyle(greyWithCustomTransparency); context2d.setLineWidth(1.0); context2d.beginPath(); context2d.moveTo(cornerRadius / 2, infoBoxHeight / 2); From 422646c4d5ade88711d3903b7827d52caed9f2ba Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 11 Jul 2018 17:46:42 +0200 Subject: [PATCH 44/53] bug4572: added isShowInfoOverlayWithHelplines() to LineInfoProvider interface in RaceMap. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 70 ++++++++++++++----- 1 file changed, 52 insertions(+), 18 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 37cd5e5b398..49d2a3810ce 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -1819,7 +1819,12 @@ public class RaceMap extends AbstractCompositeComponent impleme } private interface LineInfoProvider { + /* is used to get the information that appears on hovering over a line is written in {@link #adjustInfoOverlayForVisibleLine} */ String getLineInfo(); + /* defines if a info overlay is shown with helplines */ + default boolean isShowInfoOverlayWithHelplines() { + return true; + }; } /** @@ -1892,21 +1897,25 @@ public class RaceMap extends AbstractCompositeComponent impleme private void adjustInfoOverlayForVisibleLine(Polyline lineToShowOrRemoveOrUpdate, final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider) { SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.get(lineToShowOrRemoveOrUpdate); - if (getSettings().getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) { - if (infoOverlay == null) { - infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, lineInfoProvider.getLineInfo(), coordinateSystem); - infoOverlaysForLinesForCourseGeometry.put(lineToShowOrRemoveOrUpdate, infoOverlay); - infoOverlay.addToMap(); + if (lineInfoProvider.isShowInfoOverlayWithHelplines()) { + if (getSettings().getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) { + if (infoOverlay == null) { + infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, + lineInfoProvider.getLineInfo(), coordinateSystem); + infoOverlaysForLinesForCourseGeometry.put(lineToShowOrRemoveOrUpdate, infoOverlay); + infoOverlay.addToMap(); + } else { + infoOverlay.setInfoText(lineInfoProvider.getLineInfo()); + } + infoOverlay + .setPosition(position1DTO.translateGreatCircle(position1DTO.getBearingGreatCircle(position2DTO), + position1DTO.getDistance(position2DTO).scale(0.5)), /* transition time */ -1); + infoOverlay.draw(); } else { - infoOverlay.setInfoText(lineInfoProvider.getLineInfo()); - } - infoOverlay.setPosition(position1DTO.translateGreatCircle(position1DTO.getBearingGreatCircle(position2DTO), - position1DTO.getDistance(position2DTO).scale(0.5)), /* transition time */ -1); - infoOverlay.draw(); - } else { - if (infoOverlay != null) { - infoOverlay.removeFromMap(); - infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); + if (infoOverlay != null) { + infoOverlay.removeFromMap(); + infoOverlaysForLinesForCourseGeometry.remove(lineToShowOrRemoveOrUpdate); + } } } } @@ -3139,34 +3148,55 @@ public class RaceMap extends AbstractCompositeComponent impleme return raceMapLifecycle; } final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.middleManeuverAngle(); - } + @Override + public String getLineInfo() { + return stringMessages.middleManeuverAngle(); + } + + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } }; final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { return stringMessages.extrapolatedManeuverStartPosition(); } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } }; final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { return stringMessages.projectedExtrapolatedManeuverStartPosition(); } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } }; final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { return stringMessages.projectedManeuverEndPosition(); } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } }; final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { return stringMessages.maneuverLoss(); } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } }; final LineInfoProvider bearingAtManeuverEndPositionLineInfoProvider = new LineInfoProvider() { @@ -3174,6 +3204,10 @@ public class RaceMap extends AbstractCompositeComponent impleme public String getLineInfo() { return stringMessages.bearingAtManeuverEndPosition(); } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } }; /** From 1c8f36219a279733f4ea500b29cdf91e3a37a863 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 16 Jul 2018 18:43:03 +0200 Subject: [PATCH 45/53] whitespace/formatting changes only Change-Id: Ied9425cc9a41ed6842a04d312d063d46df88190f --- .../gwt/ui/client/shared/racemap/RaceMap.java | 19 +++++++++++-------- 1 file changed, 11 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 49d2a3810ce..31d2e3c6fdc 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -458,13 +458,13 @@ public class RaceMap extends AbstractCompositeComponent impleme private final Consumer showWindChartForProvider; private ManagedInfoWindow managedInfoWindow; - /** The Map where for each competitor and each maneuver is stored if it is visualized or not.*/ + /** The Map where for each competitor and each maneuver is stored if it is visualized or not. */ private Set> maneuverLossCheckBoxValueStore = new HashSet<>(); - /** The Map where the polylines for the specific maneuver are stored.*/ + /** The Map where the polylines for the specific maneuver are stored. */ private Map, Set> maneuverLossLinesMap = new HashMap<>(); - /** The Map where the info overlays for a maneuver loss visualization are stored.*/ + /** The Map where the info overlays for a maneuver loss visualization are stored. */ private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); private class AdvantageLineUpdater implements QuickRanksListener { @@ -1818,10 +1818,14 @@ public class RaceMap extends AbstractCompositeComponent impleme lineInfoProvider, "#2268a0", STANDARD_LINE_STROKEWEIGHT, STANDARD_LINE_OPACITY); } + /** + * is used to get the information that appears on hovering over a line is written in + * {@link #adjustInfoOverlayForVisibleLine} + */ private interface LineInfoProvider { - /* is used to get the information that appears on hovering over a line is written in {@link #adjustInfoOverlayForVisibleLine} */ String getLineInfo(); - /* defines if a info overlay is shown with helplines */ + + /** defines if a info overlay is shown with helplines */ default boolean isShowInfoOverlayWithHelplines() { return true; }; @@ -2490,7 +2494,7 @@ public class RaceMap extends AbstractCompositeComponent impleme }); maneuverMarkers.add(marker); marker.setMap(map); - /* maneuverloss visualization */ + // maneuverloss visualization if (settings.isShowManeuverLossVisualization() && maneuver.getManeuverLoss() != null) { Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); maneuverLossCheckBoxValueStore.add(t); @@ -3215,8 +3219,7 @@ public class RaceMap extends AbstractCompositeComponent impleme * {@link #createManeuverLossLinesAndInfoOverlays(ManeuverDTO, CompetitorDTO)}. If called with * showManeuverVisualization=false removes the polylines and infooverlays, if there are any. */ - private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, - CompetitorDTO competitor) { + private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { if (showManeuverVisualization == false) { Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); From 4c5144e4857596b56dd8fad957deb06c5f85e960 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Tue, 17 Jul 2018 18:21:04 +0200 Subject: [PATCH 46/53] bug4572: Fixed issues described in bug4670. Added showManeuverLossChanged in RaceMap#updateSettings(). Made Maneuver Loss CheckBox in RaceMapSettings to override the checkbox value for the specific maneuver. --- .../gwt/ui/client/shared/racemap/RaceMap.java | 73 ++++++++++++------- 1 file changed, 45 insertions(+), 28 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 31d2e3c6fdc..433266ae773 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -334,8 +334,8 @@ public class RaceMap extends AbstractCompositeComponent impleme * markers displayed in response to * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} */ - private Set maneuverMarkers; + private Set maneuverMarkers; private Map> lastManeuverResult; private Map> lastDouglasPeuckerResult; @@ -1086,7 +1086,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } if (maneuverMarkers != null) { removeAllManeuverMarkers(); - removeManeuverLossLinesAndInfoOverlays(); + removeAllManeuverLossLinesAndInfoOverlays(); } if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); @@ -2381,7 +2381,7 @@ public class RaceMap extends AbstractCompositeComponent impleme lastManeuverResult = result; if (maneuverMarkers != null) { removeAllManeuverMarkers(); - removeManeuverLossLinesAndInfoOverlays(); + removeAllManeuverLossLinesAndInfoOverlays(); } if (!(timer.getPlayState() == PlayStates.Playing)) { showManeuvers(result); @@ -2460,7 +2460,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } } } - + private void showManeuvers(Map> maneuvers) { maneuverMarkers = new HashSet(); if (map != null && maneuvers != null) { @@ -2494,7 +2494,7 @@ public class RaceMap extends AbstractCompositeComponent impleme }); maneuverMarkers.add(marker); marker.setMap(map); - // maneuverloss visualization + // maneuver loss visualization if (settings.isShowManeuverLossVisualization() && maneuver.getManeuverLoss() != null) { Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); maneuverLossCheckBoxValueStore.add(t); @@ -2693,21 +2693,18 @@ public class RaceMap extends AbstractCompositeComponent impleme @Override public void updateSettings(RaceMapSettings newSettings) { boolean maneuverTypeSelectionChanged = false; + boolean showManeuverLossChanged = false; boolean requiresRedraw = false; boolean requiresUpdateCoordinateSystem = false; + if (newSettings.isShowManeuverLossVisualization() != settings.isShowManeuverLossVisualization()) { + showManeuverLossChanged = true; + } for (ManeuverType maneuverType : ManeuverType.values()) { if (newSettings.isShowManeuverType(maneuverType) != settings.isShowManeuverType(maneuverType)) { maneuverTypeSelectionChanged = true; } } - if (maneuverTypeSelectionChanged) { - if (!(timer.getPlayState() == PlayStates.Playing) && lastManeuverResult != null) { - removeAllManeuverMarkers(); - showManeuvers(lastManeuverResult); - removeManeuverLossLinesAndInfoOverlays(); - } - } if (newSettings.isShowDouglasPeuckerPoints() != settings.isShowDouglasPeuckerPoints()) { if (!(timer.getPlayState() == PlayStates.Playing) && lastDouglasPeuckerResult != null && newSettings.isShowDouglasPeuckerPoints()) { removeAllMarkDouglasPeuckerpoints(); @@ -2719,7 +2716,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (newSettings.getTailLengthInMilliseconds() != settings.getTailLengthInMilliseconds()) { requiresRedraw = true; } - if (newSettings.getBuoyZoneRadius() != settings.getBuoyZoneRadius()) { + if (!newSettings.getBuoyZoneRadius().equals(settings.getBuoyZoneRadius())) { requiresRedraw = true; } if (newSettings.isShowOnlySelectedCompetitors() != settings.isShowOnlySelectedCompetitors()) { @@ -2765,6 +2762,14 @@ public class RaceMap extends AbstractCompositeComponent impleme } this.settings = newSettings; + if (maneuverTypeSelectionChanged || showManeuverLossChanged) { + if (!(timer.getPlayState() == PlayStates.Playing) && lastManeuverResult != null) { + removeAllManeuverMarkers(); + removeAllManeuverLossLinesAndInfoOverlays(); + showManeuvers(lastManeuverResult); + } + } + if (requiresUpdateCoordinateSystem) { updateCoordinateSystemFromSettings(); } @@ -3202,7 +3207,6 @@ public class RaceMap extends AbstractCompositeComponent impleme return false; } }; - final LineInfoProvider bearingAtManeuverEndPositionLineInfoProvider = new LineInfoProvider() { @Override public String getLineInfo() { @@ -3213,7 +3217,6 @@ public class RaceMap extends AbstractCompositeComponent impleme return false; } }; - /** * Visualizes the Maneuver Loss, creates the corresponding polylines and Info Overlays calling * {@link #createManeuverLossLinesAndInfoOverlays(ManeuverDTO, CompetitorDTO)}. If called with @@ -3221,24 +3224,36 @@ public class RaceMap extends AbstractCompositeComponent impleme */ private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { if (showManeuverVisualization == false) { - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), - maneuver.getType()); - if (maneuverLossLinesMap.get(t) != null) { - for (Polyline p : maneuverLossLinesMap.get(t)) { - p.setMap((MapWidget) null); - } - } - maneuverLossInfoOverlayMap.get(t).removeFromMap(); + removeManeuverLossLinesAndInfoOverlayForManeuver(maneuver, competitor); } else { createManeuverLossLinesAndInfoOverlays(maneuver, competitor); } } /** - * Removes the Polylines visualizing the ManeuverLoss and the SmallTransparentInfoOverlays attached to the - * Polylines. Also clears the CheckboxValueStore for maneuverLoss. + * Removes the polylines and the info overlay visualizing one maneuver from the map. Also removes the identifying + * Triple from the corresponding + * {@link #maneuverLossLinesMap} and {@link #maneuverLossInfoOverlayMap}. */ - private void removeManeuverLossLinesAndInfoOverlays() { + private void removeManeuverLossLinesAndInfoOverlayForManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { + Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + if (maneuverLossLinesMap.get(t) != null) { + for (Polyline p : maneuverLossLinesMap.get(t)) { + p.setMap((MapWidget) null); + } + maneuverLossLinesMap.remove(t); + } + if (maneuverLossInfoOverlayMap.get(t) != null) { + maneuverLossInfoOverlayMap.get(t).removeFromMap(); + maneuverLossInfoOverlayMap.remove(t); + } + } + + /** + * Removes all maneuver loss polylines and corresponding info overlays from the map. Clears {@link #maneuverLossInfoOverlayMap} + * and {@link #maneuverLossLinesMap}. Clears {@link #maneuverLossCheckBoxValueStore}. + */ + private void removeAllManeuverLossLinesAndInfoOverlays() { for (Triple t : maneuverLossLinesMap.keySet()) { for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { maneuverLossLine.setMap((MapWidget) null); @@ -3253,8 +3268,10 @@ public class RaceMap extends AbstractCompositeComponent impleme } /** - * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLossInMeters()} and draw - * {@link SmallTransparentInfoOverlay}s attached to the {@link Polyline}s visualizing the maneuver. + * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLossInMeters()} and + * {@link SmallTransparentInfoOverlay}s attached to the {@link Polyline}s visualizing the maneuver. Stores them in + * {@link #maneuverLossInfoOverlayMap} and {@link #maneuverLossLinesMap} with the identifier + * Triple. */ private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { final Set maneuverLossLines = new HashSet<>(); From 07c875f21ef0ab0bde03f5efbcf0e966ec25aecd Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Tue, 17 Jul 2018 19:13:35 +0200 Subject: [PATCH 47/53] bug4572: formatting changes --- .../sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java | 7 ++----- 1 file changed, 2 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 433266ae773..a70611bea9d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -334,8 +334,8 @@ public class RaceMap extends AbstractCompositeComponent impleme * markers displayed in response to * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} */ - private Set maneuverMarkers; + private Map> lastManeuverResult; private Map> lastDouglasPeuckerResult; @@ -1824,8 +1824,6 @@ public class RaceMap extends AbstractCompositeComponent impleme */ private interface LineInfoProvider { String getLineInfo(); - - /** defines if a info overlay is shown with helplines */ default boolean isShowInfoOverlayWithHelplines() { return true; }; @@ -1911,8 +1909,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } else { infoOverlay.setInfoText(lineInfoProvider.getLineInfo()); } - infoOverlay - .setPosition(position1DTO.translateGreatCircle(position1DTO.getBearingGreatCircle(position2DTO), + infoOverlay.setPosition(position1DTO.translateGreatCircle(position1DTO.getBearingGreatCircle(position2DTO), position1DTO.getDistance(position2DTO).scale(0.5)), /* transition time */ -1); infoOverlay.draw(); } else { From 37baa6d5be4c2c127ac3a72abd375bd218763f82 Mon Sep 17 00:00:00 2001 From: Alessandro Stoltenberg Date: Wed, 18 Jul 2018 09:45:54 +0200 Subject: [PATCH 48/53] bug4572: added release notes --- .../desktop/places/whatsnew/resources/SailingAnalyticsNotes.html | 1 + 1 file changed, 1 insertion(+) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html index a4f323b99b4..fa28a560f40 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html @@ -6,6 +6,7 @@

July 2018
    +
  • In RaceBoard.html there is a new feature for maneuver loss visualization. There is a setting in the map settings and in the maneuver pop up.
  • In RaceBoard.html when zooming into a specific time range, the time slider will provide a "reset zoom" button in expanded mode.
From 10eb0d065c111b60542c593c60f754741870981f Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 18 Jul 2018 13:57:38 +0200 Subject: [PATCH 49/53] introduced method createManeuverKey to factor the commonalities in four places Change-Id: I1359a49e1be15646f887b4bbec596ada4102c000 --- .../gwt/ui/client/shared/racemap/RaceMap.java | 20 +++++++++++-------- 1 file changed, 12 insertions(+), 8 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index df8317ebb84..a9cea971aab 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -1819,7 +1819,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } /** - * is used to get the information that appears on hovering over a line is written in + * Used to get the information that appears on hovering over a line and that is displayed in * {@link #adjustInfoOverlayForVisibleLine} */ private interface LineInfoProvider { @@ -1898,8 +1898,8 @@ public class RaceMap extends AbstractCompositeComponent impleme private void adjustInfoOverlayForVisibleLine(Polyline lineToShowOrRemoveOrUpdate, final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider) { - SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.get(lineToShowOrRemoveOrUpdate); if (lineInfoProvider.isShowInfoOverlayWithHelplines()) { + SmallTransparentInfoOverlay infoOverlay = infoOverlaysForLinesForCourseGeometry.get(lineToShowOrRemoveOrUpdate); if (getSettings().getHelpLinesSettings().isVisible(HelpLineTypes.COURSEGEOMETRY)) { if (infoOverlay == null) { infoOverlay = new SmallTransparentInfoOverlay(map, RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, @@ -2243,7 +2243,7 @@ public class RaceMap extends AbstractCompositeComponent impleme Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + Triple t = createManeuverKey(maneuver, competitor); maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.contains(t)); maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { @Override @@ -2493,12 +2493,16 @@ public class RaceMap extends AbstractCompositeComponent impleme marker.setMap(map); // maneuver loss visualization if (settings.isShowManeuverLossVisualization() && maneuver.getManeuverLoss() != null) { - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + Triple t = createManeuverKey(maneuver, competitor); maneuverLossCheckBoxValueStore.add(t); visualizeManeuverLoss(maneuver, true, competitor); } } + private Triple createManeuverKey(ManeuverDTO maneuver, CompetitorDTO competitor) { + return new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + } + /** * @param date * the point in time for which to determine the competitor's boat position; approximated by using the fix @@ -3224,7 +3228,8 @@ public class RaceMap extends AbstractCompositeComponent impleme /** * Visualizes the Maneuver Loss, creates the corresponding polylines and Info Overlays calling * {@link #createManeuverLossLinesAndInfoOverlays(ManeuverDTO, CompetitorDTO)}. If called with - * showManeuverVisualization=false removes the polylines and infooverlays, if there are any. + * {@code showManeuverVisualization=false} removes the polylines and info overlays, if there are any. + * This modifies the {@link #maneuverLossInfoOverlayMap} and {@link #maneuverLossLinesMap} structures. */ private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { if (showManeuverVisualization == false) { @@ -3240,7 +3245,7 @@ public class RaceMap extends AbstractCompositeComponent impleme * {@link #maneuverLossLinesMap} and {@link #maneuverLossInfoOverlayMap}. */ private void removeManeuverLossLinesAndInfoOverlayForManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + Triple t = createManeuverKey(maneuver, competitor); if (maneuverLossLinesMap.get(t) != null) { for (Polyline p : maneuverLossLinesMap.get(t)) { p.setMap((MapWidget) null); @@ -3318,8 +3323,7 @@ public class RaceMap extends AbstractCompositeComponent impleme maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, HIGHLIGHTED_LINE_STROKEWEIGHT, STANDARD_LINE_OPACITY)); - Triple t = new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), - maneuver.getType()); + Triple t = createManeuverKey(maneuver, competitor); maneuverLossLinesMap.put(t, maneuverLossLines); StringBuilder sb = new StringBuilder(); sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) From 48f74336aba650fff132ce909057306ba1228787 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 18 Jul 2018 14:38:03 +0200 Subject: [PATCH 50/53] moved functionality around maneuver markers and loss indicators into a class Change-Id: Ibbfdfcadac294282cf420b7c775ddf1cf0017d33 --- .../shared/racemap/LineInfoProvider.java | 12 + .../ManeuverMarkersAndLossIndicators.java | 437 ++++++++++++++++++ .../gwt/ui/client/shared/racemap/RaceMap.java | 417 ++--------------- .../shared/racemap/RaceMapImageManager.java | 34 +- 4 files changed, 497 insertions(+), 403 deletions(-) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/LineInfoProvider.java create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/LineInfoProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/LineInfoProvider.java new file mode 100755 index 00000000000..a3f22b42b26 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/LineInfoProvider.java @@ -0,0 +1,12 @@ +package com.sap.sailing.gwt.ui.client.shared.racemap; + +/** + * Used to get the information that appears on hovering over a line and that is displayed in + * {@link #adjustInfoOverlayForVisibleLine} + */ +interface LineInfoProvider { + String getLineInfo(); + default boolean isShowInfoOverlayWithHelplines() { + return true; + }; +} \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java new file mode 100755 index 00000000000..cc26c3feb8e --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java @@ -0,0 +1,437 @@ +package com.sap.sailing.gwt.ui.client.shared.racemap; + +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.canvas.dom.client.CssColor; +import com.google.gwt.event.logical.shared.ValueChangeEvent; +import com.google.gwt.event.logical.shared.ValueChangeHandler; +import com.google.gwt.i18n.client.DateTimeFormat; +import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; +import com.google.gwt.i18n.client.NumberFormat; +import com.google.gwt.maps.client.MapWidget; +import com.google.gwt.maps.client.base.LatLng; +import com.google.gwt.maps.client.overlays.Marker; +import com.google.gwt.maps.client.overlays.MarkerOptions; +import com.google.gwt.maps.client.overlays.Polyline; +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.VerticalPanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.domain.common.ManeuverType; +import com.sap.sailing.domain.common.NauticalSide; +import com.sap.sailing.domain.common.Position; +import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; +import com.sap.sailing.gwt.ui.client.ManeuverTypeFormatter; +import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; +import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.shared.ManeuverDTO; +import com.sap.sailing.gwt.ui.shared.SpeedWithBearingDTO; +import com.sap.sse.common.Bearing; +import com.sap.sse.common.Distance; +import com.sap.sse.common.TimeRange; +import com.sap.sse.common.Util.Pair; +import com.sap.sse.common.Util.Triple; +import com.sap.sse.common.impl.DegreeBearingImpl; +import com.sap.sse.gwt.client.ErrorReporter; +import com.sap.sse.gwt.client.player.Timer.PlayStates; + +/** + * Encapsulates the diamond-shaped maneuver markers that can be displayed on a competitor's + * tail, as well as the optional maneuver loss indicators that may be shown for a maneuver + * marker.

+ * + * Invariants: + * + *

    + *
  • When a maneuver loss indicator is shown then the corresponding maneuver marker is + * visible as well.
  • + *
  • When adding a maneuver marker to the map display, the {@link RaceMapSettings#isShowManeuverLossVisualization()} setting + * decides whether a maneuver loss indicator is added to the map display as well.
  • + *
+ * + * @author Axel Uhl (d043530) + * + */ +public class ManeuverMarkersAndLossIndicators { + private static final String MANEUVERLOSSLINES_PROJECTEDLINES_COLOR = "#ffff00"; + private static final String MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR = "#ffffff"; + private static final String MANEUVERLOSSLINES_RED = "#ff0000"; + private static final String MANEUVERLOSSLINES_GREEN = "#00ff00"; + private static final double LOWLIGHTED_LINE_OPACITY = 0.5; + private static final int HIGHLIGHTED_LINE_STROKEWEIGHT = 2; + private static final NumberFormat numberFormatOneDecimal = NumberFormatterFactory.getDecimalFormat(1); + + private final RaceMap raceMap; + private final SailingServiceAsync sailingService; + private final StringMessages stringMessages; + private final ErrorReporter errorReporter; + private Map> lastManeuverResult; + + /** + * markers displayed in response to + * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} + */ + private Set maneuverMarkers; + + /** The Map where for each competitor and each maneuver is stored if it is visualized or not. */ + private Set> maneuverLossCheckBoxValueStore = new HashSet<>(); + + /** The Map where the polylines for the specific maneuver are stored. */ + private Map, Set> maneuverLossLinesMap = new HashMap<>(); + + /** The Map where the info overlays for a maneuver loss visualization are stored. */ + private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); + + public ManeuverMarkersAndLossIndicators(RaceMap raceMap, SailingServiceAsync sailingService, ErrorReporter errorReporter, StringMessages stringMessages) { + this.raceMap = raceMap; + this.sailingService = sailingService; + this.errorReporter = errorReporter; + this.stringMessages = stringMessages; + } + + public void getAndShowManeuvers(RegattaAndRaceIdentifier race, Map timeRange) { + sailingService.getManeuvers(race, timeRange, new AsyncCallback>>() { + @Override + public void onFailure(Throwable caught) { + errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), true /*silentMode */); + } + + @Override + public void onSuccess(Map> result) { + lastManeuverResult = result; + if (maneuverMarkers != null) { + removeAllManeuverMarkers(); + removeAllManeuverLossLinesAndInfoOverlays(); + } + if (raceMap.getTimer().getPlayState() != PlayStates.Playing) { + showManeuvers(result); + } + } + }); + } + + protected void removeAllManeuverMarkers() { + if (maneuverMarkers != null) { + for (Marker marker : maneuverMarkers) { + marker.setMap((MapWidget) null); + } + maneuverMarkers = null; + } + } + + private void showManeuvers(Map> maneuvers) { + maneuverMarkers = new HashSet(); + if (raceMap.getMap() != null && maneuvers != null) { + Set keySet = maneuvers.keySet(); + Iterator iter = keySet.iterator(); + while (iter.hasNext()) { + CompetitorDTO competitorDTO = iter.next(); + List maneuversForCompetitor = maneuvers.get(competitorDTO); + for (ManeuverDTO maneuver : maneuversForCompetitor) { + if (raceMap.getSettings().isShowManeuverType(maneuver.getType())) { + createAndAddMarkerOfManeuver(maneuver, competitorDTO); + } + } + } + } + } + + private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { + LatLng latLng = raceMap.getCoordinateSystem().toLatLng(maneuver.getPosition()); + Marker maneuverMarker = raceMap.getRaceMapImageManager().getManeuverIconsForTypeAndDirectionIndicatingColor() + .get(new Pair(maneuver.getType(), ManeuverColor.getManeuverColor(maneuver))); + MarkerOptions options = MarkerOptions.newInstance(); + options.setIcon(maneuverMarker.getIcon_MarkerImage()); + Marker marker = Marker.newInstance(options); + marker.setPosition(latLng); + marker.setTitle(ManeuverTypeFormatter.format(maneuver.getType(), stringMessages)); + marker.addClickHandler(event -> { + LatLng where = raceMap.getCoordinateSystem().toLatLng(maneuver.getPosition()); + Widget content = getInfoWindowContent(maneuver, competitor); + raceMap.getManagedInfoWindow().openAtPosition(content, where); + }); + maneuverMarkers.add(marker); + marker.setMap(raceMap.getMap()); + // maneuver loss visualization: if the setting is active, show loss visualization for the maneuver by default + if (raceMap.getSettings().isShowManeuverLossVisualization() && maneuver.getManeuverLoss() != null) { + Triple t = createManeuverKey(maneuver, competitor); + maneuverLossCheckBoxValueStore.add(t); + visualizeManeuverLoss(maneuver, true, competitor); + } + } + + private Triple createManeuverKey(ManeuverDTO maneuver, CompetitorDTO competitor) { + return new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); + } + + final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.middleManeuverAngle(); + } + + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } + }; + final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.extrapolatedManeuverStartPosition(); + } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } + }; + final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.projectedExtrapolatedManeuverStartPosition(); + } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } + }; + final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.projectedManeuverEndPosition(); + } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } + }; + final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.maneuverLoss(); + } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } + }; + final LineInfoProvider bearingAtManeuverEndPositionLineInfoProvider = new LineInfoProvider() { + @Override + public String getLineInfo() { + return stringMessages.bearingAtManeuverEndPosition(); + } + @Override + public boolean isShowInfoOverlayWithHelplines() { + return false; + } + }; + /** + * Visualizes the Maneuver Loss, creates the corresponding polylines and Info Overlays calling + * {@link #createManeuverLossLinesAndInfoOverlays(ManeuverDTO, CompetitorDTO)}. If called with + * {@code showManeuverVisualization=false} removes the polylines and info overlays, if there are any. + * This modifies the {@link #maneuverLossInfoOverlayMap} and {@link #maneuverLossLinesMap} structures. + */ + private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { + if (showManeuverVisualization == false) { + removeManeuverLossLinesAndInfoOverlayForManeuver(maneuver, competitor); + } else { + createManeuverLossLinesAndInfoOverlays(maneuver, competitor); + } + } + + /** + * Removes the polylines and the info overlay visualizing one maneuver from the map. Also removes the identifying + * Triple from the corresponding + * {@link #maneuverLossLinesMap} and {@link #maneuverLossInfoOverlayMap}. + */ + private void removeManeuverLossLinesAndInfoOverlayForManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { + Triple t = createManeuverKey(maneuver, competitor); + if (maneuverLossLinesMap.get(t) != null) { + for (Polyline p : maneuverLossLinesMap.get(t)) { + p.setMap((MapWidget) null); + } + maneuverLossLinesMap.remove(t); + } + if (maneuverLossInfoOverlayMap.get(t) != null) { + maneuverLossInfoOverlayMap.get(t).removeFromMap(); + maneuverLossInfoOverlayMap.remove(t); + } + } + + /** + * Removes all maneuver loss polylines and corresponding info overlays from the map. Clears {@link #maneuverLossInfoOverlayMap} + * and {@link #maneuverLossLinesMap}. Clears {@link #maneuverLossCheckBoxValueStore}. + */ + private void removeAllManeuverLossLinesAndInfoOverlays() { + for (Triple t : maneuverLossLinesMap.keySet()) { + for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { + maneuverLossLine.setMap((MapWidget) null); + } + } + maneuverLossLinesMap.clear(); + for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap.entrySet()) { + e.getValue().removeFromMap(); + } + maneuverLossInfoOverlayMap.clear(); + maneuverLossCheckBoxValueStore.clear(); + } + + /** + * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLossInMeters()} and + * {@link SmallTransparentInfoOverlay}s attached to the {@link Polyline}s visualizing the maneuver. Stores them in + * {@link #maneuverLossInfoOverlayMap} and {@link #maneuverLossLinesMap} with the identifier + * Triple. + */ + private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { + final Set maneuverLossLines = new HashSet<>(); + Bearing bearingBefore = maneuver.getManeuverLoss().getSpeedWithBearingBefore().getBearing(); + Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.getManeuverLoss().getMiddleManeuverAngle()); + Distance extrapolationOfManeuverStartPoint = + maneuver.getManeuverLoss().getSpeedWithBearingBefore().travel(maneuver.getManeuverLoss().getManeuverDuration()); + Position extrapolatedManeuverStartPosition = maneuver.getManeuverLoss().getManeuverStartPosition() + .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); + Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.getManeuverLoss().getManeuverStartPosition() + .getIntersection(bearingBefore, maneuver.getPosition(), middleManeuverAngle); + Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + Position startOfBearingAtManeuverEndPositionLineOnMiddleManeuverAngleLine = maneuver.getManeuverLoss() + .getManeuverEndPosition() + .getIntersection(new DegreeBearingImpl(maneuver.getSpeedWithBearingAfter().bearingInDegrees), + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); + maneuverLossLines.add(raceMap.showOrRemoveOrUpdateLine(null, true, + startOfBearingAtManeuverEndPositionLineOnMiddleManeuverAngleLine, + maneuver.getManeuverLoss().getManeuverEndPosition(), bearingAtManeuverEndPositionLineInfoProvider, + MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, RaceMap.STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_LINE_OPACITY)); + String color = maneuver.getManeuverLoss().getDistanceLost().compareTo(Distance.NULL) > 0 + ? color = MANEUVERLOSSLINES_RED : MANEUVERLOSSLINES_GREEN; + maneuverLossLines.add(raceMap.showOrRemoveOrUpdateLine(null, true, + intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, + middleManeuverAngleLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, + RaceMap.STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_LINE_OPACITY)); + maneuverLossLines.add(raceMap.showOrRemoveOrUpdateLine(null, true, + maneuver.getManeuverLoss().getManeuverStartPosition(), extrapolatedManeuverStartPosition, + extrapolatedLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, RaceMap.STANDARD_LINE_STROKEWEIGHT, + LOWLIGHTED_LINE_OPACITY)); + maneuverLossLines.add(raceMap.showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, + projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, + MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, RaceMap.STANDARD_LINE_STROKEWEIGHT, RaceMap.LOWLIGHTED_TAIL_OPACITY)); + maneuverLossLines.add(raceMap.showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverEndPosition(), + projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, + MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, RaceMap.STANDARD_LINE_STROKEWEIGHT, RaceMap.LOWLIGHTED_TAIL_OPACITY)); + maneuverLossLines.add(raceMap.showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, + projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, HIGHLIGHTED_LINE_STROKEWEIGHT, + RaceMap.STANDARD_LINE_OPACITY)); + Triple t = createManeuverKey(maneuver, competitor); + maneuverLossLinesMap.put(t, maneuverLossLines); + StringBuilder sb = new StringBuilder(); + sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + + stringMessages.metersUnit() + '\n'); + if (maneuver.getType() == ManeuverType.TACK) { + sb.append(stringMessages.tackAngle() + ": "); + } else if (maneuver.getType() == ManeuverType.JIBE) { + sb.append(stringMessages.jibeAngle() + ": "); + } else { + sb.append(stringMessages.maneuverAngle() + ": "); + } + sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.getDirectionChangeInDegrees())) + + stringMessages.degreesUnit()); + CssColor greyWithTransparency = CssColor.make("rgba(255,255,255,0.5)"); + SmallTransparentInfoOverlay maneuverLossInfoOverlay = new SmallTransparentInfoOverlay(raceMap.getMap(), + RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), raceMap.getCoordinateSystem(), greyWithTransparency); + maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, + maneuver.getManeuverLoss().getDistanceLost().scale(0.5)), /* transition time */ -1); + maneuverLossInfoOverlay.draw(); + maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); + } + + void removeManeuverMarkers() { + if (maneuverMarkers != null) { + removeAllManeuverMarkers(); + removeAllManeuverLossLinesAndInfoOverlays(); + } + } + + Widget getInfoWindowContent(ManeuverDTO maneuver, CompetitorDTO competitor) { + SpeedWithBearingDTO before = maneuver.getSpeedWithBearingBefore(); + SpeedWithBearingDTO after = maneuver.getSpeedWithBearingAfter(); + VerticalPanel vPanel = new VerticalPanel(); + vPanel.add(raceMap.createInfoWindowLabelAndValue(stringMessages.maneuverType(), + ManeuverTypeFormatter.format(maneuver.getType(), stringMessages))); + vPanel.add(raceMap.createInfoWindowLabelAndValue(stringMessages.time(), + DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getTimePoint()))); + vPanel.add(raceMap.createInfoWindowLabelAndValue(stringMessages.directionChange(), + ((int) Math.round(maneuver.getDirectionChangeInDegrees())) + " " + stringMessages.degreesShort() + " (" + + ((int) Math.round(before.bearingInDegrees)) + " " + stringMessages.degreesShort() + " -> " + + ((int) Math.round(after.bearingInDegrees)) + " " + stringMessages.degreesShort() + ")")); + vPanel.add(raceMap.createInfoWindowLabelAndValue(stringMessages.speedChange(), + NumberFormat.getDecimalFormat().format(after.speedInKnots - before.speedInKnots) + " " + + stringMessages.knotsUnit() + " (" + + NumberFormat.getDecimalFormat().format(before.speedInKnots) + " " + stringMessages.knotsUnit() + + " -> " + NumberFormat.getDecimalFormat().format(after.speedInKnots) + " " + + stringMessages.knotsUnit() + ")")); + vPanel.add(raceMap.createInfoWindowLabelAndValue(stringMessages.maxTurningRate(), + NumberFormat.getDecimalFormat().format(maneuver.getMaxTurningRateInDegreesPerSecond()) + " " + + stringMessages.degreesPerSecondUnit())); + vPanel.add(raceMap.createInfoWindowLabelAndValue(stringMessages.avgTurningRate(), + NumberFormat.getDecimalFormat().format(maneuver.getAvgTurningRateInDegreesPerSecond()) + " " + + stringMessages.degreesPerSecondUnit())); + if (maneuver.getType() != ManeuverType.BEAR_AWAY && maneuver.getType() != ManeuverType.HEAD_UP) { + vPanel.add(raceMap.createInfoWindowLabelAndValue(stringMessages.lowestSpeed(), + NumberFormat.getDecimalFormat().format(maneuver.getLowestSpeedInKnots()) + " " + + stringMessages.knotsUnit())); + } + if (maneuver.getManeuverLoss() != null) { + Widget maneuverLossWidget = raceMap.createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); + CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); + Triple t = createManeuverKey(maneuver, competitor); + maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.contains(t)); + maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { + @Override + public void onValueChange(ValueChangeEvent event) { + visualizeManeuverLoss(maneuver, event.getValue(), competitor); + if (event.getValue()) { + maneuverLossCheckBoxValueStore.add(t); + } else { + maneuverLossCheckBoxValueStore.remove(t); + } + } + }); + HorizontalPanel hPanel = new HorizontalPanel(); + hPanel.add(maneuverLossWidget); + hPanel.add(maneuverLossLinesCheckBox); + vPanel.add(hPanel); + } + if (maneuver.getMarkPassingTimePoint() != null) { + vPanel.add( + raceMap.createInfoWindowLabelAndValue(stringMessages.markPassing(), + DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getMarkPassingTimePoint()) + + (maneuver.getMarkPassingSide() == null ? "" + : " (" + (maneuver.getMarkPassingSide() == NauticalSide.PORT + ? stringMessages.portSide() : stringMessages.starboardSide()) + + ")"))); + } + return vPanel; + } + + public void updateManeuverMarkersAfterSettingsChanged() { + // TODO is it really necessary to remove them all first...? + if (lastManeuverResult != null) { + removeAllManeuverMarkers(); + removeAllManeuverLossLinesAndInfoOverlays(); + showManeuvers(lastManeuverResult); + } + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index a9cea971aab..91b18c91cfd 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -15,7 +15,6 @@ import java.util.Objects; import java.util.Set; import java.util.function.Consumer; -import com.google.gwt.canvas.dom.client.CssColor; import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.core.client.Scheduler.ScheduledCommand; @@ -23,11 +22,7 @@ import com.google.gwt.dom.client.Style; import com.google.gwt.dom.client.Style.Unit; 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.event.shared.HandlerRegistration; -import com.google.gwt.i18n.client.DateTimeFormat; -import com.google.gwt.i18n.client.DateTimeFormat.PredefinedFormat; import com.google.gwt.i18n.client.NumberFormat; import com.google.gwt.maps.client.LoadApi; import com.google.gwt.maps.client.LoadApi.LoadLibrary; @@ -68,7 +63,6 @@ import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.ui.AbsolutePanel; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Button; -import com.google.gwt.user.client.ui.CheckBox; import com.google.gwt.user.client.ui.FlowPanel; import com.google.gwt.user.client.ui.HorizontalPanel; import com.google.gwt.user.client.ui.Image; @@ -78,7 +72,6 @@ import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.domain.common.Bounds; import com.sap.sailing.domain.common.ManeuverType; -import com.sap.sailing.domain.common.NauticalSide; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.WindSource; @@ -92,7 +85,6 @@ import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.scalablevalue.impl.ScalableBearing; import com.sap.sailing.domain.common.scalablevalue.impl.ScalablePosition; import com.sap.sailing.domain.common.windfinder.SpotDTO; -import com.sap.sailing.domain.maneuverdetection.impl.ManeuverDetectorImpl; import com.sap.sailing.gwt.ui.actions.GetBoatPositionsAction; import com.sap.sailing.gwt.ui.actions.GetPolarAction; import com.sap.sailing.gwt.ui.actions.GetRaceMapDataAction; @@ -100,7 +92,6 @@ import com.sap.sailing.gwt.ui.actions.GetWindInfoAction; import com.sap.sailing.gwt.ui.client.ClientResources; import com.sap.sailing.gwt.ui.client.CompetitorSelectionChangeListener; import com.sap.sailing.gwt.ui.client.CompetitorSelectionProvider; -import com.sap.sailing.gwt.ui.client.ManeuverTypeFormatter; import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; import com.sap.sailing.gwt.ui.client.RaceCompetitorSelectionProvider; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProviderListener; @@ -120,7 +111,6 @@ import com.sap.sailing.gwt.ui.shared.ControlPointDTO; import com.sap.sailing.gwt.ui.shared.CoursePositionsDTO; import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType; import com.sap.sailing.gwt.ui.shared.LegInfoDTO; -import com.sap.sailing.gwt.ui.shared.ManeuverDTO; import com.sap.sailing.gwt.ui.shared.MarkDTO; import com.sap.sailing.gwt.ui.shared.QuickRankDTO; import com.sap.sailing.gwt.ui.shared.RaceCourseDTO; @@ -168,20 +158,14 @@ import com.sap.sse.gwt.client.shared.settings.ComponentContext; public class RaceMap extends AbstractCompositeComponent implements TimeListener, CompetitorSelectionChangeListener, RaceTimesInfoProviderListener, TailFactory, RequiresDataInitialization, RequiresResize, QuickRankProvider { /* Line colors */ - private static final String ADVANTAGE_LINE_COLOR = "#ff9900"; // orange - private static final String START_LINE_COLOR = "#ffffff"; - private static final String FINISH_LINE_COLOR = "#000000"; - private static final Color LOWLIGHTED_TAIL_COLOR = new RGBColor(200, 200, 200); - private static final String MANEUVERLOSSLINES_PROJECTEDLINES_COLOR = "#ffff00"; - private static final String MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR = "#ffffff"; - private static final String MANEUVERLOSSLINES_RED = "#ff0000"; - private static final String MANEUVERLOSSLINES_GREEN = "#00ff00"; + static final String ADVANTAGE_LINE_COLOR = "#ff9900"; // orange + static final String START_LINE_COLOR = "#ffffff"; + static final String FINISH_LINE_COLOR = "#000000"; + static final Color LOWLIGHTED_TAIL_COLOR = new RGBColor(200, 200, 200); /* Line opacities and stroke weights */ - private static final double LOWLIGHTED_TAIL_OPACITY = 0.3; - private static final double STANDARD_LINE_OPACITY = 1.0; - private static final double LOWLIGHTED_LINE_OPACITY = 0.5; - private static final int STANDARD_LINE_STROKEWEIGHT = 1; - private static final int HIGHLIGHTED_LINE_STROKEWEIGHT = 2; + static final double LOWLIGHTED_TAIL_OPACITY = 0.3; + static final double STANDARD_LINE_OPACITY = 1.0; + static final int STANDARD_LINE_STROKEWEIGHT = 1; public static final String GET_RACE_MAP_DATA_CATEGORY = "getRaceMapData"; public static final String GET_WIND_DATA_CATEGORY = "getWindData"; @@ -330,14 +314,6 @@ public class RaceMap extends AbstractCompositeComponent impleme */ protected Set douglasMarkers; - /** - * markers displayed in response to - * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} - */ - private Set maneuverMarkers; - - private Map> lastManeuverResult; - private Map> lastDouglasPeuckerResult; private final RaceCompetitorSelectionProvider competitorSelection; @@ -369,7 +345,7 @@ public class RaceMap extends AbstractCompositeComponent impleme */ private int startedProcessingRequestID; - private RaceMapImageManager raceMapImageManager; + private final RaceMapImageManager raceMapImageManager; private RaceMapSettings settings; private final RaceMapLifecycle raceMapLifecycle; @@ -441,8 +417,8 @@ public class RaceMap extends AbstractCompositeComponent impleme */ private boolean orientationChangeInProgress; - private final NumberFormat numberFormatOneDecimal = NumberFormat.getFormat("0.0"); - private final NumberFormat numberFormatNoDecimal = NumberFormat.getFormat("0"); + private final NumberFormat numberFormatOneDecimal = NumberFormatterFactory.getDecimalFormat(1); + private final NumberFormat numberFormatNoDecimal = NumberFormatterFactory.getDecimalFormat(0); /** * The competitor for which the advantage line is currently showing. Should this competitor's quick rank change, or @@ -458,14 +434,7 @@ public class RaceMap extends AbstractCompositeComponent impleme private final Consumer showWindChartForProvider; private ManagedInfoWindow managedInfoWindow; - /** The Map where for each competitor and each maneuver is stored if it is visualized or not. */ - private Set> maneuverLossCheckBoxValueStore = new HashSet<>(); - - /** The Map where the polylines for the specific maneuver are stored. */ - private Map, Set> maneuverLossLinesMap = new HashMap<>(); - - /** The Map where the info overlays for a maneuver loss visualization are stored. */ - private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); + private final ManeuverMarkersAndLossIndicators maneuverMarkersAndLossIndicators; private class AdvantageLineUpdater implements QuickRanksListener { @Override @@ -496,6 +465,7 @@ public class RaceMap extends AbstractCompositeComponent impleme StringMessages stringMessages, RegattaAndRaceIdentifier raceIdentifier, RaceMapResources raceMapResources, boolean showHeaderPanel, QuickRanksDTOProvider quickRanksDTOProvider, Consumer showWindChartForProvider) { super(parent, context); + this.maneuverMarkersAndLossIndicators = new ManeuverMarkersAndLossIndicators(this, sailingService, errorReporter, stringMessages); this.showHeaderPanel = showHeaderPanel; this.quickRanksDTOProvider = quickRanksDTOProvider; this.raceMapLifecycle = raceMapLifecycle; @@ -551,6 +521,14 @@ public class RaceMap extends AbstractCompositeComponent impleme this.setSize("100%", "100%"); } + ManagedInfoWindow getManagedInfoWindow() { + return managedInfoWindow; + } + + RaceMapImageManager getRaceMapImageManager() { + return raceMapImageManager; + } + public void setQuickRanksDTOProvider(QuickRanksDTOProvider newQuickRanksDTOProvider) { if (this.quickRanksDTOProvider != null) { this.quickRanksDTOProvider.moveListernersTo(newQuickRanksDTOProvider); @@ -1084,10 +1062,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); } - if (maneuverMarkers != null) { - removeAllManeuverMarkers(); - removeAllManeuverLossLinesAndInfoOverlays(); - } + maneuverMarkersAndLossIndicators.removeManeuverMarkers(); if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } @@ -1818,17 +1793,6 @@ public class RaceMap extends AbstractCompositeComponent impleme lineInfoProvider, "#2268a0", STANDARD_LINE_STROKEWEIGHT, STANDARD_LINE_OPACITY); } - /** - * Used to get the information that appears on hovering over a line and that is displayed in - * {@link #adjustInfoOverlayForVisibleLine} - */ - private interface LineInfoProvider { - String getLineInfo(); - default boolean isShowInfoOverlayWithHelplines() { - return true; - }; - } - /** * @param showLine * tells whether or not to show the line; if the lineToShowOrRemoveOrUpdate references a @@ -1838,7 +1802,7 @@ public class RaceMap extends AbstractCompositeComponent impleme * @return null if the line is not shown; the polyline object representing the line being displayed * otherwise */ - private Polyline showOrRemoveOrUpdateLine(Polyline lineToShowOrRemoveOrUpdate, final boolean showLine, + Polyline showOrRemoveOrUpdateLine(Polyline lineToShowOrRemoveOrUpdate, final boolean showLine, final Position position1DTO, final Position position2DTO, final LineInfoProvider lineInfoProvider, String lineColorRGB, int strokeWeight, double strokeOpacity) { if (position1DTO != null && position2DTO != null) { @@ -2182,7 +2146,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } } - private Widget createInfoWindowLabelAndValue(String labelName, String value) { + Widget createInfoWindowLabelAndValue(String labelName, String value) { Label valueLabel = new Label(value); valueLabel.setWordWrap(false); return createInfoWindowLabelWithWidget(labelName, valueLabel); @@ -2210,69 +2174,6 @@ public class RaceMap extends AbstractCompositeComponent impleme return vPanel; } - private Widget getInfoWindowContent(ManeuverDTO maneuver, CompetitorDTO competitor) { - SpeedWithBearingDTO before = maneuver.getSpeedWithBearingBefore(); - SpeedWithBearingDTO after = maneuver.getSpeedWithBearingAfter(); - VerticalPanel vPanel = new VerticalPanel(); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.maneuverType(), - ManeuverTypeFormatter.format(maneuver.getType(), stringMessages))); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.time(), - DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getTimePoint()))); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.directionChange(), - ((int) Math.round(maneuver.getDirectionChangeInDegrees())) + " " + stringMessages.degreesShort() + " (" - + ((int) Math.round(before.bearingInDegrees)) + " " + stringMessages.degreesShort() + " -> " - + ((int) Math.round(after.bearingInDegrees)) + " " + stringMessages.degreesShort() + ")")); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.speedChange(), - NumberFormat.getDecimalFormat().format(after.speedInKnots - before.speedInKnots) + " " - + stringMessages.knotsUnit() + " (" - + NumberFormat.getDecimalFormat().format(before.speedInKnots) + " " + stringMessages.knotsUnit() - + " -> " + NumberFormat.getDecimalFormat().format(after.speedInKnots) + " " - + stringMessages.knotsUnit() + ")")); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.maxTurningRate(), - NumberFormat.getDecimalFormat().format(maneuver.getMaxTurningRateInDegreesPerSecond()) + " " - + stringMessages.degreesPerSecondUnit())); - vPanel.add(createInfoWindowLabelAndValue(stringMessages.avgTurningRate(), - NumberFormat.getDecimalFormat().format(maneuver.getAvgTurningRateInDegreesPerSecond()) + " " - + stringMessages.degreesPerSecondUnit())); - if (maneuver.getType() != ManeuverType.BEAR_AWAY && maneuver.getType() != ManeuverType.HEAD_UP) { - vPanel.add(createInfoWindowLabelAndValue(stringMessages.lowestSpeed(), - NumberFormat.getDecimalFormat().format(maneuver.getLowestSpeedInKnots()) + " " - + stringMessages.knotsUnit())); - } - if (maneuver.getManeuverLoss() != null) { - Widget maneuverLossWidget = createInfoWindowLabelAndValue(stringMessages.maneuverLoss(), - numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); - CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); - Triple t = createManeuverKey(maneuver, competitor); - maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.contains(t)); - maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { - @Override - public void onValueChange(ValueChangeEvent event) { - visualizeManeuverLoss(maneuver, event.getValue(), competitor); - if (event.getValue()) { - maneuverLossCheckBoxValueStore.add(t); - } else { - maneuverLossCheckBoxValueStore.remove(t); - } - } - }); - HorizontalPanel hPanel = new HorizontalPanel(); - hPanel.add(maneuverLossWidget); - hPanel.add(maneuverLossLinesCheckBox); - vPanel.add(hPanel); - } - if (maneuver.getMarkPassingTimePoint() != null) { - vPanel.add( - createInfoWindowLabelAndValue(stringMessages.markPassing(), - DateTimeFormat.getFormat(PredefinedFormat.TIME_FULL).format(maneuver.getMarkPassingTimePoint()) - + (maneuver.getMarkPassingSide() == null ? "" - : " (" + (maneuver.getMarkPassingSide() == NauticalSide.PORT - ? stringMessages.portSide() : stringMessages.starboardSide()) - + ")"))); - } - return vPanel; - } - private Widget getInfoWindowContent(final WindSource windSource, WindTrackInfoDTO windTrackInfoDTO) { WindDTO windDTO = windTrackInfoDTO.windFixes.get(0); final VerticalPanel vPanel = new VerticalPanel(); @@ -2333,7 +2234,7 @@ public class RaceMap extends AbstractCompositeComponent impleme speedWithBearing = new SpeedWithBearingDTO(0, 0); } vPanel.add(createInfoWindowLabelAndValue(stringMessages.speed(), - NumberFormatterFactory.getDecimalFormat(1).format(speedWithBearing.speedInKnots) + " "+stringMessages.knotsUnit())); + numberFormatOneDecimal.format(speedWithBearing.speedInKnots) + " "+stringMessages.knotsUnit())); vPanel.add(createInfoWindowLabelAndValue(stringMessages.bearing(), (int) speedWithBearing.bearingInDegrees + " "+stringMessages.degreesShort())); if (lastFix.degreesBoatToTheWind != null) { vPanel.add(createInfoWindowLabelAndValue(stringMessages.degreesBoatToTheWind(), @@ -2367,25 +2268,7 @@ public class RaceMap extends AbstractCompositeComponent impleme } } }); - sailingService.getManeuvers(race, timeRange, new AsyncCallback>>() { - @Override - public void onFailure(Throwable caught) { - errorReporter.reportError("Error obtaining maneuvers: " + caught.getMessage(), true /*silentMode */); - } - - @Override - public void onSuccess(Map> result) { - lastManeuverResult = result; - if (maneuverMarkers != null) { - removeAllManeuverMarkers(); - removeAllManeuverLossLinesAndInfoOverlays(); - } - if (!(timer.getPlayState() == PlayStates.Playing)) { - showManeuvers(result); - } - } - }); - + maneuverMarkersAndLossIndicators.getAndShowManeuvers(race, timeRange); } } return vPanel; @@ -2428,15 +2311,6 @@ public class RaceMap extends AbstractCompositeComponent impleme douglasMarkers = null; } - protected void removeAllManeuverMarkers() { - if (maneuverMarkers != null) { - for (Marker marker : maneuverMarkers) { - marker.setMap((MapWidget) null); - } - maneuverMarkers = null; - } - } - private void showMarkDouglasPeuckerPoints(Map> gpsFixPointMapForCompetitors) { douglasMarkers = new HashSet(); if (map != null && gpsFixPointMapForCompetitors != null) { @@ -2458,51 +2332,6 @@ public class RaceMap extends AbstractCompositeComponent impleme } } - private void showManeuvers(Map> maneuvers) { - maneuverMarkers = new HashSet(); - if (map != null && maneuvers != null) { - Set keySet = maneuvers.keySet(); - Iterator iter = keySet.iterator(); - while (iter.hasNext()) { - CompetitorDTO competitorDTO = iter.next(); - List maneuversForCompetitor = maneuvers.get(competitorDTO); - for (ManeuverDTO maneuver : maneuversForCompetitor) { - if (settings.isShowManeuverType(maneuver.getType())) { - createAndAddMarkerOfManeuver(maneuver, competitorDTO); - } - } - } - } - } - - private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { - LatLng latLng = coordinateSystem.toLatLng(maneuver.getPosition()); - Marker maneuverMarker = raceMapImageManager.maneuverIconsForTypeAndDirectionIndicatingColor - .get(new Pair(maneuver.getType(), ManeuverColor.getManeuverColor(maneuver))); - MarkerOptions options = MarkerOptions.newInstance(); - options.setIcon(maneuverMarker.getIcon_MarkerImage()); - Marker marker = Marker.newInstance(options); - marker.setPosition(latLng); - marker.setTitle(ManeuverTypeFormatter.format(maneuver.getType(), stringMessages)); - marker.addClickHandler(event -> { - LatLng where = getCoordinateSystem().toLatLng(maneuver.getPosition()); - Widget content = getInfoWindowContent(maneuver, competitor); - managedInfoWindow.openAtPosition(content, where); - }); - maneuverMarkers.add(marker); - marker.setMap(map); - // maneuver loss visualization - if (settings.isShowManeuverLossVisualization() && maneuver.getManeuverLoss() != null) { - Triple t = createManeuverKey(maneuver, competitor); - maneuverLossCheckBoxValueStore.add(t); - visualizeManeuverLoss(maneuver, true, competitor); - } - } - - private Triple createManeuverKey(ManeuverDTO maneuver, CompetitorDTO competitor) { - return new Triple<>(competitor.getIdAsString(), maneuver.getTimePoint(), maneuver.getType()); - } - /** * @param date * the point in time for which to determine the competitor's boat position; approximated by using the fix @@ -2762,22 +2591,11 @@ public class RaceMap extends AbstractCompositeComponent impleme requiresRedraw = true; } this.settings = newSettings; - - if (maneuverTypeSelectionChanged) { - if (!(timer.getPlayState() == PlayStates.Playing) && lastManeuverResult != null) { - removeAllManeuverMarkers(); - showManeuvers(lastManeuverResult); - } - } - if (maneuverTypeSelectionChanged || showManeuverLossChanged) { - if (!(timer.getPlayState() == PlayStates.Playing) && lastManeuverResult != null) { - removeAllManeuverMarkers(); - removeAllManeuverLossLinesAndInfoOverlays(); - showManeuvers(lastManeuverResult); + if (timer.getPlayState() != PlayStates.Playing) { + maneuverMarkersAndLossIndicators.updateManeuverMarkersAfterSettingsChanged(); } } - if (requiresUpdateCoordinateSystem) { updateCoordinateSystemFromSettings(); } @@ -3164,185 +2982,4 @@ public class RaceMap extends AbstractCompositeComponent impleme public RaceMapLifecycle getLifecycle() { return raceMapLifecycle; } - final LineInfoProvider middleManeuverAngleLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.middleManeuverAngle(); - } - - @Override - public boolean isShowInfoOverlayWithHelplines() { - return false; - } - }; - final LineInfoProvider extrapolatedLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.extrapolatedManeuverStartPosition(); - } - @Override - public boolean isShowInfoOverlayWithHelplines() { - return false; - } - }; - final LineInfoProvider projectedExtrapolatedLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.projectedExtrapolatedManeuverStartPosition(); - } - @Override - public boolean isShowInfoOverlayWithHelplines() { - return false; - } - }; - final LineInfoProvider projectedManeuverEndLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.projectedManeuverEndPosition(); - } - @Override - public boolean isShowInfoOverlayWithHelplines() { - return false; - } - }; - final LineInfoProvider maneuverLossLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.maneuverLoss(); - } - @Override - public boolean isShowInfoOverlayWithHelplines() { - return false; - } - }; - final LineInfoProvider bearingAtManeuverEndPositionLineInfoProvider = new LineInfoProvider() { - @Override - public String getLineInfo() { - return stringMessages.bearingAtManeuverEndPosition(); - } - @Override - public boolean isShowInfoOverlayWithHelplines() { - return false; - } - }; - /** - * Visualizes the Maneuver Loss, creates the corresponding polylines and Info Overlays calling - * {@link #createManeuverLossLinesAndInfoOverlays(ManeuverDTO, CompetitorDTO)}. If called with - * {@code showManeuverVisualization=false} removes the polylines and info overlays, if there are any. - * This modifies the {@link #maneuverLossInfoOverlayMap} and {@link #maneuverLossLinesMap} structures. - */ - private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { - if (showManeuverVisualization == false) { - removeManeuverLossLinesAndInfoOverlayForManeuver(maneuver, competitor); - } else { - createManeuverLossLinesAndInfoOverlays(maneuver, competitor); - } - } - - /** - * Removes the polylines and the info overlay visualizing one maneuver from the map. Also removes the identifying - * Triple from the corresponding - * {@link #maneuverLossLinesMap} and {@link #maneuverLossInfoOverlayMap}. - */ - private void removeManeuverLossLinesAndInfoOverlayForManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { - Triple t = createManeuverKey(maneuver, competitor); - if (maneuverLossLinesMap.get(t) != null) { - for (Polyline p : maneuverLossLinesMap.get(t)) { - p.setMap((MapWidget) null); - } - maneuverLossLinesMap.remove(t); - } - if (maneuverLossInfoOverlayMap.get(t) != null) { - maneuverLossInfoOverlayMap.get(t).removeFromMap(); - maneuverLossInfoOverlayMap.remove(t); - } - } - - /** - * Removes all maneuver loss polylines and corresponding info overlays from the map. Clears {@link #maneuverLossInfoOverlayMap} - * and {@link #maneuverLossLinesMap}. Clears {@link #maneuverLossCheckBoxValueStore}. - */ - private void removeAllManeuverLossLinesAndInfoOverlays() { - for (Triple t : maneuverLossLinesMap.keySet()) { - for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { - maneuverLossLine.setMap((MapWidget) null); - } - } - maneuverLossLinesMap.clear(); - for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap.entrySet()) { - e.getValue().removeFromMap(); - } - maneuverLossInfoOverlayMap.clear(); - maneuverLossCheckBoxValueStore.clear(); - } - - /** - * Creates the ManeuverLoss Lines as calculated in {@link ManeuverDetectorImpl#getManeuverLossInMeters()} and - * {@link SmallTransparentInfoOverlay}s attached to the {@link Polyline}s visualizing the maneuver. Stores them in - * {@link #maneuverLossInfoOverlayMap} and {@link #maneuverLossLinesMap} with the identifier - * Triple. - */ - private void createManeuverLossLinesAndInfoOverlays(ManeuverDTO maneuver, CompetitorDTO competitor) { - final Set maneuverLossLines = new HashSet<>(); - Bearing bearingBefore = maneuver.getManeuverLoss().getSpeedWithBearingBefore().getBearing(); - Bearing middleManeuverAngle = new DegreeBearingImpl(maneuver.getManeuverLoss().getMiddleManeuverAngle()); - Distance extrapolationOfManeuverStartPoint = - maneuver.getManeuverLoss().getSpeedWithBearingBefore().travel(maneuver.getManeuverLoss().getManeuverDuration()); - Position extrapolatedManeuverStartPosition = maneuver.getManeuverLoss().getManeuverStartPosition() - .translateRhumb(bearingBefore, extrapolationOfManeuverStartPoint); - Position intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint = maneuver.getManeuverLoss().getManeuverStartPosition() - .getIntersection(bearingBefore, maneuver.getPosition(), middleManeuverAngle); - Position projectedExtrapolatedManeuverStartPosition = extrapolatedManeuverStartPosition.projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - Position projectedManeuverEndPosition = maneuver.getManeuverLoss().getManeuverEndPosition().projectToLineThrough( - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - Position startOfBearingAtManeuverEndPositionLineOnMiddleManeuverAngleLine = maneuver.getManeuverLoss() - .getManeuverEndPosition() - .getIntersection(new DegreeBearingImpl(maneuver.getSpeedWithBearingAfter().bearingInDegrees), - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, middleManeuverAngle); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, - startOfBearingAtManeuverEndPositionLineOnMiddleManeuverAngleLine, - maneuver.getManeuverLoss().getManeuverEndPosition(), bearingAtManeuverEndPositionLineInfoProvider, - MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_LINE_OPACITY)); - String color = maneuver.getManeuverLoss().getDistanceLost().compareTo(Distance.NULL) > 0 - ? color = MANEUVERLOSSLINES_RED : MANEUVERLOSSLINES_GREEN; - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, - intersectionMiddleManeuverAngleWithExtrapolationOfManeuverStartPoint, projectedManeuverEndPosition, - middleManeuverAngleLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, - STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_LINE_OPACITY)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, - maneuver.getManeuverLoss().getManeuverStartPosition(), extrapolatedManeuverStartPosition, - extrapolatedLineInfoProvider, MANEUVERLOSSLINES_EXTRAPOLATEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, - LOWLIGHTED_LINE_OPACITY)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, extrapolatedManeuverStartPosition, - projectedExtrapolatedManeuverStartPosition, projectedExtrapolatedLineInfoProvider, - MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_TAIL_OPACITY)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, maneuver.getManeuverLoss().getManeuverEndPosition(), - projectedManeuverEndPosition, projectedManeuverEndLineInfoProvider, - MANEUVERLOSSLINES_PROJECTEDLINES_COLOR, STANDARD_LINE_STROKEWEIGHT, LOWLIGHTED_TAIL_OPACITY)); - maneuverLossLines.add(showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, - projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, HIGHLIGHTED_LINE_STROKEWEIGHT, - STANDARD_LINE_OPACITY)); - Triple t = createManeuverKey(maneuver, competitor); - maneuverLossLinesMap.put(t, maneuverLossLines); - StringBuilder sb = new StringBuilder(); - sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) - + stringMessages.metersUnit() + '\n'); - if (maneuver.getType() == ManeuverType.TACK) { - sb.append(stringMessages.tackAngle() + ": "); - } else if (maneuver.getType() == ManeuverType.JIBE) { - sb.append(stringMessages.jibeAngle() + ": "); - } else { - sb.append(stringMessages.maneuverAngle() + ": "); - } - sb.append(numberFormatOneDecimal.format(Math.abs(maneuver.getDirectionChangeInDegrees())) - + stringMessages.degreesUnit()); - CssColor greyWithTransparency = CssColor.make("rgba(255,255,255,0.5)"); - SmallTransparentInfoOverlay maneuverLossInfoOverlay = new SmallTransparentInfoOverlay((MapWidget) map, - RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, sb.toString(), coordinateSystem, greyWithTransparency); - maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, - maneuver.getManeuverLoss().getDistanceLost().scale(0.5)), /* transition time */ -1); - maneuverLossInfoOverlay.draw(); - maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); - } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapImageManager.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapImageManager.java index 8404c590b7a..92cd181cb30 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapImageManager.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMapImageManager.java @@ -25,19 +25,19 @@ public class RaceMapImageManager { /** * An arrow showing the combined wind depending on the wind direction */ - protected ImageTransformer combinedWindIconTransformer; + private final ImageTransformer combinedWindIconTransformer; /** * An arrow showing the true north direction */ - protected ImageTransformer trueNorthIndicatorIconTransformer; + private final ImageTransformer trueNorthIndicatorIconTransformer; /** * An arrow showing the wind provided by a wind sensor on a boat */ - protected ImageTransformer windSensorIconTransformer; + private final ImageTransformer windSensorIconTransformer; - protected Map, Marker> maneuverIconsForTypeAndDirectionIndicatingColor; + private final Map, Marker> maneuverIconsForTypeAndDirectionIndicatingColor; private final RaceMapResources resources; @@ -61,11 +61,11 @@ public class RaceMapImageManager { } Marker icon; for (ManeuverType maneuver : maneuvers) { - icon = createMarker(resources.maneuverMarkerRed()); - maneuverIconsForTypeAndDirectionIndicatingColor + icon = createMarker(getResources().maneuverMarkerRed()); + getManeuverIconsForTypeAndDirectionIndicatingColor() .put(new Util.Pair(maneuver, ManeuverColor.RED), icon); - icon = createMarker(resources.maneuverMarkerGreen()); - maneuverIconsForTypeAndDirectionIndicatingColor + icon = createMarker(getResources().maneuverMarkerGreen()); + getManeuverIconsForTypeAndDirectionIndicatingColor() .put(new Util.Pair(maneuver, ManeuverColor.GREEN), icon); } } @@ -81,19 +81,27 @@ public class RaceMapImageManager { return marker; } - public ImageTransformer getTrueNorthIndicatorIconTransformer() { + ImageTransformer getTrueNorthIndicatorIconTransformer() { return trueNorthIndicatorIconTransformer; } - public ImageTransformer getCombinedWindIconTransformer() { + ImageTransformer getCombinedWindIconTransformer() { return combinedWindIconTransformer; } - public ImageTransformer getWindSensorIconTransformer() { + ImageTransformer getWindSensorIconTransformer() { return windSensorIconTransformer; } - public TextResource getWindFinderLogo() { - return resources.getWindFinderLogo(); + TextResource getWindFinderLogo() { + return getResources().getWindFinderLogo(); + } + + Map, Marker> getManeuverIconsForTypeAndDirectionIndicatingColor() { + return maneuverIconsForTypeAndDirectionIndicatingColor; + } + + private RaceMapResources getResources() { + return resources; } } From 46375000251efe4bcfe41c2e2be821663f066ac4 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 18 Jul 2018 14:40:35 +0200 Subject: [PATCH 51/53] removed method visualizeManeuverLoss Change-Id: I964c5f777cf7faac58fbbf4e9e8bd37af2d67cfb --- .../ManeuverMarkersAndLossIndicators.java | 20 ++++--------------- 1 file changed, 4 insertions(+), 16 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java index cc26c3feb8e..10bf1ca9824 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java @@ -166,7 +166,7 @@ public class ManeuverMarkersAndLossIndicators { if (raceMap.getSettings().isShowManeuverLossVisualization() && maneuver.getManeuverLoss() != null) { Triple t = createManeuverKey(maneuver, competitor); maneuverLossCheckBoxValueStore.add(t); - visualizeManeuverLoss(maneuver, true, competitor); + createManeuverLossLinesAndInfoOverlays(maneuver, competitor); } } @@ -235,20 +235,7 @@ public class ManeuverMarkersAndLossIndicators { return false; } }; - /** - * Visualizes the Maneuver Loss, creates the corresponding polylines and Info Overlays calling - * {@link #createManeuverLossLinesAndInfoOverlays(ManeuverDTO, CompetitorDTO)}. If called with - * {@code showManeuverVisualization=false} removes the polylines and info overlays, if there are any. - * This modifies the {@link #maneuverLossInfoOverlayMap} and {@link #maneuverLossLinesMap} structures. - */ - private void visualizeManeuverLoss(ManeuverDTO maneuver, boolean showManeuverVisualization, CompetitorDTO competitor) { - if (showManeuverVisualization == false) { - removeManeuverLossLinesAndInfoOverlayForManeuver(maneuver, competitor); - } else { - createManeuverLossLinesAndInfoOverlays(maneuver, competitor); - } - } - + /** * Removes the polylines and the info overlay visualizing one maneuver from the map. Also removes the identifying * Triple from the corresponding @@ -401,10 +388,11 @@ public class ManeuverMarkersAndLossIndicators { maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { @Override public void onValueChange(ValueChangeEvent event) { - visualizeManeuverLoss(maneuver, event.getValue(), competitor); if (event.getValue()) { + createManeuverLossLinesAndInfoOverlays(maneuver, competitor); maneuverLossCheckBoxValueStore.add(t); } else { + removeManeuverLossLinesAndInfoOverlayForManeuver(maneuver, competitor); maneuverLossCheckBoxValueStore.remove(t); } } From 3bcbbd57ccc37393e49a40724539f53ecc305075 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 18 Jul 2018 15:01:09 +0200 Subject: [PATCH 52/53] simplified ManeuverMarkersAndLossIndicators Change-Id: Ic43baa9fc6f2e9de7b7d20317be95adb9beb9744 --- .../ManeuverMarkersAndLossIndicators.java | 95 ++++++++----------- .../gwt/ui/client/shared/racemap/RaceMap.java | 2 +- 2 files changed, 38 insertions(+), 59 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java index 10bf1ca9824..29778534560 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java @@ -6,6 +6,7 @@ import java.util.HashSet; import java.util.Iterator; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import java.util.Set; import com.google.gwt.canvas.dom.client.CssColor; @@ -81,22 +82,22 @@ public class ManeuverMarkersAndLossIndicators { * markers displayed in response to * {@link SailingServiceAsync#getDouglasPoints(String, String, Map, Map, double, AsyncCallback)} */ - private Set maneuverMarkers; - - /** The Map where for each competitor and each maneuver is stored if it is visualized or not. */ - private Set> maneuverLossCheckBoxValueStore = new HashSet<>(); + private final Map, Marker> maneuverMarkers; /** The Map where the polylines for the specific maneuver are stored. */ - private Map, Set> maneuverLossLinesMap = new HashMap<>(); + private final Map, Set> maneuverLossLinesMap; /** The Map where the info overlays for a maneuver loss visualization are stored. */ - private Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap = new HashMap<>(); + private final Map, SmallTransparentInfoOverlay> maneuverLossInfoOverlayMap; public ManeuverMarkersAndLossIndicators(RaceMap raceMap, SailingServiceAsync sailingService, ErrorReporter errorReporter, StringMessages stringMessages) { this.raceMap = raceMap; this.sailingService = sailingService; this.errorReporter = errorReporter; this.stringMessages = stringMessages; + this.maneuverMarkers = new HashMap<>(); + this.maneuverLossLinesMap = new HashMap<>(); + this.maneuverLossInfoOverlayMap = new HashMap<>(); } public void getAndShowManeuvers(RegattaAndRaceIdentifier race, Map timeRange) { @@ -109,10 +110,7 @@ public class ManeuverMarkersAndLossIndicators { @Override public void onSuccess(Map> result) { lastManeuverResult = result; - if (maneuverMarkers != null) { - removeAllManeuverMarkers(); - removeAllManeuverLossLinesAndInfoOverlays(); - } + removeAllManeuverMarkers(); if (raceMap.getTimer().getPlayState() != PlayStates.Playing) { showManeuvers(result); } @@ -121,16 +119,19 @@ public class ManeuverMarkersAndLossIndicators { } protected void removeAllManeuverMarkers() { - if (maneuverMarkers != null) { - for (Marker marker : maneuverMarkers) { - marker.setMap((MapWidget) null); - } - maneuverMarkers = null; + // clone entry set to avoid concurrent modification exception + for (final Entry, Marker> keyAndMarker : new HashSet<>(maneuverMarkers.entrySet())) { + removeManeuverMarker(keyAndMarker.getKey(), keyAndMarker.getValue()); } } - + + private void removeManeuverMarker(Triple key, Marker marker) { + marker.setMap((MapWidget) null); + removeManeuverLossLinesAndInfoOverlayForManeuver(key); + maneuverMarkers.remove(key); + } + private void showManeuvers(Map> maneuvers) { - maneuverMarkers = new HashSet(); if (raceMap.getMap() != null && maneuvers != null) { Set keySet = maneuvers.keySet(); Iterator iter = keySet.iterator(); @@ -146,6 +147,10 @@ public class ManeuverMarkersAndLossIndicators { } } + /** + * Creates a maneuver marker. Additionally, if the {@link RaceMapSettings#isShowManeuverLossVisualization()} setting is {@code true}, + * a maneuver loss indicator is shown. + */ private void createAndAddMarkerOfManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { LatLng latLng = raceMap.getCoordinateSystem().toLatLng(maneuver.getPosition()); Marker maneuverMarker = raceMap.getRaceMapImageManager().getManeuverIconsForTypeAndDirectionIndicatingColor() @@ -160,12 +165,11 @@ public class ManeuverMarkersAndLossIndicators { Widget content = getInfoWindowContent(maneuver, competitor); raceMap.getManagedInfoWindow().openAtPosition(content, where); }); - maneuverMarkers.add(marker); + final Triple key = createManeuverKey(maneuver, competitor); + maneuverMarkers.put(key, marker); marker.setMap(raceMap.getMap()); // maneuver loss visualization: if the setting is active, show loss visualization for the maneuver by default if (raceMap.getSettings().isShowManeuverLossVisualization() && maneuver.getManeuverLoss() != null) { - Triple t = createManeuverKey(maneuver, competitor); - maneuverLossCheckBoxValueStore.add(t); createManeuverLossLinesAndInfoOverlays(maneuver, competitor); } } @@ -242,35 +246,20 @@ public class ManeuverMarkersAndLossIndicators { * {@link #maneuverLossLinesMap} and {@link #maneuverLossInfoOverlayMap}. */ private void removeManeuverLossLinesAndInfoOverlayForManeuver(ManeuverDTO maneuver, CompetitorDTO competitor) { - Triple t = createManeuverKey(maneuver, competitor); - if (maneuverLossLinesMap.get(t) != null) { - for (Polyline p : maneuverLossLinesMap.get(t)) { - p.setMap((MapWidget) null); - } - maneuverLossLinesMap.remove(t); - } - if (maneuverLossInfoOverlayMap.get(t) != null) { - maneuverLossInfoOverlayMap.get(t).removeFromMap(); - maneuverLossInfoOverlayMap.remove(t); - } + Triple key = createManeuverKey(maneuver, competitor); + removeManeuverLossLinesAndInfoOverlayForManeuver(key); } - /** - * Removes all maneuver loss polylines and corresponding info overlays from the map. Clears {@link #maneuverLossInfoOverlayMap} - * and {@link #maneuverLossLinesMap}. Clears {@link #maneuverLossCheckBoxValueStore}. - */ - private void removeAllManeuverLossLinesAndInfoOverlays() { - for (Triple t : maneuverLossLinesMap.keySet()) { - for (Polyline maneuverLossLine : maneuverLossLinesMap.get(t)) { - maneuverLossLine.setMap((MapWidget) null); + private void removeManeuverLossLinesAndInfoOverlayForManeuver(Triple key) { + if (maneuverLossLinesMap.get(key) != null) { + for (Polyline p : maneuverLossLinesMap.get(key)) { + p.setMap((MapWidget) null); } + maneuverLossLinesMap.remove(key); } - maneuverLossLinesMap.clear(); - for (Map.Entry, SmallTransparentInfoOverlay> e : maneuverLossInfoOverlayMap.entrySet()) { - e.getValue().removeFromMap(); + if (maneuverLossInfoOverlayMap.remove(key) != null) { + maneuverLossInfoOverlayMap.get(key).removeFromMap(); } - maneuverLossInfoOverlayMap.clear(); - maneuverLossCheckBoxValueStore.clear(); } /** @@ -320,8 +309,8 @@ public class ManeuverMarkersAndLossIndicators { maneuverLossLines.add(raceMap.showOrRemoveOrUpdateLine(null, true, projectedExtrapolatedManeuverStartPosition, projectedManeuverEndPosition, maneuverLossLineInfoProvider, color, HIGHLIGHTED_LINE_STROKEWEIGHT, RaceMap.STANDARD_LINE_OPACITY)); - Triple t = createManeuverKey(maneuver, competitor); - maneuverLossLinesMap.put(t, maneuverLossLines); + final Triple key = createManeuverKey(maneuver, competitor); + maneuverLossLinesMap.put(key, maneuverLossLines); StringBuilder sb = new StringBuilder(); sb.append(stringMessages.maneuverLoss() + ": " + numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + stringMessages.metersUnit() + '\n'); @@ -340,14 +329,7 @@ public class ManeuverMarkersAndLossIndicators { maneuverLossInfoOverlay.setPosition(projectedManeuverEndPosition.translateGreatCircle(middleManeuverAngle, maneuver.getManeuverLoss().getDistanceLost().scale(0.5)), /* transition time */ -1); maneuverLossInfoOverlay.draw(); - maneuverLossInfoOverlayMap.put(t, maneuverLossInfoOverlay); - } - - void removeManeuverMarkers() { - if (maneuverMarkers != null) { - removeAllManeuverMarkers(); - removeAllManeuverLossLinesAndInfoOverlays(); - } + maneuverLossInfoOverlayMap.put(key, maneuverLossInfoOverlay); } Widget getInfoWindowContent(ManeuverDTO maneuver, CompetitorDTO competitor) { @@ -384,16 +366,14 @@ public class ManeuverMarkersAndLossIndicators { numberFormatOneDecimal.format(maneuver.getManeuverLoss().getDistanceLost().getMeters()) + " " + stringMessages.metersUnit()); CheckBox maneuverLossLinesCheckBox = new CheckBox(stringMessages.show()); Triple t = createManeuverKey(maneuver, competitor); - maneuverLossLinesCheckBox.setValue(maneuverLossCheckBoxValueStore.contains(t)); + maneuverLossLinesCheckBox.setValue(maneuverLossLinesMap.containsKey(t)); maneuverLossLinesCheckBox.addValueChangeHandler(new ValueChangeHandler() { @Override public void onValueChange(ValueChangeEvent event) { if (event.getValue()) { createManeuverLossLinesAndInfoOverlays(maneuver, competitor); - maneuverLossCheckBoxValueStore.add(t); } else { removeManeuverLossLinesAndInfoOverlayForManeuver(maneuver, competitor); - maneuverLossCheckBoxValueStore.remove(t); } } }); @@ -418,7 +398,6 @@ public class ManeuverMarkersAndLossIndicators { // TODO is it really necessary to remove them all first...? if (lastManeuverResult != null) { removeAllManeuverMarkers(); - removeAllManeuverLossLinesAndInfoOverlays(); showManeuvers(lastManeuverResult); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 91b18c91cfd..f384f894216 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -1062,7 +1062,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); } - maneuverMarkersAndLossIndicators.removeManeuverMarkers(); + maneuverMarkersAndLossIndicators.removeAllManeuverMarkers(); if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } From 59a3431d5d63553ffe62b7300ef8488ffb682d10 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Wed, 18 Jul 2018 15:24:42 +0200 Subject: [PATCH 53/53] fixed bug regarding erasing last maneuvers received from server Change-Id: I3288685cc0e440a9d9aff16ab15ae1161c246e1b --- .../ManeuverMarkersAndLossIndicators.java | 25 ++++++++++++++++--- .../gwt/ui/client/shared/racemap/RaceMap.java | 3 +-- 2 files changed, 23 insertions(+), 5 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java index 29778534560..c9e99a4ab70 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/ManeuverMarkersAndLossIndicators.java @@ -118,7 +118,12 @@ public class ManeuverMarkersAndLossIndicators { }); } - protected void removeAllManeuverMarkers() { + /** + * Removes the visualizations of maneuvers from the map. The maneuver objects as received from the server are remembered + * (see {@link #lastManeuverResult}) and may be restored by a subsequent call to {@link #showManeuvers(Map)} as it is, e.g., + * called by {@link #updateManeuverMarkersAfterSettingsChanged()}. + */ + void removeAllManeuverMarkers() { // clone entry set to avoid concurrent modification exception for (final Entry, Marker> keyAndMarker : new HashSet<>(maneuverMarkers.entrySet())) { removeManeuverMarker(keyAndMarker.getKey(), keyAndMarker.getValue()); @@ -257,8 +262,9 @@ public class ManeuverMarkersAndLossIndicators { } maneuverLossLinesMap.remove(key); } - if (maneuverLossInfoOverlayMap.remove(key) != null) { - maneuverLossInfoOverlayMap.get(key).removeFromMap(); + final SmallTransparentInfoOverlay overlay; + if ((overlay=maneuverLossInfoOverlayMap.remove(key)) != null) { + overlay.removeFromMap(); } } @@ -401,4 +407,17 @@ public class ManeuverMarkersAndLossIndicators { showManeuvers(lastManeuverResult); } } + + /** + * Removes all maneuver displays from the map and also clears the structure holding the maneuvers + * last received from the server so that upon the next request to show maneuvers without a previous + * update from the server no maneuvers will be displayed.

+ * + * @see #getAndShowManeuvers(RegattaAndRaceIdentifier, Map) + * @see #removeAllManeuverMarkers() + */ + void clearAllManeuverMarkers() { + removeAllManeuverMarkers(); + lastManeuverResult = null; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index f384f894216..814aac67e3f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -1062,7 +1062,7 @@ public class RaceMap extends AbstractCompositeComponent impleme if (douglasMarkers != null) { removeAllMarkDouglasPeuckerpoints(); } - maneuverMarkersAndLossIndicators.removeAllManeuverMarkers(); + maneuverMarkersAndLossIndicators.clearAllManeuverMarkers(); if (requiresCoordinateSystemUpdateWhenCoursePositionAndWindDirectionIsKnown) { updateCoordinateSystemFromSettings(); } @@ -1093,7 +1093,6 @@ public class RaceMap extends AbstractCompositeComponent impleme } zoomMapToNewBounds(zoomToBounds); mapFirstZoomDone = true; - updateEstimatedDuration(raceMapDataDTO.estimatedDuration); } } else {