diff --git a/java/com.googlecode.mgwt/.settings/org.eclipse.jdt.core.prefs b/java/com.googlecode.mgwt/.settings/org.eclipse.jdt.core.prefs index 77515b0909c..0d5ebab473f 100755 --- a/java/com.googlecode.mgwt/.settings/org.eclipse.jdt.core.prefs +++ b/java/com.googlecode.mgwt/.settings/org.eclipse.jdt.core.prefs @@ -1,4 +1,4 @@ -<<<<<<< HEAD:java/com.googlecode.mgwt/.settings/org.eclipse.jdt.core.prefs +<<<<<<< HEAD:java/com.googlecode.mgwt/.settings/org.eclipse.jdt.core.prefs #Thu Jan 19 09:46:08 CET 2012 eclipse.preferences.version=1 org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled @@ -7,25 +7,25 @@ org.eclipse.jdt.core.compiler.compliance=1.7 org.eclipse.jdt.core.compiler.problem.assertIdentifier=error org.eclipse.jdt.core.compiler.problem.enumIdentifier=error org.eclipse.jdt.core.compiler.source=1.7 -||||||| merged common ancestors -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 -org.eclipse.jdt.core.compiler.compliance=1.7 -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.7 -======= -eclipse.preferences.version=1 -org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled -org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate -org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 -org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve -org.eclipse.jdt.core.compiler.compliance=1.8 -org.eclipse.jdt.core.compiler.debug.lineNumber=generate -org.eclipse.jdt.core.compiler.debug.localVariable=generate -org.eclipse.jdt.core.compiler.debug.sourceFile=generate -org.eclipse.jdt.core.compiler.problem.assertIdentifier=error -org.eclipse.jdt.core.compiler.problem.enumIdentifier=error -org.eclipse.jdt.core.compiler.source=1.8 ->>>>>>> origin/master:java/com.sap.sailing.datamining.test/.settings/org.eclipse.jdt.core.prefs +||||||| merged common ancestors +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.7 +org.eclipse.jdt.core.compiler.compliance=1.7 +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.7 +======= +eclipse.preferences.version=1 +org.eclipse.jdt.core.compiler.codegen.inlineJsrBytecode=enabled +org.eclipse.jdt.core.compiler.codegen.methodParameters=do not generate +org.eclipse.jdt.core.compiler.codegen.targetPlatform=1.8 +org.eclipse.jdt.core.compiler.codegen.unusedLocal=preserve +org.eclipse.jdt.core.compiler.compliance=1.8 +org.eclipse.jdt.core.compiler.debug.lineNumber=generate +org.eclipse.jdt.core.compiler.debug.localVariable=generate +org.eclipse.jdt.core.compiler.debug.sourceFile=generate +org.eclipse.jdt.core.compiler.problem.assertIdentifier=error +org.eclipse.jdt.core.compiler.problem.enumIdentifier=error +org.eclipse.jdt.core.compiler.source=1.8 +>>>>>>> origin/master:java/com.sap.sailing.datamining.test/.settings/org.eclipse.jdt.core.prefs diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java index c357e8dbdf1..cc8c7806dac 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/DetailType.java @@ -24,9 +24,9 @@ public enum DetailType implements Serializable { AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING), AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING), RACE_DISTANCE_TRAVELED(0, ASCENDING), RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START(0, ASCENDING), RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS(2, DESCENDING), RACE_GAP_TO_LEADER_IN_SECONDS(0, ASCENDING), - RACE_DISTANCE_TO_LEADER_IN_METERS(0, ASCENDING), RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING), + RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS(0, ASCENDING), RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING), RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS(0, ASCENDING), - WINDWARD_DISTANCE_TO_OVERALL_LEADER(0, ASCENDING), START_TACK(0, ASCENDING), DISTANCE_TO_START_AT_RACE_START(1, ASCENDING), + WINDWARD_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD(0, ASCENDING), START_TACK(0, ASCENDING), DISTANCE_TO_START_AT_RACE_START(1, ASCENDING), TIME_BETWEEN_RACE_START_AND_COMPETITOR_START(1, ASCENDING), SPEED_OVER_GROUND_AT_RACE_START(1, DESCENDING), SPEED_OVER_GROUND_WHEN_PASSING_START(1, DESCENDING), DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS(1, ASCENDING), @@ -43,7 +43,7 @@ public enum DetailType implements Serializable { RACE_TOTAL_POINTS(2, ASCENDING), REGATTA_TOTAL_POINTS(2, ASCENDING), REGATTA_TOTAL_POINTS_SUM(2, ASCENDING), RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL(1, ASCENDING), RACE_DISTANCE_TO_START_FIVE_SECONDS_BEFORE_RACE_START(1, ASCENDING), RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START(2, DESCENDING), - RACE_CORRECTED_TIME_TRAVELED(0, ASCENDING), RACE_CORRECTED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD(0, ASCENDING), + RACE_CALCULATED_TIME_TRAVELED(0, ASCENDING), RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD(0, ASCENDING), RACE_TIME_TRAVELED(0, ASCENDING), RACE_TIME_TRAVELED_UPWIND(0, ASCENDING), RACE_TIME_TRAVELED_DOWNWIND(0, ASCENDING), RACE_TIME_TRAVELED_REACHING(0, ASCENDING), DISTANCE_TO_START_LINE(0, ASCENDING), BEAT_ANGLE(0, ASCENDING), COURSE_OVER_GROUND_TRUE_DEGREES(0, ASCENDING); diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java index 1e846807219..7df1e2403a2 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/dto/LeaderboardEntryDTO.java @@ -42,7 +42,7 @@ public class LeaderboardEntryDTO implements Serializable { public boolean discarded; - public Double windwardDistanceToOverallLeaderInMeters; + public Double windwardDistanceToCompetitorFarthestAheadInMeters; public Double averageAbsoluteCrossTrackErrorInMeters; @@ -104,14 +104,14 @@ public class LeaderboardEntryDTO implements Serializable { * The corrected time spent in the race; usually based on the current time and distance, calculated by the {@link RankingMetric}. * For one-design classes this equals the time spent in the race. */ - public Duration correctedTime; + public Duration calculatedTime; /** * The corrections applied to the time and distance sailed when the competitor would have reached the * competitor farthest ahead, based on average VMG on the current leg and equal performance to the boat * farthest ahead for all subsequent legs. */ - public Duration correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead; + public Duration calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead; /** * If null, no leg details are known yet, the race is not being tracked or the details @@ -142,9 +142,9 @@ public class LeaderboardEntryDTO implements Serializable { result = prime * result + ((gapToLeaderInOwnTime == null) ? 0 : gapToLeaderInOwnTime.hashCode()); result = prime * result - + ((correctedTime == null) ? 0 : correctedTime.hashCode()); + + ((calculatedTime == null) ? 0 : calculatedTime.hashCode()); result = prime * result - + ((correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead == null) ? 0 : correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead.hashCode()); + + ((calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead == null) ? 0 : calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead.hashCode()); result = prime * result + ((averageSignedCrossTrackErrorInMeters == null) ? 0 : averageSignedCrossTrackErrorInMeters.hashCode()); result = prime * result + (discarded ? 1231 : 1237); @@ -179,7 +179,7 @@ public class LeaderboardEntryDTO implements Serializable { result = prime * result + ((totalPoints == null) ? 0 : totalPoints.hashCode()); result = prime * result - + ((windwardDistanceToOverallLeaderInMeters == null) ? 0 : windwardDistanceToOverallLeaderInMeters + + ((windwardDistanceToCompetitorFarthestAheadInMeters == null) ? 0 : windwardDistanceToCompetitorFarthestAheadInMeters .hashCode()); return result; } @@ -198,15 +198,15 @@ public class LeaderboardEntryDTO implements Serializable { return false; } else if (!gapToLeaderInOwnTime.equals(other.gapToLeaderInOwnTime)) return false; - if (correctedTime == null) { - if (other.correctedTime != null) + if (calculatedTime == null) { + if (other.calculatedTime != null) return false; - } else if (!correctedTime.equals(other.correctedTime)) + } else if (!calculatedTime.equals(other.calculatedTime)) return false; - if (correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead == null) { - if (other.correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead != null) + if (calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead == null) { + if (other.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead != null) return false; - } else if (!correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead.equals(other.correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead)) + } else if (!calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead.equals(other.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead)) return false; if (averageAbsoluteCrossTrackErrorInMeters == null) { if (other.averageAbsoluteCrossTrackErrorInMeters != null) @@ -297,10 +297,10 @@ public class LeaderboardEntryDTO implements Serializable { return false; } else if (!totalPoints.equals(other.totalPoints)) return false; - if (windwardDistanceToOverallLeaderInMeters == null) { - if (other.windwardDistanceToOverallLeaderInMeters != null) + if (windwardDistanceToCompetitorFarthestAheadInMeters == null) { + if (other.windwardDistanceToCompetitorFarthestAheadInMeters != null) return false; - } else if (!windwardDistanceToOverallLeaderInMeters.equals(other.windwardDistanceToOverallLeaderInMeters)) + } else if (!windwardDistanceToCompetitorFarthestAheadInMeters.equals(other.windwardDistanceToCompetitorFarthestAheadInMeters)) return false; return true; } diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/ImageSizeImpl.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/ImageSizeImpl.java index 1c6ef6246d1..fb15291dca8 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/ImageSizeImpl.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/impl/ImageSizeImpl.java @@ -1,6 +1,6 @@ package com.sap.sailing.domain.common.impl; -import com.sap.sailing.domain.common.ImageSize; +import com.sap.sse.common.media.ImageSize; public class ImageSizeImpl implements ImageSize { private static final long serialVersionUID = 1170701774852068780L; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaCategory.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaCategory.java deleted file mode 100644 index 1419e5bd7c4..00000000000 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaCategory.java +++ /dev/null @@ -1,5 +0,0 @@ -package com.sap.sailing.domain.common.media; - -public enum MediaCategory { - highlight, livestream; -} \ No newline at end of file diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaTrack.java b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaTrack.java index f630c43596c..74c855dcba9 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaTrack.java +++ b/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaTrack.java @@ -8,6 +8,8 @@ import java.util.Set; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.media.MediaSubType; +import com.sap.sse.common.media.MimeType; /** * See http://my.opera.com/core/blog/2010/03/03/everything-you-need-to-know-about-html5-video-and-audio-2 diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java index 284322a59d1..93b272be5e2 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/MediaDB.java @@ -6,9 +6,9 @@ import java.util.Set; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.media.MimeType; /** * Offers CRUD methods for mongo representation of media track objects. @@ -17,7 +17,7 @@ import com.sap.sse.common.TimePoint; * */ public interface MediaDB { - + /** * Stores a new track to the database, returning the db-generated id. */ @@ -40,7 +40,7 @@ public interface MediaDB { TimePoint startTime, Duration duration, MimeType mimeType, Set regattaAndRace); - List loadAllMediaTracks(); + List loadAllMediaTracks(); void deleteMediaTrack(String dbId); @@ -53,7 +53,7 @@ public interface MediaDB { void updateDuration(String dbId, Duration duration); void updateRace(String dbId, Set race); - + MediaDB TEST_STUB = new MediaDB() { @Override @@ -61,7 +61,7 @@ public interface MediaDB { TimePoint startTime, Duration duration, MimeType mimeType, Set assignedRaces) { return "0"; - } + } @Override public void insertMediaTrackWithId(String dbId, String videoTitle, diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java index 664c90cfbfd..c06e4f1162d 100644 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/media/impl/MediaDBImpl.java @@ -19,12 +19,12 @@ import com.mongodb.WriteConcern; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.domain.persistence.media.MediaDB; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MimeType; /** * MongoDB Java API examples: http://blog.rasc.ch/?p=1096 @@ -121,8 +121,8 @@ public class MediaDBImpl implements MediaDB { String dbId = ((ObjectId) dbObject.get(DbNames.Fields._id.name())).toHexString(); String title = (String) dbObject.get(DbNames.Fields.MEDIA_TITLE.name()); String url = (String) dbObject.get(DbNames.Fields.MEDIA_URL.name()); - Date startTime = (Date) dbObject.get(DbNames.Fields.STARTTIME.name()); - Number duration = (Number) dbObject.get(DbNames.Fields.DURATION_IN_MILLIS.name()); + Date startTime = (Date) dbObject.get(DbNames.Fields.STARTTIME.name()); + Number duration = (Number) dbObject.get(DbNames.Fields.DURATION_IN_MILLIS.name()); String mimeTypeText = (String) dbObject.get(DbNames.Fields.MIME_TYPE.name()); MimeType mimeType = MimeType.byName(mimeTypeText); Set assignedRaces = new HashSet(); diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/EventBase.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/EventBase.java index 0e4ac88213d..ec8dd8ea3b1 100644 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/EventBase.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/EventBase.java @@ -3,11 +3,11 @@ package com.sap.sailing.domain.base; import java.net.URL; import java.util.concurrent.ExecutionException; -import com.sap.sailing.domain.common.ImageSize; import com.sap.sailing.domain.common.Renamable; import com.sap.sse.common.Named; import com.sap.sse.common.TimePoint; import com.sap.sse.common.WithID; +import com.sap.sse.common.media.ImageSize; /** * Base interface for an Event consisting of all static information, which might be shared diff --git a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/StrippedEventImpl.java b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/StrippedEventImpl.java index 2c4332ab02c..a5b88e9b506 100755 --- a/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/StrippedEventImpl.java +++ b/java/com.sap.sailing.domain.shared.android/src/com/sap/sailing/domain/base/impl/StrippedEventImpl.java @@ -9,8 +9,8 @@ import java.util.concurrent.ExecutionException; import com.sap.sailing.domain.base.EventBase; import com.sap.sailing.domain.base.LeaderboardGroupBase; import com.sap.sailing.domain.base.Venue; -import com.sap.sailing.domain.common.ImageSize; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.media.ImageSize; /** * A simplified implementation of the {@link EventBase} interface which maintains an immutable collection of diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java index ae69c51bc6f..87040eed61f 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/ranking/TestSimpleTimeOnTimeRankingWithOneUpwindLeg.java @@ -116,6 +116,12 @@ public class TestSimpleTimeOnTimeRankingWithOneUpwindLeg { c2, Arrays. asList(new MarkPassingImpl(startOfRace, trackedRace.getRace().getCourse() .getFirstWaypoint(), c2))); + trackedRace.getTrack(c1).add( + new GPSFixMovingImpl(new DegreePosition(0.0, 0), startOfRace, new KnotSpeedWithBearingImpl(12, + new DegreeBearingImpl(45)))); + trackedRace.getTrack(c2).add( + new GPSFixMovingImpl(new DegreePosition(0.0, 0), startOfRace, new KnotSpeedWithBearingImpl(12, + new DegreeBearingImpl(315)))); trackedRace.getTrack(c1).add( new GPSFixMovingImpl(new DegreePosition(0.5, 0), middleOfFirstLeg, new KnotSpeedWithBearingImpl(12, new DegreeBearingImpl(45)))); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CalculateImageSizeFromUrlTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CalculateImageSizeFromUrlTest.java index 920d42c7573..0a4e994af77 100644 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CalculateImageSizeFromUrlTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/CalculateImageSizeFromUrlTest.java @@ -19,8 +19,8 @@ import org.junit.Test; import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.impl.EventImpl; -import com.sap.sailing.domain.common.ImageSize; import com.sap.sailing.domain.common.impl.ImageSizeImpl; +import com.sap.sse.common.media.ImageSize; public class CalculateImageSizeFromUrlTest { diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DirectGapCalculationVersusOneDesignRankingMetricTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DirectGapCalculationVersusOneDesignRankingMetricTest.java index f19e05fef5e..c3e6b4adfab 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DirectGapCalculationVersusOneDesignRankingMetricTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/DirectGapCalculationVersusOneDesignRankingMetricTest.java @@ -23,6 +23,7 @@ import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; import com.sap.sailing.domain.ranking.OneDesignRankingMetric; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; import com.sap.sailing.domain.tracking.WindPositionMode; import com.sap.sailing.domain.tractracadapter.ReceiverType; @@ -65,7 +66,7 @@ public class DirectGapCalculationVersusOneDesignRankingMetricTest extends Abstra /** * Asserts that Philipp Buhl is having equal gap value, regardless whether calculated using the traditional - * {@link TrackedLegOfCompetitor#getGapToLeader(TimePoint, WindPositionMode)} method or the + * {@link TrackedLegOfCompetitor#getGapToLeader(TimePoint, RankingInfo, WindPositionMode)} method or the * {@link OneDesignRankingMetric#getGapToLeaderInOwnTime(Competitor, TimePoint)} method. */ @Test @@ -74,7 +75,8 @@ public class DirectGapCalculationVersusOneDesignRankingMetricTest extends Abstra TimePoint timePoint = getTrackedRace().getStartOfRace(); for (int i=1; i<10; i++) { timePoint = timePoint.plus(Duration.ONE_MINUTE); - Duration classicGap = getTrackedRace().getTrackedLeg(buhli, timePoint).getGapToLeader(timePoint, WindPositionMode.LEG_MIDDLE); + Duration classicGap = getTrackedRace().getTrackedLeg(buhli, timePoint).getGapToLeader(timePoint, + getTrackedRace().getRankingMetric().getRankingInfo(timePoint), WindPositionMode.LEG_MIDDLE); Duration rankingMetricGap = getTrackedRace().getRankingMetric().getGapToLeaderInOwnTime(buhli, timePoint); assertEquals("At "+i+" minutes into the race ("+timePoint+"): ", classicGap.asSeconds(), rankingMetricGap.asSeconds(), 0.000001); } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java index 84245a09f93..483614af2a2 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/ReachingLegTest.java @@ -194,8 +194,8 @@ public class ReachingLegTest extends TrackBasedTest { assertEquals(0., getTrackedRace().getTrack(plattner).getEstimatedPosition(timePointInReaching, /* extrapolate */false) .getDistance(windwardMarkPos).getMeters(), 0.00001); - Distance hungersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(hunger, timePointInReaching, WindPositionMode.LEG_MIDDLE); - Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(plattner, timePointInReaching, WindPositionMode.LEG_MIDDLE); + Distance hungersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(hunger, timePointInReaching, WindPositionMode.LEG_MIDDLE); + Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointInReaching, WindPositionMode.LEG_MIDDLE); assertEquals(0., hungersDistanceToLeader.getMeters(), 0.00001); assertEquals(distanceOfReachingLeg.getMeters(), plattnersDistanceToLeader.getMeters(), 0.001); } @@ -224,8 +224,8 @@ public class ReachingLegTest extends TrackBasedTest { assertEquals(0., getTrackedRace().getTrack(plattner).getEstimatedPosition(timePointInReaching, /* extrapolate */false) .getDistance(windwardMarkPos).getMeters(), 0.00001); - Distance hungersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(hunger, timePointInReaching, WindPositionMode.LEG_MIDDLE); - Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(plattner, timePointInReaching, WindPositionMode.LEG_MIDDLE); + Distance hungersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(hunger, timePointInReaching, WindPositionMode.LEG_MIDDLE); + Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointInReaching, WindPositionMode.LEG_MIDDLE); assertEquals(0., hungersDistanceToLeader.getMeters(), 0.00001); assertEquals(distanceOfReachingLeg.getMeters(), plattnersDistanceToLeader.getMeters(), 0.001); } @@ -254,8 +254,8 @@ public class ReachingLegTest extends TrackBasedTest { assertEquals(0., getTrackedRace().getTrack(plattner).getEstimatedPosition(timePointInReaching, /* extrapolate */false) .getDistance(windwardMarkPos).getMeters(), 0.00001); - Distance hungersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(hunger, timePointInReaching, WindPositionMode.LEG_MIDDLE); - Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(plattner, timePointInReaching, WindPositionMode.LEG_MIDDLE); + Distance hungersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(hunger, timePointInReaching, WindPositionMode.LEG_MIDDLE); + Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointInReaching, WindPositionMode.LEG_MIDDLE); assertEquals(0., hungersDistanceToLeader.getMeters(), 0.00001); assertEquals(distanceOfReachingLeg.getMeters(), plattnersDistanceToLeader.getMeters(), 0.001); } @@ -289,8 +289,8 @@ public class ReachingLegTest extends TrackBasedTest { assertEquals(0., getTrackedRace().getTrack(plattner).getEstimatedPosition(timePointToConsider, /* extrapolate */false) .getDistance(leewardPos).getMeters(), 0.00001); - Distance hungersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(hunger, timePointToConsider, WindPositionMode.LEG_MIDDLE); - Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE); + Distance hungersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(hunger, timePointToConsider, WindPositionMode.LEG_MIDDLE); + Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE); assertEquals(0., hungersDistanceToLeader.getMeters(), 0.00001); assertEquals(distanceOfReachingLeg.getMeters()+windwardDistanceOfUpwindLeg.getMeters(), plattnersDistanceToLeader.getMeters(), 0.001); } @@ -333,8 +333,8 @@ public class ReachingLegTest extends TrackBasedTest { assertEquals(0., getTrackedRace().getTrack(plattner).getEstimatedPosition(timePointToConsider, /* extrapolate */false) .getDistance(leewardPos).getMeters(), 0.00001); - Distance schomaekersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(schomaeker, timePointToConsider, WindPositionMode.LEG_MIDDLE); - Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE); + Distance schomaekersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(schomaeker, timePointToConsider, WindPositionMode.LEG_MIDDLE); + Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE); assertEquals(0., schomaekersDistanceToLeader.getMeters(), 0.00001); // distance to leading Schomaeker expected to be the entire upwind distance plus the offset distance plus half the downwind assertEquals(distanceOfReachingLeg.getMeters()+windwardDistanceOfUpwindLeg.getMeters()*1.5, plattnersDistanceToLeader.getMeters(), 0.001); @@ -374,7 +374,7 @@ public class ReachingLegTest extends TrackBasedTest { getTrackedRace().getTrack(plattner).getEstimatedPosition(timePointToConsider, /* extrapolate */ false)).getMeters(), 0.00001); // however, projected onto the leg their distance should be 0 - Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToOverallLeader(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE); + Distance plattnersDistanceToLeader = getTrackedRace().getWindwardDistanceToCompetitorFarthestAhead(plattner, timePointToConsider, WindPositionMode.LEG_MIDDLE); assertEquals(0., plattnersDistanceToLeader.getMeters(), 0.00001); } } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnConstructedTracksTest.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnConstructedTracksTest.java index bec8592bcdb..12035de5908 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnConstructedTracksTest.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/WindEstimationOnConstructedTracksTest.java @@ -239,9 +239,9 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest assertNotNull(estimatedWindDirectionCached); assertEquals(185., estimatedWindDirectionCached.getBearing().getDegrees(), 0.00000001); // now add a GPS fix and make sure the cache is invalidated by adding it inside the averaging interval - now = new MillisecondsTimePoint(checkTime.asMillis() + track.getMillisecondsOverWhichToAverageWind()/2); - setBearingForCompetitor(competitors.get(0), now, 330); - setBearingForCompetitor(competitors.get(0), now, 310); + now = new MillisecondsTimePoint(checkTime.asMillis() + track.getMillisecondsOverWhichToAverageWind()/2); + setBearingForCompetitor(competitors.get(0), now, 330); + setBearingForCompetitor(competitors.get(0), now, 310); Wind estimatedWindDirectionNew = track.getAveragedWind(/* position */ null, checkTime); assertFalse(cachedFixes.isEmpty()); assertNotNull(estimatedWindDirectionNew); diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java index ec4d14c8127..a84e8d3c8b2 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRace.java @@ -44,6 +44,7 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.ranking.RankingMetric; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.ranking.RankingMetricConstructor; import com.sap.sailing.domain.regattalike.RegattaLikeIdentifier; import com.sap.sailing.domain.regattalike.RegattaLikeListener; @@ -506,6 +507,22 @@ public class MockedTrackedRace implements DynamicTrackedRace { DynamicRaceDefinitionSet raceDefinitionSetToUpdate, boolean useMarkPassingcalculator) { return null; } + + @Override + public void lockTrackedRacesForRead() { + } + + @Override + public void unlockTrackedRacesAfterRead() { + } + + @Override + public void lockTrackedRacesForWrite() { + } + + @Override + public void unlockTrackedRacesAfterWrite() { + } }; } @@ -564,7 +581,7 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { return null; } @@ -935,14 +952,8 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public Distance getCorrectedWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode) throws NoWindException { - return null; - } - - @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, + WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { return null; } diff --git a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java index 9489add3d10..fc669816f33 100755 --- a/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java +++ b/java/com.sap.sailing.domain.test/src/com/sap/sailing/domain/test/mock/MockedTrackedRaceWithStartTimeAndRanks.java @@ -35,6 +35,7 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.ranking.RankingMetric; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.CourseDesignChangedListener; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.LineDetails; @@ -345,7 +346,7 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { } @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { return null; } @@ -611,14 +612,8 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace { } @Override - public Distance getCorrectedWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode) throws NoWindException { - return null; - } - - @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, + WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { return null; } diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java index 4d5115509a9..c0e19ce6f7a 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/DomainFactoryImpl.java @@ -494,9 +494,17 @@ public class DomainFactoryImpl implements DomainFactory { TrackedRegatta trackedRegatta = trackedRegattaRegistry.getTrackedRegatta(regatta); if (trackedRegatta != null) { // see above; only remove tracked regatta if it *became* empty because of the tracked race removal here - int oldSizeOfTrackedRaces = Util.size(trackedRegatta.getTrackedRaces()); - trackedRegatta.removeTrackedRace(raceDefinition); - if (oldSizeOfTrackedRaces > 0 && Util.size(trackedRegatta.getTrackedRaces()) == 0) { + final int oldSizeOfTrackedRaces; + final int newSizeOfTrackedRaces; + trackedRegatta.lockTrackedRacesForWrite(); + try { + oldSizeOfTrackedRaces = Util.size(trackedRegatta.getTrackedRaces()); + trackedRegatta.removeTrackedRace(raceDefinition); + newSizeOfTrackedRaces = Util.size(trackedRegatta.getTrackedRaces()); + } finally { + trackedRegatta.unlockTrackedRacesAfterWrite(); + } + if (oldSizeOfTrackedRaces > 0 && newSizeOfTrackedRaces == 0) { trackedRegattaRegistry.removeTrackedRegatta(regatta); } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Event.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Event.java index 1abf52edd38..fb25d1c77c3 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Event.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/Event.java @@ -4,8 +4,8 @@ import java.net.URL; import java.util.UUID; import java.util.concurrent.ExecutionException; -import com.sap.sailing.domain.common.ImageSize; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; +import com.sap.sse.common.media.ImageSize; /** * An event is a group of {@link Regatta regattas} carried out at a common venue within a common time frame. For diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java index 321fc4b5026..19d1fa73ce0 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/base/impl/EventImpl.java @@ -26,11 +26,11 @@ import javax.imageio.stream.ImageInputStream; import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Venue; -import com.sap.sailing.domain.common.ImageSize; import com.sap.sailing.domain.common.impl.ImageSizeImpl; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; +import com.sap.sse.common.media.ImageSize; public class EventImpl extends EventBaseImpl implements Event { private static final long serialVersionUID = 855135446595485715L; diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardGroup.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardGroup.java index 502a3c78437..5d030461e04 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardGroup.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/LeaderboardGroup.java @@ -28,7 +28,12 @@ public interface LeaderboardGroup extends LeaderboardGroupBase { void addLeaderboardGroupListener(LeaderboardGroupListener listener); void removeLeaderboardGroupListener(LeaderboardGroupListener listener); boolean isDisplayGroupsInReverseOrder(); + + /** + * @return a non-live copy of the leaderboard list as it looked when the method was called + */ Iterable getLeaderboards(); + int getIndexOf(Leaderboard leaderboard); void addLeaderboard(Leaderboard leaderboard); void addLeaderboardAt(Leaderboard leaderboard, int index); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java index 9a97fcb5f33..9f545a222a3 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/AbstractSimpleLeaderboardImpl.java @@ -1392,15 +1392,15 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race RaceDetails raceDetails = getRaceDetails(trackedRace, competitor, timePoint, waitForLatestAnalyses, legRanksCache, rankingInfo, cache); entryDTO.legDetails = raceDetails.getLegDetails(); - entryDTO.windwardDistanceToOverallLeaderInMeters = raceDetails.getWindwardDistanceToOverallLeader() == null ? null - : raceDetails.getWindwardDistanceToOverallLeader().getMeters(); + entryDTO.windwardDistanceToCompetitorFarthestAheadInMeters = raceDetails.getWindwardDistanceToCompetitorFarthestAhead() == null ? null + : raceDetails.getWindwardDistanceToCompetitorFarthestAhead().getMeters(); entryDTO.gapToLeaderInOwnTime = trackedRace.getRankingMetric().getGapToLeaderInOwnTime(rankingInfo, competitor, cache); entryDTO.averageAbsoluteCrossTrackErrorInMeters = raceDetails.getAverageAbsoluteCrossTrackError() == null ? null : raceDetails.getAverageAbsoluteCrossTrackError().getMeters(); entryDTO.averageSignedCrossTrackErrorInMeters = raceDetails.getAverageSignedCrossTrackError() == null ? null : raceDetails.getAverageSignedCrossTrackError().getMeters(); - entryDTO.correctedTime = raceDetails.getCorrectedTime(); - entryDTO.correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead = raceDetails.getCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAhead(); + entryDTO.calculatedTime = raceDetails.getCorrectedTime(); + entryDTO.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead = raceDetails.getCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAhead(); entryDTO.gapToLeaderInOwnTime = raceDetails.getGapToLeaderInOwnTime(); final TimePoint startOfRace = trackedRace.getStartOfRace(); if (startOfRace != null) { @@ -1481,20 +1481,20 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race private static class RaceDetails { private final List legDetails; - private final Distance windwardDistanceToOverallLeader; + private final Distance windwardDistanceToCompetitorFarthestAhead; private final Distance averageAbsoluteCrossTrackError; private final Distance averageSignedCrossTrackError; private final Duration gapToLeaderInOwnTime; private final Duration correctedTime; private final Duration correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead; - public RaceDetails(List legDetails, Distance windwardDistanceToOverallLeader, + public RaceDetails(List legDetails, Distance windwardDistanceToCompetitorFarthestAhead, Distance averageAbsoluteCrossTrackError, Distance averageSignedCrossTrackError, Duration gapToLeaderInOwnTime, Duration correctedTime, Duration correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead) { super(); this.legDetails = legDetails; - this.windwardDistanceToOverallLeader = windwardDistanceToOverallLeader; + this.windwardDistanceToCompetitorFarthestAhead = windwardDistanceToCompetitorFarthestAhead; this.averageAbsoluteCrossTrackError = averageAbsoluteCrossTrackError; this.averageSignedCrossTrackError = averageSignedCrossTrackError; this.gapToLeaderInOwnTime = gapToLeaderInOwnTime; @@ -1504,8 +1504,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race public List getLegDetails() { return legDetails; } - public Distance getWindwardDistanceToOverallLeader() { - return windwardDistanceToOverallLeader; + public Distance getWindwardDistanceToCompetitorFarthestAhead() { + return windwardDistanceToCompetitorFarthestAhead; } public Distance getAverageAbsoluteCrossTrackError() { return averageAbsoluteCrossTrackError; @@ -1581,7 +1581,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race private RaceDetails calculateRaceDetails(TrackedRace trackedRace, Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalyses, Map> legRanksCache, - WindLegTypeAndLegBearingCache cache, RankingInfo rankingInfo) throws NoWindException { + WindLegTypeAndLegBearingCache cache, final RankingInfo rankingInfo) throws NoWindException { final List legDetails = new ArrayList(); final Course course = trackedRace.getRace().getCourse(); course.lockForRead(); // hold back any course re-configurations while looping over the legs @@ -1599,8 +1599,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race } legDetails.add(legEntry); } - final Distance windwardDistanceToOverallLeader = trackedRace == null ? null : trackedRace - .getWindwardDistanceToOverallLeader(competitor, timePoint, WindPositionMode.LEG_MIDDLE, cache); + final Distance windwardDistanceToCompetitorFarthestAhead = trackedRace == null ? null : trackedRace + .getWindwardDistanceToCompetitorFarthestAhead(competitor, timePoint, WindPositionMode.LEG_MIDDLE, rankingInfo, cache); Distance averageAbsoluteCrossTrackError; try { averageAbsoluteCrossTrackError = trackedRace == null ? null : trackedRace.getAverageAbsoluteCrossTrackError( @@ -1617,7 +1617,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race // without wind information, use null meaning "unknown" averageSignedCrossTrackError = null; } - return new RaceDetails(legDetails, windwardDistanceToOverallLeader, averageAbsoluteCrossTrackError, averageSignedCrossTrackError, + return new RaceDetails(legDetails, windwardDistanceToCompetitorFarthestAhead, averageAbsoluteCrossTrackError, averageSignedCrossTrackError, trackedRace.getRankingMetric().getGapToLeaderInOwnTime(rankingInfo, competitor, cache), trackedRace.getRankingMetric().getCorrectedTime(competitor, timePoint), rankingInfo.getCompetitorRankingInfo().apply(competitor).getCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAhead()); @@ -1678,10 +1678,10 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race trackedLeg.hasFinishedLeg(timePoint) ? trackedLeg.getFinishTime() : timePoint, cache) : null; // fetch the leg gap in own corrected time from the ranking metric final Duration gapToLeaderInOwnTime = trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric(). - getLegGapToLegLeaderInOwnTime(trackedLeg, timePoint, cache); + getLegGapToLegLeaderInOwnTime(trackedLeg, timePoint, rankingInfo, cache); result.gapToLeaderInSeconds = gapToLeaderInOwnTime == null ? null : gapToLeaderInOwnTime.asSeconds(); if (result.gapToLeaderInSeconds != null) { - final Duration gapAtEndOfPreviousLeg = getGapAtEndOfPreviousLeg(trackedLeg, cache); + final Duration gapAtEndOfPreviousLeg = getGapAtEndOfPreviousLeg(trackedLeg, rankingInfo, cache); if (gapAtEndOfPreviousLeg != null) { result.gapChangeSinceLegStartInSeconds = result.gapToLeaderInSeconds - gapAtEndOfPreviousLeg.asSeconds(); } @@ -1766,7 +1766,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race return result; } - private Duration getGapAtEndOfPreviousLeg(TrackedLegOfCompetitor trackedLeg, WindLegTypeAndLegBearingCache cache) { + private Duration getGapAtEndOfPreviousLeg(TrackedLegOfCompetitor trackedLeg, final RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { final Duration result; final Course course = trackedLeg.getTrackedLeg().getTrackedRace().getRace().getCourse(); // if trackedLeg is the first leg, compute the gap at the start of this leg; otherwise, compute gap @@ -1779,7 +1779,7 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race tloc = trackedLeg.getTrackedLeg().getTrackedRace().getTrackedLegFinishingAt(trackedLeg.getLeg().getFrom()) .getTrackedLeg(trackedLeg.getCompetitor()); } - result = trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric().getLegGapToLegLeaderInOwnTime(tloc, timePoint, cache); + result = trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric().getLegGapToLegLeaderInOwnTime(tloc, timePoint, rankingInfo, cache); return result; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardGroupImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardGroupImpl.java index 6cc07c35d9e..c94449d7b9d 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardGroupImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/impl/LeaderboardGroupImpl.java @@ -13,12 +13,25 @@ import com.sap.sailing.domain.base.impl.LeaderboardGroupBaseImpl; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.leaderboard.LeaderboardGroupListener; +import com.sap.sse.concurrent.LockUtil; +import com.sap.sse.concurrent.NamedReentrantReadWriteLock; public class LeaderboardGroupImpl extends LeaderboardGroupBaseImpl implements LeaderboardGroup { private static final long serialVersionUID = 2035927369446736934L; private boolean displayGroupsInReverseOrder; + + /** + * The lock that guards access to the {@link #leaderboards} list. + */ + private final NamedReentrantReadWriteLock leaderboardsLock; + + /** + * The list of leaderboards grouped in this leaderboard group. Access to this list is guarded by + * {@link #leaderboardsLock}. All access has to acquire the corresponding read or write lock. + */ private final List leaderboards; + private final Set listeners; /** @@ -36,8 +49,13 @@ public class LeaderboardGroupImpl extends LeaderboardGroupBaseImpl implements Le this(UUID.randomUUID(), name, description, displayName, displayGroupsInReverseOrder, leaderboards); } - private synchronized void writeObject(ObjectOutputStream oos) throws IOException { - oos.defaultWriteObject(); + private void writeObject(ObjectOutputStream oos) throws IOException { + LockUtil.lockForRead(leaderboardsLock); + try { + oos.defaultWriteObject(); + } finally { + LockUtil.unlockAfterRead(leaderboardsLock); + } } /** @@ -46,9 +64,10 @@ public class LeaderboardGroupImpl extends LeaderboardGroupBaseImpl implements Le public LeaderboardGroupImpl(UUID id, String name, String description, String displayName, boolean displayGroupsInReverseOrder, List leaderboards) { super(id, name, description, displayName); + this.leaderboardsLock = new NamedReentrantReadWriteLock("leaderboards lock fo rleaderboard group "+name, /* fair */ false); this.displayGroupsInReverseOrder = displayGroupsInReverseOrder; - this.leaderboards = new ArrayList(leaderboards); - this.listeners = new HashSet(); + this.leaderboards = new ArrayList<>(leaderboards); + this.listeners = new HashSet<>(); } @Override @@ -99,24 +118,42 @@ public class LeaderboardGroupImpl extends LeaderboardGroupBaseImpl implements Le } @Override - public synchronized Iterable getLeaderboards() { - return new ArrayList(leaderboards); + public Iterable getLeaderboards() { + LockUtil.lockForRead(leaderboardsLock); + try { + return new ArrayList(leaderboards); + } finally { + LockUtil.unlockAfterRead(leaderboardsLock); + } } @Override - public synchronized int getIndexOf(Leaderboard leaderboard) { - return leaderboards.indexOf(leaderboard); + public int getIndexOf(Leaderboard leaderboard) { + LockUtil.lockForRead(leaderboardsLock); + try { + return leaderboards.indexOf(leaderboard); + } finally { + LockUtil.unlockAfterRead(leaderboardsLock); + } } @Override public void addLeaderboard(Leaderboard leaderboard) { - addLeaderboardAt(leaderboard, leaderboards.size()); + LockUtil.lockForWrite(leaderboardsLock); + try { + addLeaderboardAt(leaderboard, leaderboards.size()); + } finally { + LockUtil.unlockAfterWrite(leaderboardsLock); + } } @Override public void addLeaderboardAt(Leaderboard leaderboard, int index) { - synchronized (this) { + LockUtil.lockForWrite(leaderboardsLock); + try { leaderboards.add(index, leaderboard); + } finally { + LockUtil.unlockAfterWrite(leaderboardsLock); } notifyLeaderboardGroupListenersAboutLeaderboardAdded(leaderboard); } @@ -130,8 +167,11 @@ public class LeaderboardGroupImpl extends LeaderboardGroupBaseImpl implements Le @Override public void removeLeaderboard(Leaderboard leaderboard) { - synchronized (this) { + LockUtil.lockForWrite(leaderboardsLock); + try { leaderboards.remove(leaderboard); + } finally { + LockUtil.unlockAfterWrite(leaderboardsLock); } notifyLeaderboardGroupListenersAboutLeaderboardRemoved(leaderboard); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java index 637d96d9b58..8d65c64a5af 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/polars/PolarDataService.java @@ -173,12 +173,11 @@ public interface PolarDataService { * * @param boatClass * @param legType - * @param tack * @return The estimating function for the tack and legtype combination estimating boatspeed over windspeed for the * given boat class. All values in kn. * @throws NotEnoughDataHasBeenAddedException */ - PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException; + PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException; /** * This method is not intended to be used directly apart from debugging purposes. If you intend to use the polar service please @@ -186,12 +185,11 @@ public interface PolarDataService { * * @param boatClass * @param legType - * @param tack * @return The estimating function for the tack and legtype combination estimating true wind angle over windspeed for the * given boat class. TWA in degrees and windspeeds in knots. * @throws NotEnoughDataHasBeenAddedException */ - PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException; + PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException; /** * This method is not intended to be used directly apart from debugging purposes. If you intend to use the polar service please diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java index 952674756ba..a54bafa34ac 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java @@ -2,7 +2,9 @@ package com.sap.sailing.domain.ranking; import java.util.Comparator; import java.util.HashMap; +import java.util.Iterator; import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; import java.util.function.Function; import java.util.function.Supplier; import java.util.stream.StreamSupport; @@ -10,9 +12,11 @@ import java.util.stream.StreamSupport; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.base.impl.CompetitorImpl; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.Speed; +import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedLeg; @@ -121,14 +125,81 @@ public abstract class AbstractRankingMetric implements RankingMetric { } } - public class RankingInfoImpl implements RankingMetric.RankingInfo { - private static final long serialVersionUID = -2390284312153324336L; - + /** + * Helper instance used to encode null values in {@link ConcurrentHashMap} instances which do not accept + * null as key nor value. + */ + private final static Competitor NULL_COMPETITOR = new CompetitorImpl(null, null, null, null, null, null, null, null, null); + + public abstract class AbstractRankingInfo implements RankingMetric.RankingInfo { + private static final long serialVersionUID = 6845168655725234325L; + /** * The time point for which this ranking information is valid */ private final TimePoint timePoint; + /** + * Caches, on demand, the results of calls to {@link #getCompetitorFarthestAheadInLeg(Leg, TimePoint, WindLegTypeAndLegBearingCache)}. + * null values are encoded as the {@link #NULL_COMPETITOR} and must be translated back to null before returning + * to a caller outside of this class. + */ + private final ConcurrentHashMap competitorFarthestAheadInLeg; + + public AbstractRankingInfo(final TimePoint timePoint) { + this.timePoint = timePoint; + this.competitorFarthestAheadInLeg = new ConcurrentHashMap<>(); + } + + @Override + public TimePoint getTimePoint() { + return timePoint; + } + + @Override + public Duration getActualTimeFromRaceStartToReachFarthestAheadInLeg(Competitor competitor, Leg leg, WindLegTypeAndLegBearingCache cache) { + final Duration result; + final TrackedLegOfCompetitor tloc = getTrackedRace().getTrackedLeg(competitor, getTimePoint()); + final Duration raceDurationAtTimePoint = getTrackedRace().getStartOfRace().until(getTimePoint()); + if (tloc != null && tloc.hasStartedLeg(getTimePoint())) { + final Competitor competitorFarthestAheadInLeg = getCompetitorFarthestAheadInLeg(leg, getTimePoint(), cache); + final TrackedLegOfCompetitor tlocOfCompetitorFarthestAheadInLeg = tloc.getTrackedLeg().getTrackedLeg(competitorFarthestAheadInLeg); + final Duration predictedDurationFromTimePointToReachFarthestAheadInLeg = getPredictedDurationToReachWindwardPositionOf( + tloc, tlocOfCompetitorFarthestAheadInLeg, getTimePoint(), cache); + if (predictedDurationFromTimePointToReachFarthestAheadInLeg == null) { + result = null; + } else { + final Duration cDurationFromRaceStartToReachFarthestInLeg = raceDurationAtTimePoint.plus(predictedDurationFromTimePointToReachFarthestAheadInLeg); + result = cDurationFromRaceStartToReachFarthestInLeg; + } + } else { + result = null; + } + return result; + } + + @Override + public Competitor getCompetitorFarthestAheadInLeg(Leg leg, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) { + Competitor result = competitorFarthestAheadInLeg.get(leg); + if (result == NULL_COMPETITOR) { + result = null; + } else if (result == null) { + result = AbstractRankingMetric.this.getCompetitorFarthestAheadInLeg(getTrackedRace().getTrackedLeg(leg), timePoint, cache); + competitorFarthestAheadInLeg.put(leg, result); + } + return result; + } + + @Override + public Competitor getLeaderInLegByCalculatedTime(Leg leg, WindLegTypeAndLegBearingCache cache) { + final TrackedLeg trackedLeg = getTrackedRace().getTrackedLeg(leg); + return trackedLeg.getLeader(getTimePoint(), cache); + } + } + + public class RankingInfoImpl extends AbstractRankingInfo { + private static final long serialVersionUID = -2390284312153324336L; + /** * The basic information for each competitor, telling about actual and corrected times as well as information * about actual and corrected times needed to reach the position of the competitor farthest ahead at @@ -145,20 +216,15 @@ public abstract class AbstractRankingMetric implements RankingMetric { private final Competitor leaderByCorrectedEstimatedTimeToCompetitorFarthestAhead; public RankingInfoImpl(TimePoint timePoint, Map competitorRankingInfo, Competitor competitorFarthestAhead) { + super(timePoint); final Comparator durationComparatorNullsLast = Comparator.nullsLast(Comparator.naturalOrder()); - this.timePoint = timePoint; this.competitorRankingInfo = c->competitorRankingInfo.get(c); this.competitorFarthestAhead = competitorFarthestAhead; leaderByCorrectedEstimatedTimeToCompetitorFarthestAhead = competitorRankingInfo.keySet().stream().sorted( (c1, c2) -> durationComparatorNullsLast.compare( competitorRankingInfo.get(c1).getCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAhead(), competitorRankingInfo.get(c2).getCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAhead())). - findFirst().get(); - } - - @Override - public TimePoint getTimePoint() { - return timePoint; + findFirst().orElse(null); } @Override @@ -219,7 +285,7 @@ public abstract class AbstractRankingMetric implements RankingMetric { */ private Comparator getWindwardDistanceTraveledComparator(final TimePoint timePoint, final WindLegTypeAndLegBearingCache cache) { final Map windwardDistanceTraveledPerCompetitor = new HashMap<>(); - for (final Competitor competitor : getTrackedRace().getRace().getCompetitors()) { + for (final Competitor competitor : getCompetitors()) { windwardDistanceTraveledPerCompetitor.put(competitor, getWindwardDistanceTraveled(competitor, timePoint, cache)); } final Comparator nullsFirstDistanceComparator = Comparator.nullsFirst(Comparator.naturalOrder()); @@ -227,12 +293,19 @@ public abstract class AbstractRankingMetric implements RankingMetric { windwardDistanceTraveledPerCompetitor.get(c2), windwardDistanceTraveledPerCompetitor.get(c1)); } + + /** + * Fetches the competitors to consider for this ranking + */ + protected Iterable getCompetitors() { + return getTrackedRace().getRace().getCompetitors(); + } public RankingMetric.RankingInfo getRankingInfo(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) { Map result = new HashMap<>(); Comparator oneDesignComparator = getWindwardDistanceTraveledComparator(timePoint, cache); Competitor competitorFarthestAhead = StreamSupport - .stream(getTrackedRace().getRace().getCompetitors().spliterator(), /* parallel */true). + .stream(getCompetitors().spliterator(), /* parallel */true). sorted(oneDesignComparator).findFirst().get(); final Distance totalWindwardDistanceTraveled = getWindwardDistanceTraveled(competitorFarthestAhead, timePoint, cache); final TimePoint startOfRace = getTrackedRace().getStartOfRace(); @@ -241,27 +314,27 @@ public abstract class AbstractRankingMetric implements RankingMetric { actualRaceDuration = null; } else { actualRaceDuration = startOfRace.until(timePoint); - for (Competitor competitor : getTrackedRace().getRace().getCompetitors()) { + for (Competitor competitor : getCompetitors()) { final Duration predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead = getPredictedDurationToReachWindwardPositionOf( competitor, competitorFarthestAhead, timePoint, cache); final Duration totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead = predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead == null ? null : actualRaceDuration.plus(predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead); - final Duration correctedEstimatedTimeWhenReachingCompetitorFarthestAhead = totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead == null ? null - : getCorrectedTime( + final Duration calculatedEstimatedTimeWhenReachingCompetitorFarthestAhead = totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead == null ? null + : getCalculatedTime( competitor, () -> getTrackedRace().getTrackedLeg(competitorFarthestAhead, timePoint).getLeg(), () -> getTrackedRace().getTrack(competitorFarthestAhead).getEstimatedPosition( timePoint, /* extrapolate */true), totalEstimatedDurationSinceRaceStartToCompetitorFarthestAhead, totalWindwardDistanceTraveled); - final Duration correctedTime = getCorrectedTime(competitor, + final Duration calculatedTime = getCalculatedTime(competitor, () -> getTrackedRace().getCurrentLeg(competitor, timePoint).getLeg(), () -> getTrackedRace() .getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolated */true), actualRaceDuration, totalWindwardDistanceTraveled); RankingMetric.CompetitorRankingInfo rankingInfo = new CompetitorRankingInfoImpl(timePoint, competitor, - getWindwardDistanceTraveled(competitor, timePoint, cache), actualRaceDuration, correctedTime, + getWindwardDistanceTraveled(competitor, timePoint, cache), actualRaceDuration, calculatedTime, predictedDurationToReachWindwardPositionOfCompetitorFarthestAhead, - correctedEstimatedTimeWhenReachingCompetitorFarthestAhead); + calculatedEstimatedTimeWhenReachingCompetitorFarthestAhead); result.put(competitor, rankingInfo); } } @@ -290,7 +363,7 @@ public abstract class AbstractRankingMetric implements RankingMetric { * competitor has sailed. As others may not need it at all, the parameter is declared as a * {@link Supplier} which delays evaluation until it is needed or avoids it altogether. */ - protected abstract Duration getCorrectedTime(Competitor who, Supplier leg, + protected abstract Duration getCalculatedTime(Competitor who, Supplier leg, Supplier estimatedPosition, Duration totalDurationSinceRaceStart, Distance totalWindwardDistanceTraveled); @@ -371,14 +444,18 @@ public abstract class AbstractRankingMetric implements RankingMetric { assert getTrackedRace().getRace().getCourse().getIndexOfWaypoint(legWho.getLeg().getFrom()) <= getTrackedRace() .getRace().getCourse().getIndexOfWaypoint(legTo.getLeg().getFrom()); final Duration toEndOfLegOrTo = getPredictedDurationToEndOfLegOrTo(timePoint, legWho, legWho.getTrackedLeg().getTrackedLeg(to), cache); - final Duration durationForSubsequentLegsToReachAtEqualPerformance; - if (legWho.getLeg() == legTo.getLeg()) { - durationForSubsequentLegsToReachAtEqualPerformance = Duration.NULL; + if (toEndOfLegOrTo == null) { + result = null; } else { - durationForSubsequentLegsToReachAtEqualPerformance = getDurationToReachAtEqualPerformance(who, to, - legWho.getLeg().getTo(), timePoint, cache); + final Duration durationForSubsequentLegsToReachAtEqualPerformance; + if (legWho.getLeg() == legTo.getLeg()) { + durationForSubsequentLegsToReachAtEqualPerformance = Duration.NULL; + } else { + durationForSubsequentLegsToReachAtEqualPerformance = getDurationToReachAtEqualPerformance(who, to, + legWho.getLeg().getTo(), timePoint, cache); + } + result = toEndOfLegOrTo.plus(durationForSubsequentLegsToReachAtEqualPerformance); } - result = toEndOfLegOrTo.plus(durationForSubsequentLegsToReachAtEqualPerformance); } } return result; @@ -427,9 +504,6 @@ public abstract class AbstractRankingMetric implements RankingMetric { WindLegTypeAndLegBearingCache cache) { assert legWho.hasStartedLeg(timePoint); assert legTo.hasStartedLeg(timePoint); - // FIXME see bug 2907!!! - //assert getWindwardDistanceTraveled(legTo.getCompetitor(), legTo.hasFinishedLeg(timePoint)?legTo.getFinishTime():timePoint, cache).compareTo( - // getWindwardDistanceTraveled(legWho.getCompetitor(), legWho.hasFinishedLeg(timePoint)?legWho.getFinishTime():timePoint, cache)) >= 0; final Duration toEndOfLegOrTo; if (legTo.hasFinishedLeg(timePoint)) { // calculate actual time it takes who to reach the end of the leg starting at timePoint: @@ -438,6 +512,8 @@ public abstract class AbstractRankingMetric implements RankingMetric { // who's leg finishing time is known; we don't need to extrapolate toEndOfLegOrTo = timePoint.until(whosLegFinishTime); } else { + assert getWindwardDistanceTraveled(legTo.getCompetitor(), legTo.hasFinishedLeg(timePoint)?legTo.getFinishTime():timePoint, cache).compareTo( + getWindwardDistanceTraveled(legWho.getCompetitor(), legWho.hasFinishedLeg(timePoint)?legWho.getFinishTime():timePoint, cache)) >= 0; // estimate who's leg finishing time by extrapolating with the average VMG (if available) or the current VMG // (if no average VMG can currently be computed, e.g., because the time point is exactly at the leg start) final Position windwardPositionToReachInWhosCurrentLeg = @@ -455,10 +531,10 @@ public abstract class AbstractRankingMetric implements RankingMetric { private Duration getDurationToReach(final Position windwardPositionToReachInWhosCurrentLeg, TimePoint timePoint, final TrackedLegOfCompetitor whosLeg, WindLegTypeAndLegBearingCache cache) { final Duration toEndOfLegOrTo; - final Speed currentVMG = whosLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache); final Speed averageVMG = whosLeg.getAverageVelocityMadeGood(timePoint, cache); - final Speed vmg = Double.isNaN(averageVMG.getKnots()) ? currentVMG : averageVMG; - toEndOfLegOrTo = vmg.getDuration( + final Speed vmg = averageVMG == null || Double.isNaN(averageVMG.getKnots()) ? + /* default to current VMG */ whosLeg.getVelocityMadeGood(timePoint, WindPositionMode.EXACT, cache) : averageVMG; + toEndOfLegOrTo = vmg == null || vmg.getKnots() == 0.0 ? null : vmg.getDuration( whosLeg.getTrackedLeg().getWindwardDistance( getTrackedRace().getTrack(whosLeg.getCompetitor()).getEstimatedPosition(timePoint, /* extrapolate */true), windwardPositionToReachInWhosCurrentLeg, timePoint, WindPositionMode.LEG_MIDDLE)); @@ -532,16 +608,18 @@ public abstract class AbstractRankingMetric implements RankingMetric { if (trackedLegOfCompetitor.hasStartedLeg(timePoint)) { if (!trackedLegOfCompetitor.hasFinishedLeg(timePoint)) { // partial distance sailed: - final Distance windwardDistanceFromLegStart = trackedLeg.getWindwardDistanceFromLegStart( - getTrackedRace().getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolate */ true), cache); - final Distance legWindwardDistance = trackedLeg.getWindwardDistance(cache); - if (legWindwardDistance.compareTo(windwardDistanceFromLegStart) < 0) { - d = d.add(legWindwardDistance); - } else { - // if the competitor is currently at the mark rounding, the windward distance within the leg may - // be negative; don't reduce the distance in this case - if (windwardDistanceFromLegStart.getMeters() > 0) { - d = d.add(windwardDistanceFromLegStart); + final Position estimatedPosition = getTrackedRace().getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolate */ true); + if (estimatedPosition != null) { + final Distance windwardDistanceFromLegStart = trackedLeg.getWindwardDistanceFromLegStart(estimatedPosition, cache); + final Distance legWindwardDistance = trackedLeg.getWindwardDistance(cache); + if (legWindwardDistance.compareTo(windwardDistanceFromLegStart) < 0) { + d = d.add(legWindwardDistance); + } else { + // if the competitor is currently at the mark rounding, the windward distance within the leg may + // be negative; don't reduce the distance in this case + if (windwardDistanceFromLegStart.getMeters() > 0) { + d = d.add(windwardDistanceFromLegStart); + } } } break; @@ -555,4 +633,61 @@ public abstract class AbstractRankingMetric implements RankingMetric { } return result; } + + /** + * @return null if no competitor has started the leg yet; the first competitor to finish the leg if any + * has already finished the leg at timePoint; or the competitor with the greatest windward + * distance traveled in the leg at timePoint otherwise + */ + protected Competitor getCompetitorFarthestAheadInLeg(TrackedLeg trackedLeg, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) { + Competitor firstAroundMark = getFirstLegFinisherBefore(trackedLeg, timePoint); + final Competitor result; + if (firstAroundMark != null) { + result = firstAroundMark; + } else { + Iterable markPassingsForLegStart = getTrackedRace().getMarkPassingsInOrder(trackedLeg.getLeg().getFrom()); + Distance maxWindwardDistanceTraveled = new MeterDistance(Double.MIN_VALUE); + Competitor competitorFarthestAlong = null; + getTrackedRace().lockForRead(markPassingsForLegStart); + try { + for (MarkPassing mp : markPassingsForLegStart) { + if (mp.getTimePoint().after(timePoint)) { + break; + } + final Distance windwardDistanceTraveled = getWindwardDistanceTraveled(mp.getCompetitor(), mp.getWaypoint(), + timePoint, cache); + if (windwardDistanceTraveled.compareTo(maxWindwardDistanceTraveled) > 0) { + maxWindwardDistanceTraveled = windwardDistanceTraveled; + competitorFarthestAlong = mp.getCompetitor(); + } + } + } finally { + getTrackedRace().unlockAfterRead(markPassingsForLegStart); + } + result = competitorFarthestAlong; + } + return result; + } + + /** + * Determines the first competitor finishing the leg identified by trackedLeg at or before timePoint. If + * no such competitor exists, null is returned. + */ + private Competitor getFirstLegFinisherBefore(TrackedLeg trackedLeg, TimePoint timePoint) { + Iterable markPassingsForLegEnd = getTrackedRace().getMarkPassingsInOrder(trackedLeg.getLeg().getTo()); + Competitor firstAroundMark = null; + getTrackedRace().lockForRead(markPassingsForLegEnd); + try { + final Iterator i = markPassingsForLegEnd.iterator(); + if (i.hasNext()) { + MarkPassing markPassing = i.next(); + if (!markPassing.getTimePoint().after(timePoint)) { + firstAroundMark = markPassing.getCompetitor(); + } + } + } finally { + getTrackedRace().unlockAfterRead(markPassingsForLegEnd); + } + return firstAroundMark; + } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/ORCPerformanceCurveRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/ORCPerformanceCurveRankingMetric.java index 751d71e029b..1b8dfe31edc 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/ORCPerformanceCurveRankingMetric.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/ORCPerformanceCurveRankingMetric.java @@ -51,7 +51,7 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric { } @Override - protected Duration getCorrectedTime(Competitor who, Supplier leg, Supplier estimatedPosition, + protected Duration getCalculatedTime(Competitor who, Supplier leg, Supplier estimatedPosition, Duration totalDurationSinceRaceStart, Distance totalWindwardDistanceTraveled) { // TODO Auto-generated method stub return null; @@ -66,7 +66,7 @@ public class ORCPerformanceCurveRankingMetric extends AbstractRankingMetric { @Override public Duration getLegGapToLegLeaderInOwnTime(TrackedLegOfCompetitor trackedLegOfCompetitor, TimePoint timePoint, - WindLegTypeAndLegBearingCache cache) { + RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { // TODO Auto-generated method stub return null; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java index 77acfc206e8..6971565d580 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java @@ -62,27 +62,22 @@ public class OneDesignRankingMetric extends AbstractRankingMetric { } @Override - protected Duration getCorrectedTime(Competitor who, Supplier leg, Supplier estimatedPosition, + protected Duration getCalculatedTime(Competitor who, Supplier leg, Supplier estimatedPosition, Duration totalDurationSinceRaceStart, Distance totalWindwardDistanceTraveled) { return totalDurationSinceRaceStart; } @Override public Duration getLegGapToLegLeaderInOwnTime(TrackedLegOfCompetitor trackedLegOfCompetitor, TimePoint timePoint, - WindLegTypeAndLegBearingCache cache) { - return trackedLegOfCompetitor.getGapToLeader(timePoint, WindPositionMode.LEG_MIDDLE, cache); + final RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { + return trackedLegOfCompetitor.getGapToLeader(timePoint, WindPositionMode.LEG_MIDDLE, rankingInfo, cache); } @Override public RankingInfo getRankingInfo(final TimePoint timePoint, final WindLegTypeAndLegBearingCache cache) { - return new RankingInfo() { + return new AbstractRankingInfo(timePoint) { private static final long serialVersionUID = 25689357311324825L; - @Override - public TimePoint getTimePoint() { - return timePoint; - } - @Override public Function getCompetitorRankingInfo() { return competitor -> getCompetitorRankingInfo(competitor); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/RankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/RankingMetric.java index e3299b5be69..83d5b32cbfa 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/RankingMetric.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/RankingMetric.java @@ -5,6 +5,7 @@ import java.util.Comparator; import java.util.function.Function; import com.sap.sailing.domain.base.Competitor; +import com.sap.sailing.domain.base.Leg; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; import com.sap.sailing.domain.tracking.TrackedLeg; @@ -112,6 +113,20 @@ public interface RankingMetric extends Serializable { * position at {@link #timePoint}. */ Competitor getLeaderByCorrectedEstimatedTimeToCompetitorFarthestAhead(); + + /** + * Similar to {@link #getLeaderByCorrectedEstimatedTimeToCompetitorFarthestAhead()}, but relative to a + * {@link Leg}. This will not consider any progress or position beyond the finishing of that leg. + * Instead, for those competitors who have already finished the leg, their + * {@link TrackedLegOfCompetitor#getFinishTime() finishing time} for the leg is used, and the distance traveled + * is normalized to the {@link TrackedLeg#getWindwardDistance() windward distance of the leg} at the + * {@link TrackedLeg#getReferenceTimePoint() reference time point}. + */ + Competitor getLeaderInLegByCalculatedTime(Leg leg, WindLegTypeAndLegBearingCache cache); + + Competitor getCompetitorFarthestAheadInLeg(Leg leg, TimePoint timePoint, WindLegTypeAndLegBearingCache cache); + + Duration getActualTimeFromRaceStartToReachFarthestAheadInLeg(Competitor competitor, Leg leg, WindLegTypeAndLegBearingCache cache); } public interface LegRankingInfo extends Timed, Serializable { @@ -178,7 +193,7 @@ public interface RankingMetric extends Serializable { * @param competitor * the competitor for which to tell the gap to the leader in competitor's own time * - * @see #getLegGapToLegLeaderInOwnTime(TrackedLegOfCompetitor, TimePoint, WindLegTypeAndLegBearingCache) + * @see #getLegGapToLegLeaderInOwnTime(TrackedLegOfCompetitor, TimePoint, com.sap.sailing.domain.ranking.RankingMetric.CompetitorRankingInfo.RankingInfo, WindLegTypeAndLegBearingCache) */ default Duration getGapToLeaderInOwnTime(RankingInfo rankingInfo, Competitor competitor) { return getGapToLeaderInOwnTime(rankingInfo, competitor, new LeaderboardDTOCalculationReuseCache(rankingInfo.getTimePoint())); @@ -205,6 +220,10 @@ public interface RankingMetric extends Serializable { * handicap ranking is in effect) of the leg that the competitor is in at timePoint (or * the last leg if timePoint is after competitor has finished the race). Returns * null if timePoint is before competitor has started. + * + * @param rankingInfo + * tells about leader (by calculated time) and boat farthest ahead */ - Duration getLegGapToLegLeaderInOwnTime(TrackedLegOfCompetitor trackedLegOfCompetitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache); + Duration getLegGapToLegLeaderInOwnTime(TrackedLegOfCompetitor trackedLegOfCompetitor, TimePoint timePoint, + RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/TimeOnTimeAndDistanceRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/TimeOnTimeAndDistanceRankingMetric.java index 5178f0de435..7b7821f117c 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/TimeOnTimeAndDistanceRankingMetric.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/TimeOnTimeAndDistanceRankingMetric.java @@ -2,7 +2,6 @@ package com.sap.sailing.domain.ranking; import java.util.Comparator; import java.util.HashMap; -import java.util.Iterator; import java.util.Map; import java.util.function.Function; import java.util.function.Supplier; @@ -14,7 +13,6 @@ import com.sap.sailing.domain.base.Waypoint; import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.Mile; import com.sap.sailing.domain.common.Position; -import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; @@ -106,8 +104,10 @@ public class TimeOnTimeAndDistanceRankingMetric extends AbstractRankingMetric { // competitors that have not yet started the leg will get a duration based on Long.MAX_VALUE final Map correctedTimesToReachFastestBoatsPositionAtTimePointOrEndOfLegMeasuredFromStartOfRace = new HashMap<>(); final Competitor fastestCompetitorInLeg = getCompetitorFarthestAheadInLeg(trackedLeg, timePoint, cache); + final boolean fastestCompetitorHasStartedLeg; if (fastestCompetitorInLeg != null) { final TrackedLegOfCompetitor trackedLegOfFastestCompetitorInLeg = trackedLeg.getTrackedLeg(fastestCompetitorInLeg); + fastestCompetitorHasStartedLeg = trackedLegOfFastestCompetitorInLeg.hasStartedLeg(timePoint); final Distance totalWindwardDistanceLegLeaderTraveledUpToTimePointOrLegEnd; final TimePoint startOfRace = getTrackedRace().getStartOfRace(); final Position positionOfFastestBoatInLegAtTimePointOrLegEnd; @@ -137,14 +137,15 @@ public class TimeOnTimeAndDistanceRankingMetric extends AbstractRankingMetric { totalWindwardDistanceLegLeaderTraveledUpToTimePointOrLegEnd = getWindwardDistanceTraveled( fastestCompetitorInLeg, timePoint, cache); } - for (Competitor competitor : getTrackedRace().getRace().getCompetitors()) { + for (Competitor competitor : getCompetitors()) { final TrackedLegOfCompetitor competitorLeg = trackedLeg.getTrackedLeg(competitor); final Duration correctedTime; if (competitorLeg != null && competitorLeg.hasStartedLeg(timePoint)) { final Duration timeToReachFastest = getPredictedDurationToEndOfLegOrTo(timePoint, competitorLeg, trackedLegOfFastestCompetitorInLeg, cache); - final Duration totalDurationSinceRaceStart = startOfRace.until(timePoint).plus(timeToReachFastest); - correctedTime = getCorrectedTime(competitor, () -> trackedLeg.getLeg(), + final Duration totalDurationSinceRaceStart = timeToReachFastest == null ? null : + startOfRace.until(timePoint).plus(timeToReachFastest); + correctedTime = getCalculatedTime(competitor, () -> trackedLeg.getLeg(), () -> positionOfFastestBoatInLegAtTimePointOrLegEnd, totalDurationSinceRaceStart, totalWindwardDistanceLegLeaderTraveledUpToTimePointOrLegEnd); } else { // competitor hasn't started the leg yet; they all get MAX_VALUE as the corrected duration, @@ -154,74 +155,22 @@ public class TimeOnTimeAndDistanceRankingMetric extends AbstractRankingMetric { } correctedTimesToReachFastestBoatsPositionAtTimePointOrEndOfLegMeasuredFromStartOfRace.put(competitor, correctedTime); } - } - return (tloc1, tloc2) -> fastestCompetitorInLeg==null ? 0 : - correctedTimesToReachFastestBoatsPositionAtTimePointOrEndOfLegMeasuredFromStartOfRace.get(tloc1.getCompetitor()). - compareTo(correctedTimesToReachFastestBoatsPositionAtTimePointOrEndOfLegMeasuredFromStartOfRace.get(tloc2.getCompetitor())); - } - - /** - * @return null if no competitor has started the leg yet; the first competitor to finish the leg if any - * has already finished the leg at timePoint; or the competitor with the greatest windward - * distance traveled in the leg at timePoint otherwise - */ - private Competitor getCompetitorFarthestAheadInLeg(TrackedLeg trackedLeg, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) { - Competitor firstAroundMark = getFirstLegFinisherBefore(trackedLeg, timePoint); - final Competitor result; - if (firstAroundMark != null) { - result = firstAroundMark; } else { - Iterable markPassingsForLegStart = getTrackedRace().getMarkPassingsInOrder(trackedLeg.getLeg().getFrom()); - Distance maxWindwardDistanceTraveled = new MeterDistance(Double.MIN_VALUE); - Competitor competitorFarthestAlong = null; - getTrackedRace().lockForRead(markPassingsForLegStart); - try { - for (MarkPassing mp : markPassingsForLegStart) { - if (mp.getTimePoint().after(timePoint)) { - break; - } - final Distance windwardDistanceTraveled = getWindwardDistanceTraveled(mp.getCompetitor(), mp.getWaypoint(), - timePoint, cache); - if (windwardDistanceTraveled.compareTo(maxWindwardDistanceTraveled) > 0) { - maxWindwardDistanceTraveled = windwardDistanceTraveled; - competitorFarthestAlong = mp.getCompetitor(); - } - } - } finally { - getTrackedRace().unlockAfterRead(markPassingsForLegStart); - } - result = competitorFarthestAlong; + fastestCompetitorHasStartedLeg = false; } - return result; - } - - /** - * Determines the first competitor finishing the leg identified by trackedLeg at or before timePoint. If - * no such competitor exists, null is returned. - */ - private Competitor getFirstLegFinisherBefore(TrackedLeg trackedLeg, TimePoint timePoint) { - Iterable markPassingsForLegEnd = getTrackedRace().getMarkPassingsInOrder(trackedLeg.getLeg().getTo()); - Competitor firstAroundMark = null; - getTrackedRace().lockForRead(markPassingsForLegEnd); - try { - final Iterator i = markPassingsForLegEnd.iterator(); - if (i.hasNext()) { - MarkPassing markPassing = i.next(); - if (!markPassing.getTimePoint().after(timePoint)) { - firstAroundMark = markPassing.getCompetitor(); - } - } - } finally { - getTrackedRace().unlockAfterRead(markPassingsForLegEnd); - } - return firstAroundMark; + final Comparator durationComparatorNullsLast = Comparator.nullsLast(Comparator.naturalOrder()); + return (tloc1, tloc2) -> fastestCompetitorHasStartedLeg ? + durationComparatorNullsLast.compare( + correctedTimesToReachFastestBoatsPositionAtTimePointOrEndOfLegMeasuredFromStartOfRace.get(tloc1.getCompetitor()), + correctedTimesToReachFastestBoatsPositionAtTimePointOrEndOfLegMeasuredFromStartOfRace.get(tloc2.getCompetitor())) + : 0; } @Override public Duration getCorrectedTime(Competitor competitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) { final Duration timeActuallySpent = getActualTimeSinceStartOfRace(competitor, timePoint); final Distance windwardDistanceSailed = getWindwardDistanceTraveled(competitor, timePoint, cache); - return getCorrectedTime(competitor, ()->getTrackedRace().getCurrentLeg(competitor, timePoint).getLeg(), + return getCalculatedTime(competitor, ()->getTrackedRace().getCurrentLeg(competitor, timePoint).getLeg(), ()->getTrackedRace().getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolate */true), timeActuallySpent, windwardDistanceSailed); } @@ -235,7 +184,7 @@ public class TimeOnTimeAndDistanceRankingMetric extends AbstractRankingMetric { } @Override - protected Duration getCorrectedTime(Competitor who, Supplier leg, Supplier estimatedPosition, + protected Duration getCalculatedTime(Competitor who, Supplier leg, Supplier estimatedPosition, Duration totalDurationSinceRaceStart, Distance totalWindwardDistanceTraveled) { final Duration timeOnDistanceFactorInSecondsPerNauticalMile = getTimeOnDistanceFactorInSecondsPerNauticalMile(who); return totalDurationSinceRaceStart == null ? null : @@ -311,13 +260,19 @@ public class TimeOnTimeAndDistanceRankingMetric extends AbstractRankingMetric { */ @Override public Duration getGapToLeaderInOwnTime(RankingMetric.RankingInfo rankingInfo, Competitor competitor, WindLegTypeAndLegBearingCache cache) { + final Duration result; // actual times and common distance: final Competitor leaderByCorrectedEstimatedTimeToCompetitorFarthestAhead = rankingInfo.getLeaderByCorrectedEstimatedTimeToCompetitorFarthestAhead(); - final Duration t_k = rankingInfo.getCompetitorRankingInfo().apply(leaderByCorrectedEstimatedTimeToCompetitorFarthestAhead). - getEstimatedActualDurationFromRaceStartToCompetitorFarthestAhead(); - final Duration t_i = rankingInfo.getCompetitorRankingInfo().apply(competitor).getEstimatedActualDurationFromRaceStartToCompetitorFarthestAhead(); - final Distance d = rankingInfo.getCompetitorRankingInfo().apply(rankingInfo.getCompetitorFarthestAhead()).getWindwardDistanceSailed(); - return getGapToCompetitorInOwnTime(competitor, leaderByCorrectedEstimatedTimeToCompetitorFarthestAhead, t_i, t_k, d); + if (leaderByCorrectedEstimatedTimeToCompetitorFarthestAhead == null) { + result = null; + } else { + final Duration t_k = rankingInfo.getCompetitorRankingInfo().apply(leaderByCorrectedEstimatedTimeToCompetitorFarthestAhead). + getEstimatedActualDurationFromRaceStartToCompetitorFarthestAhead(); + final Duration t_i = rankingInfo.getCompetitorRankingInfo().apply(competitor).getEstimatedActualDurationFromRaceStartToCompetitorFarthestAhead(); + final Distance d = rankingInfo.getCompetitorRankingInfo().apply(rankingInfo.getCompetitorFarthestAhead()).getWindwardDistanceSailed(); + result = getGapToCompetitorInOwnTime(competitor, leaderByCorrectedEstimatedTimeToCompetitorFarthestAhead, t_i, t_k, d); + } + return result; } /** @@ -376,47 +331,31 @@ public class TimeOnTimeAndDistanceRankingMetric extends AbstractRankingMetric { } @Override - public Duration getLegGapToLegLeaderInOwnTime(TrackedLegOfCompetitor trackedLegOfCompetitor, TimePoint timePoint, WindLegTypeAndLegBearingCache cache) { + public Duration getLegGapToLegLeaderInOwnTime(TrackedLegOfCompetitor trackedLegOfCompetitor, TimePoint timePoint, + RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { final Duration result; + final Leg leg = trackedLegOfCompetitor.getLeg(); if (getTrackedRace().getStartOfRace() == null || !trackedLegOfCompetitor.hasStartedLeg(timePoint)) { result = null; } else { - // FIXME need to calculate the ranking stuff only once for requests for all competitors to this method, or else we'll end up with n^2 effort - final Duration raceDurationAtTimePoint = getTrackedRace().getStartOfRace().until(timePoint); final Competitor farthestAheadOrEarliestLegFinisher = getCompetitorFarthestAheadInLeg(trackedLegOfCompetitor.getTrackedLeg(), timePoint, cache); - final TrackedLegOfCompetitor tlocOfFarthestAhead = trackedLegOfCompetitor.getTrackedLeg().getTrackedLeg(farthestAheadOrEarliestLegFinisher); - final boolean farthestAheadAlreadyFinishedLeg = tlocOfFarthestAhead.hasFinishedLeg(timePoint); - final Distance windwardDistanceFarthestTraveledUntilFinishingLeg = getWindwardDistanceTraveled(farthestAheadOrEarliestLegFinisher, - farthestAheadAlreadyFinishedLeg ? tlocOfFarthestAhead.getFinishTime() : timePoint, cache); - final Map correctedTimeToReachFarthestAheadInLeg = new HashMap<>(); - final Map actualTimeFromRaceStartToReachFarthestAheadInLeg = new HashMap<>(); - // Consider all competitors, regardless of whether they have started the leg or not; a competitor in a previous - // leg may well be the race leader. However, if a competitor has finished the leg already, use the finishing time - // point for that competitor. - for (Competitor c : getTrackedRace().getRace().getCompetitors()) { - final TrackedLegOfCompetitor tloc = trackedLegOfCompetitor.getTrackedLeg().getTrackedLeg(c); - if (tloc.hasStartedLeg(timePoint)) { - final Duration predictedDurationFromTimePointToReachFarthestAheadInLeg = getPredictedDurationToReachWindwardPositionOf(tloc, tlocOfFarthestAhead, timePoint, cache); - final Duration cDurationFromRaceStartToReachFarthestInLeg = raceDurationAtTimePoint.plus(predictedDurationFromTimePointToReachFarthestAheadInLeg); - actualTimeFromRaceStartToReachFarthestAheadInLeg.put(c, cDurationFromRaceStartToReachFarthestInLeg); - final Duration correctedTimeWhenReachingFarthestAheadInLeg = getCorrectedTime(c, ()->tloc.getTrackedLeg().getLeg(), - ()->farthestAheadAlreadyFinishedLeg?getTrackedRace().getApproximatePosition(tlocOfFarthestAhead.getTrackedLeg().getLeg().getTo(), tlocOfFarthestAhead.getFinishTime()): - getTrackedRace().getTrack(farthestAheadOrEarliestLegFinisher).getEstimatedPosition(timePoint, /* extrapolate */ true), - cDurationFromRaceStartToReachFarthestInLeg, windwardDistanceFarthestTraveledUntilFinishingLeg); - correctedTimeToReachFarthestAheadInLeg.put(c, correctedTimeWhenReachingFarthestAheadInLeg); - } else { - actualTimeFromRaceStartToReachFarthestAheadInLeg.put(c, null); - correctedTimeToReachFarthestAheadInLeg.put(c, null); - } + if (farthestAheadOrEarliestLegFinisher == null) { + result = null; + } else { + final TrackedLegOfCompetitor tlocOfFarthestAhead = trackedLegOfCompetitor.getTrackedLeg().getTrackedLeg(farthestAheadOrEarliestLegFinisher); + final boolean farthestAheadAlreadyFinishedLeg = tlocOfFarthestAhead.hasFinishedLeg(timePoint); + final Distance windwardDistanceFarthestTraveledUntilFinishingLeg = getWindwardDistanceTraveled(farthestAheadOrEarliestLegFinisher, + farthestAheadAlreadyFinishedLeg ? tlocOfFarthestAhead.getFinishTime() : timePoint, cache); + // Consider all competitors, regardless of whether they have started the leg or not; a competitor in a previous + // leg may well be the race leader. However, if a competitor has finished the leg already, use the finishing time + // point for that competitor. + // leader in the leg is now the competitor with the least corrected time to reach the competitor farthest ahead in the leg + final Competitor legLeader = rankingInfo.getLeaderInLegByCalculatedTime(trackedLegOfCompetitor.getTrackedLeg().getLeg(), cache); + result = getGapToCompetitorInOwnTime(trackedLegOfCompetitor.getCompetitor(), legLeader, + rankingInfo.getActualTimeFromRaceStartToReachFarthestAheadInLeg(trackedLegOfCompetitor.getCompetitor(), leg, cache), + rankingInfo.getActualTimeFromRaceStartToReachFarthestAheadInLeg(legLeader, leg, cache), + windwardDistanceFarthestTraveledUntilFinishingLeg); } - // leader in the leg is now the competitor with the least corrected time to reach the competitor farthest ahead in the leg - Comparator durationComparatorNUllsLast = Comparator.nullsLast(Comparator.naturalOrder()); - final Competitor leader = correctedTimeToReachFarthestAheadInLeg.entrySet().stream() - .sorted((e1, e2) -> durationComparatorNUllsLast.compare(e1.getValue(), e2.getValue())).findFirst().get().getKey(); - result = getGapToCompetitorInOwnTime(trackedLegOfCompetitor.getCompetitor(), leader, - actualTimeFromRaceStartToReachFarthestAheadInLeg.get(trackedLegOfCompetitor.getCompetitor()), - actualTimeFromRaceStartToReachFarthestAheadInLeg.get(leader), - windwardDistanceFarthestTraveledUntilFinishingLeg); } return result; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/BaseRegattaLikeImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/BaseRegattaLikeImpl.java index f5fb55a1948..7dbef9f0c15 100644 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/BaseRegattaLikeImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/BaseRegattaLikeImpl.java @@ -64,7 +64,12 @@ public class BaseRegattaLikeImpl implements IsRegattaLike { final Double timeOnTimeFactorFromRegattaLog = getTimeOnTimeFactorFromRegattaLog(competitor); final Double result; if (timeOnTimeFactorFromRegattaLog == null) { - result = competitor.getTimeOnTimeFactor(); + final Double competitorTimeOnTimeFactorDefault = competitor.getTimeOnTimeFactor(); + if (competitorTimeOnTimeFactorDefault == null) { + result = 1.0; + } else { + result = competitorTimeOnTimeFactorDefault; + } } else { result = timeOnTimeFactorFromRegattaLog; } @@ -76,7 +81,12 @@ public class BaseRegattaLikeImpl implements IsRegattaLike { final Duration timeOnDistanceAllowancePerNauticalMileFromRegattaLog = getTimeOnDistanceAllowancePerNauticalMileFromRegattaLog(competitor); final Duration result; if (timeOnDistanceAllowancePerNauticalMileFromRegattaLog == null) { - result = competitor.getTimeOnDistanceAllowancePerNauticalMile(); + final Duration competitorAllowanceDefault = competitor.getTimeOnDistanceAllowancePerNauticalMile(); + if (competitorAllowanceDefault == null) { + result = Duration.NULL; + } else { + result = competitorAllowanceDefault; + } } else { result = timeOnDistanceAllowancePerNauticalMileFromRegattaLog; } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java index d804bf4733d..d856219aab4 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedLegOfCompetitor.java @@ -13,6 +13,7 @@ import com.sap.sailing.domain.common.Speed; import com.sap.sailing.domain.common.SpeedWithBearing; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; @@ -151,27 +152,31 @@ public interface TrackedLegOfCompetitor extends Serializable { /** * Computes the gap in seconds to the leader / winner of this leg. Returns null in case this leg's * competitor hasn't started the leg yet. + * @param rankingInfo TODO */ - Duration getGapToLeader(TimePoint timePoint, WindPositionMode windPositionMode); + Duration getGapToLeader(TimePoint timePoint, RankingInfo rankingInfo, WindPositionMode windPositionMode); /** - * Same as {@link #getGapToLeader(TimePoint, WindPositionMode)}, only that a cache for wind and leg type data is used. + * Same as {@link #getGapToLeader(TimePoint, RankingInfo, WindPositionMode)}, only that a cache for wind and leg type data is used. + * @param rankingInfo TODO */ - Duration getGapToLeader(TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache); + Duration getGapToLeader(TimePoint timePoint, WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache); /** * If a caller already went through the effort of computing the leg's leader at timePoint, it - * can share this knowledge to speed up computation as compared to {@link #getGapToLeader(TimePoint, WindPositionMode)}. + * can share this knowledge to speed up computation as compared to {@link #getGapToLeader(TimePoint, RankingInfo, WindPositionMode)}. + * @param rankingInfo TODO */ - Duration getGapToLeader(TimePoint timePoint, Competitor leaderInLegAtTimePoint, WindPositionMode windPositionMode) throws NoWindException; + Duration getGapToLeader(TimePoint timePoint, Competitor leaderInLegAtTimePoint, RankingInfo rankingInfo, WindPositionMode windPositionMode) throws NoWindException; /** - * Same as {@link #getGapToLeader(TimePoint, Competitor, WindPositionMode)}, only that an additional cache is used + * Same as {@link #getGapToLeader(TimePoint, Competitor, RankingInfo, WindPositionMode)}, only that an additional cache is used * to avoid redundant evaluations of leg types and wind field information across various calculations that * all can use the same basic information. + * @param rankingInfo TODO */ Duration getGapToLeader(TimePoint timePoint, Competitor leaderInLegAtTimePoint, - WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache); + WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache); boolean hasStartedLeg(TimePoint timePoint); @@ -228,14 +233,17 @@ public interface TrackedLegOfCompetitor extends Serializable { * distance between this leg's competitor and the leader is returned. Note that this can lead to a situation where * the distance to leader is unrelated to the {@link #getWindwardDistanceToGo(TimePoint, WindPositionMode) distance to go} which is * used for ranking. + * + * @param rankingInfo materialized ranking information that is pre-calculated to avoid expensive redundant work */ - Distance getWindwardDistanceToOverallLeader(TimePoint timePoint, WindPositionMode windPositionMode); + Distance getWindwardDistanceToCompetitorFarthestAhead(TimePoint timePoint, WindPositionMode windPositionMode, RankingInfo rankingInfo); /** - * Same as {@link #getWindwardDistanceToOverallLeader(TimePoint, WindPositionMode)}, only that a cache for leg type + * Same as {@link #getWindwardDistanceToCompetitorFarthestAhead(TimePoint, WindPositionMode, RankingInfo)}, only that a cache for leg type * calculation is passed. + * @param rankingInfo TODO */ - Distance getWindwardDistanceToOverallLeader(TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache); + Distance getWindwardDistanceToCompetitorFarthestAhead(TimePoint timePoint, WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache); /** * Computes the average absolute cross track error for this leg. The cross track error for each fix is taken to be a diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java index f33ae238f70..c98280a45e8 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java @@ -41,6 +41,7 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.ranking.RankingMetric; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; @@ -515,17 +516,21 @@ public interface TrackedRace extends Serializable { Distance getDistanceTraveledIncludingGateStart(Competitor competitor, TimePoint timePoint); /** - * See {@link TrackedLegOfCompetitor#getWindwardDistanceToOverallLeader(TimePoint, WindPositionMode)} + * See {@link TrackedLegOfCompetitor#getWindwardDistanceToCompetitorFarthestAhead(TimePoint, WindPositionMode, RankingInfo)} */ - Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode); + Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode); /** - * Same as {@link #getWindwardDistanceToOverallLeader(Competitor, TimePoint, WindPositionMode)}, only with an additional - * cache to speed up wind and leg type and leg bearing calculations in case of multiple similar look-ups for the same - * time point. + * Same as {@link #getWindwardDistanceToCompetitorFarthestAhead(Competitor, TimePoint, WindPositionMode)}, only with an + * additional cache to speed up wind and leg type and leg bearing calculations in case of multiple similar look-ups + * for the same time point. + * + * @param rankingInfo + * materialized ranking information that is expensive to calculate, avoiding redundant calculations */ - Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache); - + Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, + WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache); + /** * Calls {@link #getWindWithConfidence(Position, TimePoint, Iterable)} and excludes those wind sources listed in * {@link #getWindSourcesToExclude}. @@ -818,13 +823,4 @@ public interface TrackedRace extends Serializable { Duration getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, NoWindException; void setPolarDataService(PolarDataService polarDataService); - - /** - * With a handicap system in place, computes the windward distance to the race's overall leader based on the correction factors - * introduced by the handicap's ranking and scoring system. For one-design classes, this method returns the same result as - * {@link #getWindwardDistanceToOverallLeader(Competitor, TimePoint, WindPositionMode)}. - */ - Distance getCorrectedWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode) throws NoWindException; - } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java index 227a8a216f5..e998afc4a5e 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRegatta.java @@ -29,8 +29,31 @@ import com.sap.sse.common.TimePoint; public interface TrackedRegatta extends Serializable { Regatta getRegatta(); + /** + * Callers must {@link #lockTrackedRacesForRead() acquire the read lock} before calling this method and hold on to the lock + * while iterating over the data structure returned. Example: + *
+     *     trackedRegatta.lockTrackedRacesForRead();
+     *     try {
+     *         for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) {
+     *             // do something
+     *         }
+     *     } finally {
+     *         trackedRegatta.unlockTrackedRacesAfterRead();
+     *     }
+     * 
+ * The method will throw an {@link IllegalArgumentException} if the caller fails to do so. + */ Iterable getTrackedRaces(); + void lockTrackedRacesForRead(); + + void unlockTrackedRacesAfterRead(); + + void lockTrackedRacesForWrite(); + + void unlockTrackedRacesAfterWrite(); + Iterable getTrackedRaces(BoatClass boatClass); /** @@ -72,5 +95,5 @@ public interface TrackedRegatta extends Serializable { int getNetPoints(Competitor competitor, TimePoint timePoint) throws NoWindException; void removeTrackedRace(RaceDefinition raceDefinition); - + } \ No newline at end of file diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java index b65c6e245d1..7b93db79d07 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegImpl.java @@ -12,6 +12,7 @@ import java.util.List; import java.util.Map; import java.util.Set; import java.util.concurrent.Callable; +import java.util.concurrent.ConcurrentHashMap; import java.util.logging.Logger; import com.sap.sailing.domain.base.BoatClass; @@ -57,7 +58,7 @@ public class TrackedLegImpl implements TrackedLeg { private final Leg leg; private final Map trackedLegsOfCompetitors; private TrackedRaceImpl trackedRace; - private transient Map> competitorTracksOrderedByRank; + private transient ConcurrentHashMap> competitorTracksOrderedByRank; public TrackedLegImpl(DynamicTrackedRaceImpl trackedRace, Leg leg, Iterable competitors) { super(); @@ -68,12 +69,12 @@ public class TrackedLegImpl implements TrackedLeg { trackedLegsOfCompetitors.put(competitor, new TrackedLegOfCompetitorImpl(this, competitor)); } trackedRace.addListener(new CacheClearingRaceChangeListener()); - competitorTracksOrderedByRank = new HashMap<>(); + competitorTracksOrderedByRank = new ConcurrentHashMap<>(); } private void readObject(ObjectInputStream ois) throws ClassNotFoundException, IOException { ois.defaultReadObject(); - competitorTracksOrderedByRank = new HashMap<>(); + competitorTracksOrderedByRank = new ConcurrentHashMap<>(); } @Override @@ -124,11 +125,9 @@ public class TrackedLegImpl implements TrackedLeg { List getCompetitorTracksOrderedByRank(TimePoint timePoint, WindLegTypeAndLegBearingCache cache) { List rankedCompetitorList; - synchronized (competitorTracksOrderedByRank) { - rankedCompetitorList = competitorTracksOrderedByRank.get(timePoint); - if (rankedCompetitorList != null) { - rankedCompetitorList = new ArrayList(rankedCompetitorList); - } + rankedCompetitorList = competitorTracksOrderedByRank.get(timePoint); + if (rankedCompetitorList != null) { + rankedCompetitorList = new ArrayList(rankedCompetitorList); } if (rankedCompetitorList == null) { rankedCompetitorList = new ArrayList(); @@ -140,9 +139,7 @@ public class TrackedLegImpl implements TrackedLeg { // an the asynchronous nature of how the data is being received Collections.sort(rankedCompetitorList, getTrackedRace().getRankingMetric().getLegRankingComparator(this, timePoint, cache)); rankedCompetitorList = Collections.unmodifiableList(rankedCompetitorList); - synchronized (competitorTracksOrderedByRank) { - competitorTracksOrderedByRank.put(timePoint, rankedCompetitorList); - } + competitorTracksOrderedByRank.put(timePoint, rankedCompetitorList); if (Util.size(getTrackedLegsOfCompetitors()) != rankedCompetitorList.size()) { logger.warning("Number of competitors in leg (" + Util.size(getTrackedLegsOfCompetitors()) + ") differs from number of competitors in race (" @@ -302,9 +299,7 @@ public class TrackedLegImpl implements TrackedLeg { } private void clearCaches() { - synchronized (competitorTracksOrderedByRank) { - competitorTracksOrderedByRank.clear(); - } + competitorTracksOrderedByRank.clear(); } @Override diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java index 5f84ac200b6..b0bdde607a6 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java @@ -25,6 +25,7 @@ import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.leaderboard.caching.LeaderboardDTOCalculationReuseCache; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.Maneuver; import com.sap.sailing.domain.tracking.MarkPassing; @@ -406,17 +407,17 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { } @Override - public Distance getWindwardDistanceToOverallLeader(TimePoint timePoint, WindPositionMode windPositionMode) { - return getWindwardDistanceToOverallLeader(timePoint, windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint)); + public Distance getWindwardDistanceToCompetitorFarthestAhead(TimePoint timePoint, WindPositionMode windPositionMode, final RankingInfo rankingInfo) { + return getWindwardDistanceToCompetitorFarthestAhead(timePoint, windPositionMode, rankingInfo, new LeaderboardDTOCalculationReuseCache(timePoint)); } @Override - public Distance getWindwardDistanceToOverallLeader(TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(TimePoint timePoint, WindPositionMode windPositionMode, final RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { // FIXME bug 607 it seems the following fetches the leader of this leg, not the overall leader; validate!!! Use getTrackedRace().getRanks() instead - Competitor leader = getTrackedRace().getOverallLeader(timePoint, cache); - TrackedLegOfCompetitor leaderLeg = getTrackedRace().getCurrentLeg(leader, timePoint); + Competitor competitorFarthestAhead = rankingInfo.getCompetitorFarthestAhead(); + TrackedLegOfCompetitor leaderLeg = getTrackedRace().getCurrentLeg(competitorFarthestAhead, timePoint); Distance result = null; - Position leaderPosition = getTrackedRace().getTrack(leader).getEstimatedPosition(timePoint, /* extrapolate */ false); + Position leaderPosition = getTrackedRace().getTrack(competitorFarthestAhead).getEstimatedPosition(timePoint, /* extrapolate */ false); Position currentPosition = getTrackedRace().getTrack(getCompetitor()).getEstimatedPosition(timePoint, /* extrapolate */ false); if (leaderPosition != null && currentPosition != null) { result = Distance.NULL; @@ -486,15 +487,15 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { } @Override - public Duration getGapToLeader(TimePoint timePoint, final Competitor leaderInLegAtTimePoint, WindPositionMode windPositionMode) - throws NoWindException { - return getGapToLeader(timePoint, leaderInLegAtTimePoint, windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint)); + public Duration getGapToLeader(TimePoint timePoint, final Competitor leaderInLegAtTimePoint, + final RankingInfo rankingInfo, WindPositionMode windPositionMode) throws NoWindException { + return getGapToLeader(timePoint, leaderInLegAtTimePoint, windPositionMode, rankingInfo, new LeaderboardDTOCalculationReuseCache(timePoint)); } @Override public Duration getGapToLeader(TimePoint timePoint, final Competitor leaderInLegAtTimePoint, - WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) { - return getGapToLeader(timePoint, ()->leaderInLegAtTimePoint, windPositionMode, cache); + WindPositionMode windPositionMode, final RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { + return getGapToLeader(timePoint, ()->leaderInLegAtTimePoint, windPositionMode, rankingInfo, cache); } @FunctionalInterface @@ -503,17 +504,17 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { } @Override - public Duration getGapToLeader(final TimePoint timePoint, WindPositionMode windPositionMode) { - return getGapToLeader(timePoint, windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint)); + public Duration getGapToLeader(final TimePoint timePoint, final RankingInfo rankingInfo, WindPositionMode windPositionMode) { + return getGapToLeader(timePoint, windPositionMode, rankingInfo, new LeaderboardDTOCalculationReuseCache(timePoint)); } @Override - public Duration getGapToLeader(final TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) { + public Duration getGapToLeader(final TimePoint timePoint, WindPositionMode windPositionMode, final RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { return getGapToLeader(timePoint, ()->getTrackedLeg().getLeader(hasFinishedLeg(timePoint) ? getFinishTime() : timePoint), - windPositionMode, new LeaderboardDTOCalculationReuseCache(timePoint)); + windPositionMode, rankingInfo, new LeaderboardDTOCalculationReuseCache(timePoint)); } - private Duration getGapToLeader(TimePoint timePoint, LeaderGetter leaderGetter, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) { + private Duration getGapToLeader(TimePoint timePoint, LeaderGetter leaderGetter, WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { // If a competitor already completed this leg, compute the estimated arrival time at the // end of this leg and compare to the first mark passing for the end of this leg; if this leg's competitor also already // finished the leg, return the difference between this competitor's leg completion time point and the leader's completion diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java index 1073903a965..13552f9dfbf 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java @@ -107,6 +107,7 @@ import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.racelogtracking.DeviceMapping; import com.sap.sailing.domain.ranking.OneDesignRankingMetric; import com.sap.sailing.domain.ranking.RankingMetric; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.ranking.RankingMetricConstructor; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.GPSFixTrack; @@ -1376,12 +1377,12 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl @Override public MarkPassing getMarkPassing(Competitor competitor, Waypoint waypoint) { - final NavigableSet markPassings = getMarkPassings(competitor); + final Iterable markPassings = getMarkPassingsInOrder(waypoint); if (markPassings != null) { lockForRead(markPassings); try { for (MarkPassing markPassing : markPassings) { - if (markPassing.getWaypoint() == waypoint) { + if (markPassing.getCompetitor() == competitor) { return markPassing; } } @@ -2869,15 +2870,17 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl } @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { final TrackedLegOfCompetitor trackedLeg = getTrackedLeg(competitor, timePoint); - return trackedLeg == null ? null : trackedLeg.getWindwardDistanceToOverallLeader(timePoint, windPositionMode); + return trackedLeg == null ? null : trackedLeg.getWindwardDistanceToCompetitorFarthestAhead(timePoint, windPositionMode, + getRankingMetric().getRankingInfo(timePoint)); } @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, + WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { final TrackedLegOfCompetitor trackedLeg = getTrackedLeg(competitor, timePoint); - return trackedLeg == null ? null : trackedLeg.getWindwardDistanceToOverallLeader(timePoint, windPositionMode, cache); + return trackedLeg == null ? null : trackedLeg.getWindwardDistanceToCompetitorFarthestAhead(timePoint, windPositionMode, rankingInfo, cache); } @Override @@ -3584,11 +3587,4 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl public void setPolarDataService(PolarDataService polarDataService) { this.polarDataService = polarDataService; } - - @Override - public Distance getCorrectedWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode) throws NoWindException { - // TODO bug 1018, see comment #17: implement the calculation for corrected windward distance - return getWindwardDistanceToOverallLeader(competitor, timePoint, windPositionMode); - } } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java index 131b490aaec..f34596a7855 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRegattaImpl.java @@ -7,6 +7,7 @@ import java.util.ArrayList; import java.util.Collection; import java.util.HashMap; import java.util.HashSet; +import java.util.List; import java.util.Map; import java.util.Set; import java.util.logging.Level; @@ -26,6 +27,9 @@ import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.TrackedRegatta; import com.sap.sailing.domain.tracking.WindStore; import com.sap.sse.common.TimePoint; +import com.sap.sse.common.Util; +import com.sap.sse.concurrent.LockUtil; +import com.sap.sse.concurrent.NamedReentrantReadWriteLock; public class TrackedRegattaImpl implements TrackedRegatta { private static final long serialVersionUID = 6480508193567014285L; @@ -33,12 +37,24 @@ public class TrackedRegattaImpl implements TrackedRegatta { private static final Logger logger = Logger.getLogger(TrackedRegattaImpl.class.getName()); private final Regatta regatta; + + /** + * Guards access to {@link #trackedRaces}. Callers of {@link #getTrackedRaces()} need to acquire the + * read lock before iterating. + */ + private final NamedReentrantReadWriteLock trackedRacesLock; + + /** + * Guarded by {@link #trackedRacesLock} + */ private final Map trackedRaces; + private final Map> trackedRacesByBoatClass; private transient Set raceListeners; public TrackedRegattaImpl(Regatta regatta) { super(); + trackedRacesLock = new NamedReentrantReadWriteLock("trackeRaces lock for tracked regatta "+regatta.getName(), /* fair */ false); this.regatta = regatta; this.trackedRaces = new HashMap(); this.trackedRacesByBoatClass = new HashMap>(); @@ -49,10 +65,33 @@ public class TrackedRegattaImpl implements TrackedRegatta { ois.defaultReadObject(); this.raceListeners = new HashSet(); } + + @Override + public void lockTrackedRacesForRead() { + LockUtil.lockForRead(trackedRacesLock); + } + + @Override + public void unlockTrackedRacesAfterRead() { + LockUtil.unlockAfterRead(trackedRacesLock); + } + + @Override + public void lockTrackedRacesForWrite() { + LockUtil.lockForWrite(trackedRacesLock); + } + + @Override + public void unlockTrackedRacesAfterWrite() { + LockUtil.unlockAfterWrite(trackedRacesLock); + } private void writeObject(ObjectOutputStream oos) throws IOException { - synchronized (trackedRaces) { + lockTrackedRacesForRead(); + try { oos.defaultWriteObject(); + } finally { + unlockTrackedRacesAfterRead(); } } /** @@ -68,10 +107,12 @@ public class TrackedRegattaImpl implements TrackedRegatta { @Override public void addTrackedRace(TrackedRace trackedRace) { - synchronized (trackedRaces) { + final TrackedRace oldTrackedRace; + LockUtil.lockForWrite(trackedRacesLock); + try { logger.info("adding tracked race for "+trackedRace.getRace()+" to tracked regatta "+getRegatta().getName()+ " with regatta hash code "+getRegatta().hashCode()); - TrackedRace oldTrackedRace = trackedRaces.put(trackedRace.getRace(), trackedRace); + oldTrackedRace = trackedRaces.put(trackedRace.getRace(), trackedRace); if (oldTrackedRace != trackedRace) { Collection coll = trackedRacesByBoatClass.get(trackedRace.getRace().getBoatClass()); if (coll == null) { @@ -79,24 +120,31 @@ public class TrackedRegattaImpl implements TrackedRegatta { trackedRacesByBoatClass.put(trackedRace.getRace().getBoatClass(), coll); } coll.add(trackedRace); - for (RaceListener listener : raceListeners) { - listener.raceAdded(trackedRace); - } - trackedRaces.notifyAll(); + } + } finally { + LockUtil.unlockAfterWrite(trackedRacesLock); + } + if (oldTrackedRace != trackedRace) { + for (RaceListener listener : raceListeners) { + listener.raceAdded(trackedRace); } } } @Override public void removeTrackedRace(RaceDefinition raceDefinition) { - synchronized (trackedRaces) { + LockUtil.lockForWrite(trackedRacesLock); + try { trackedRaces.remove(raceDefinition); + } finally { + LockUtil.unlockAfterWrite(trackedRacesLock); } } @Override public void removeTrackedRace(TrackedRace trackedRace) { - synchronized (trackedRaces) { + LockUtil.lockForWrite(trackedRacesLock); + try { trackedRaces.remove(trackedRace.getRace()); Collection trbbc = trackedRacesByBoatClass.get(trackedRace.getRace().getBoatClass()); if (trbbc != null) { @@ -108,7 +156,8 @@ public class TrackedRegattaImpl implements TrackedRegatta { for (RaceListener listener : raceListeners) { listener.raceRemoved(trackedRace); } - trackedRaces.notifyAll(); + } finally { + LockUtil.unlockAfterWrite(trackedRacesLock); } } @@ -119,6 +168,9 @@ public class TrackedRegattaImpl implements TrackedRegatta { @Override public Iterable getTrackedRaces() { + if (trackedRacesLock.getReadHoldCount() <= 0 && trackedRacesLock.getWriteHoldCount() <= 0) { + throw new IllegalStateException("Callers of TrackedRegatta.getTrackedRaces() must hold the read lock; see TrackedRegatta.lockTrackedRacesForRead()"); + } return trackedRaces.values(); } @@ -130,7 +182,8 @@ public class TrackedRegattaImpl implements TrackedRegatta { @Override public TrackedRace getTrackedRace(RaceDefinition race) { boolean interrupted = false; - synchronized (trackedRaces) { + lockTrackedRacesForRead(); + try { TrackedRace result = trackedRaces.get(race); while (!interrupted && result == null) { try { @@ -141,31 +194,48 @@ public class TrackedRegattaImpl implements TrackedRegatta { } } return result; + } finally { + unlockTrackedRacesAfterRead(); } } @Override public TrackedRace getExistingTrackedRace(RaceDefinition race) { - return trackedRaces.get(race); + lockTrackedRacesForRead(); + try { + return trackedRaces.get(race); + } finally { + unlockTrackedRacesAfterRead(); + } } @Override public void addRaceListener(RaceListener listener) { - raceListeners.add(listener); - synchronized (trackedRaces) { - for (TrackedRace trackedRace : getTrackedRaces()) { - listener.raceAdded(trackedRace); - } + final List trackedRacesCopy = new ArrayList<>(); + lockTrackedRacesForRead(); + try { + raceListeners.add(listener); + Util.addAll(getTrackedRaces(), trackedRacesCopy); + } finally { + unlockTrackedRacesAfterRead(); + } + for (TrackedRace trackedRace : trackedRacesCopy) { + listener.raceAdded(trackedRace); } } @Override public int getNetPoints(Competitor competitor, TimePoint timePoint) throws NoWindException { int result = 0; - for (TrackedRace trackedRace : getTrackedRaces()) { - result += trackedRace.getRank(competitor, timePoint); + lockTrackedRacesForRead(); + try { + for (TrackedRace trackedRace : getTrackedRaces()) { + result += trackedRace.getRank(competitor, timePoint); + } + return result; + } finally { + unlockTrackedRacesAfterRead(); } - return result; } @Override diff --git a/java/com.sap.sailing.feature/feature.xml b/java/com.sap.sailing.feature/feature.xml index 18033f4cc45..63b9d34a6fa 100755 --- a/java/com.sap.sailing.feature/feature.xml +++ b/java/com.sap.sailing.feature/feature.xml @@ -6,9 +6,9 @@ provider-name="SAP"> - The SAP Sailing Server. - -Provides connectivity to a TracTrac live GPS feed as well as a live feed from an Expedition system. + The SAP Sailing Server. + +Provides connectivity to a TracTrac live GPS feed as well as a live feed from an Expedition system. Computes leaderboard information for sailing races and offers RESTful APIs to all of this. diff --git a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java index 4636a629292..128b68ed781 100755 --- a/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java +++ b/java/com.sap.sailing.gwt.ui.test/src/com/sap/sailing/gwt/ui/test/MockedTrackedRace.java @@ -37,6 +37,7 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.ranking.RankingMetric; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.CourseDesignChangedListener; import com.sap.sailing.domain.tracking.DynamicGPSFixTrack; import com.sap.sailing.domain.tracking.DynamicRaceDefinitionSet; @@ -314,6 +315,22 @@ public class MockedTrackedRace implements DynamicTrackedRace { boolean useMarkPassingCalculator) { return null; } + + @Override + public void lockTrackedRacesForRead() { + } + + @Override + public void unlockTrackedRacesAfterRead() { + } + + @Override + public void lockTrackedRacesForWrite() { + } + + @Override + public void unlockTrackedRacesAfterWrite() { + } }; } @@ -372,7 +389,7 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { return null; } @@ -736,12 +753,6 @@ public class MockedTrackedRace implements DynamicTrackedRace { public void setPolarDataService(PolarDataService polarDataService) { } - @Override - public Distance getCorrectedWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode) { - return null; - } - @Override public Duration getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, NoWindException { @@ -749,8 +760,8 @@ public class MockedTrackedRace implements DynamicTrackedRace { } @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, + WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { return null; } diff --git a/java/com.sap.sailing.gwt.ui/CommonControls.css b/java/com.sap.sailing.gwt.ui/CommonControls.css old mode 100755 new mode 100644 index d476a4a7b31..a15efd6a2c7 --- a/java/com.sap.sailing.gwt.ui/CommonControls.css +++ b/java/com.sap.sailing.gwt.ui/CommonControls.css @@ -1,918 +1,925 @@ -@import url("SettingsDialog.css"); - -body ::-webkit-scrollbar { - width: 16px; - height: 10px; -} - -body ::-webkit-scrollbar-thumb { - min-height: 28px; - padding-top: 100px; - border-left: 6px solid transparent; - border-right: 1px solid transparent; - border-top: 0 solid transparent; - border-bottom: 0 solid transparent; - background-clip: padding-box; - background-color: rgba(0, 0, 0, 0.2); - -webkit-box-shadow: inset 1px 1px 0 rgba(0, 0, 0, 0.1), inset 0 -1px 0 - rgba(0, 0, 0, 0.07); -} - -body ::-webkit-scrollbar-button { - height: 0; - width: 0; -} - -body ::-webkit-scrollbar-track { - border-left: 6px solid transparent; - border-right: 1px solid transparent; -} - -.collapsablePanel { - background: #b6b6b6; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -o-border-radius: 5px; - -ms-border-radius: 5px; - border-radius: 5px; - overflow: hidden; - border: 1px solid #aaaaaa; - margin: 0 0 15px 0; -} - -.collapsablePanel-content { - background: #fff; - margin: 12px; - -webkit-border-radius: 5px; - -moz-border-radius: 5px; - -o-border-radius: 5px; - -ms-border-radius: 5px; - border-radius: 5px; - width: auto !important; - border: 1px solid #aaaaaa; -} - -.collapsablePanel-header { - background: url(images/titlebar-background.png) 0 0 repeat-x; - padding: 0; - margin: 0; - height: 42px; - position: relative; - -webkit-border-top-left-radius: 4px; - -moz-border-top-left-radius: 4px; - -o-border-top-left-radius: 4px; - -ms-border-top-left-radius: 4px; - border-top-left-radius: 4px; - -webkit-border-top-right-radius: 4px; - -moz-border-top-right-radius: 4px; - -o-border-top-right-radius: 4px; - -ms-border-top-right-radius: 4px; - border-top-right-radius: 4px; -} - -.collapsablePanel:last-child { - margin-bottom: 0; -} - -.collapsablePanel-open { - -} - -.collapsablePanel-closed { - -} - -.collapsablePanel-header-text { - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; - text-transform: uppercase; - margin: 15px 0 0 13px; - float: left; -} - -.collapsablePanel-header-image { - float: left; - margin: 13px 0 0 0; -} - -.collapsablePanel-header-collapse { - height: 100%; - display: block; - padding: 0 0 0 10px; - color: #3b3b3b; -} - -.collapsablePanel-header-collapse:hover { - color: #3b3b3b; -} - -.collapsablePanel-toolbar { - position: absolute; - top: 0; - right: 0; - height: 43px; - width: 43px; - overflow: hidden; -} - -.collapsablePanel-toolbar table { - border-collapse: collapse; -} - -.collapsablePanel-toolbar a { - display: block; - padding: 9px; -} - -.breadcrumbPanel-NextArrow { - display: none; -} - -.breadcrumbPanel-ActiveBreadcrumb { - font-size: 18px; - font-family: 'OpenSansLight', Arial, Verdana, sans-serif; - margin: 5px 10px 0 0; -} - -.breadcrumbPanel-InactiveBreadcrumb { - font-size: 24px; -} - -.breadcrumbPanel-ActiveBreadcrumb a, .breadcrumbPanel-InactiveBreadcrumb a, - .breadcrumbPanel-NextArrow a { - color: #A1A1A1; -} - -.breadcrumbPanel-ActiveBreadcrumb, .breadcrumbPanel-InactiveBreadcrumb, - .breadcrumbPanel-NextArrow { - color: #A1A1A1; -} - -.gwt-ToggleButton { - background: url(images/btn-cta-orangeleft.png) center top repeat-x; - padding: 8px; - margin: 0 11px 0 0; - border: 0; -} - -.gwt-ToggleButton-down { - background: url(images/btn-cta-orangeleft.png) center -30px repeat-x; - color: #fff; -} - -.gwt-SplitLayoutPanel-NorthSouthToggleButton-Panel { - position: absolute; - right: 10px; - top: 3px; -} - -.gwt-SplitLayoutPanel-EastToggleButton-Panel { - position: absolute; - bottom: 77px; - margin-left: -35px; -} - -.gwt-SplitLayoutPanel-EastToggleButton { - height: 40px; - background-color: white; - border: none; - -webkit-transform: rotate(-90deg); - -moz-transform: rotate(-90deg); - -o-transform: rotate(-90deg); - -ms-transform: rotate(-90deg); - padding-right: 25px; - padding-left: 30px; - margin-left: -15px; - border-bottom: 1px solid #cecece; - border-right: 1px solid #cecece; - border-left: 1px solid #cecece; - border-bottom-right-radius: 10px; - border-bottom-left-radius: 10px; -} - -.gwt-SplitLayoutPanel-EastToggleButton-Open { - background: white url(images/arrows_top_black_small.png) 96% 50% - no-repeat; -} - -.gwt-SplitLayoutPanel-EastToggleButton-Closed { - background: white url(images/arrows_bottom_black_small.png) 96% 50% - no-repeat; -} - -.gwt-SplitLayoutPanel-EastToggleButton:hover { - background-color: #ececec; -} - -.gwt-SplitLayoutPanel-EastToggleButton:focus { - outline: 0 !important; -} - -.gwt-SplitLayoutPanel-NorthSouthToggleButton { - height: 33px; - background-color: #fff; - border: 1px solid #cecece; - margin-left: 5px; - padding: 2px 40px; - border-radius: 7px; - margin-top: 0px; -} - -.gwt-SplitLayoutPanel-NorthSouthToggleButton-Closed { - background: white url(images/arrows_top_black_small.png) 95% 50% - no-repeat; - font-weight: normal; -} - -.gwt-SplitLayoutPanel-NorthSouthToggleButton-Open { - background: white url(images/arrows_bottom_black_small.png) 95% 50% - no-repeat; - font-weight: bold; - border-width: 3px; -} - -.gwt-SplitLayoutPanel-NorthSouthToggleButton:focus { - outline: 0 !important; -} - -.gwt-SplitLayoutPanel-NorthSouthToggleButton:hover { - background-color: #ececec; -} - -.gwt-SplitLayoutPanel-HDragger { - position: absolute; - height: 60px !important; - width: 25px !important; - margin-left: 3px; - top: 40% !important; - background: #ffffff url(images/thumb_vertical.png) 70% 50% no-repeat; - border: 2px solid #cecece; - border-left: 0px; - cursor: col-resize; - border-top-right-radius: 25px; - border-bottom-right-radius: 25px; - z-index: 1; -} - -.SplitLayoutPanel-Divider-Horizontal { - background: #ffffff; -} - -.SplitLayoutPanel-Divider-Vertical { - background: #ffffff; - border-top: 1px solid #cecece; -} - -.gwt-SplitLayoutPanel-VDragger { - position: absolute; - width: 60px !important; - height: 21px !important; - left: 48% !important; - top: 2px !important; - background: #ffffff url(images/thumb_horz.png) 50% 30% no-repeat; - cursor: row-resize; - border: 2px solid #cecece; - border-bottom: 0px; - border-top-left-radius: 25px; - border-top-right-radius: 25px; - opacity: .7; - z-index: 1; -} - -.gwt-SplitLayoutPanel-CloseAndSettingsButton-Panel { - position: absolute; - right: 2px; - top: 2px; - z-index: 1; -} - -.gwt-SplitLayoutPanel-CloseButton { - margin-left: 10px; - border-top-left-radius: 25px; - border-top-right-radius: 25px; - border-bottom-left-radius: 25px; - border-bottom-right-radius: 25px; - background: white url(images/close_black.png) center no-repeat; - z-index: 1; - width: 28px; - height: 28px; - cursor: pointer; -} - -.gwt-SplitLayoutPanel-CloseButton:focus { - outline: 0 !important; -} - -.gwt-SplitLayoutPanel-CloseButton:hover { - background-color: #ececec; -} - -.gwt-SettingsButton { - border: none; - background: grey url(images/SAP_RV_Settings.png) center no-repeat; - z-index: 1; - width: 28px; - height: 28px; - cursor: pointer; -} - -.gwt-SettingsButton:hover { - background-color: #cecece; -} - -.gwt-SettingsButton:focus { - outline: 0 !important; -} - -.gwt-MapSettingsButton { - margin-right: 30px; - margin-top: 5px; - border-top-left-radius: 25px; - border-top-right-radius: 25px; - border-bottom-left-radius: 25px; - border-bottom-right-radius: 25px; - border: none; - background: white url(images/SAP_RV_SettingsBlack.png) center no-repeat; - z-index: 1; - width: 28px; - height: 28px; - cursor: pointer; -} - -.gwt-ToggleButton-up-hovering, .gwt-ToggleButton-down-hovering { - background: url(images/btn-cta-orangeleft-down.png) center top repeat-x; - color: #fff; -} - -.gwt-TabBar { - margin-top: 20px; - border-bottom: 1px solid #dedede; - margin-bottom: 10px; -} - -.gwt-TabBar .gwt-TabBarFirst { - width: 5px; /* first tab distance from the left */ -} - -.gwt-TabBar .gwt-TabBarRest { - -} - -.gwt-TabBar .gwt-TabBarItem { - margin-left: 4px; - padding: 10px 12px; - cursor: pointer; - cursor: hand; - color: black; - font-weight: normal; - text-align: center; - background: #ebebeb; - border-radius: 4px 4px 0px 0px; - border-left: 1px solid #dedede; - border-top: 1px solid #dedede; - border-right: 1px solid #dedede; -} - -.gwt-TabBarItem:FOCUS { - outline: none; -} - -.gwt-TabBar .gwt-TabBarItem-selected { - cursor: default; - background: white; - color: black; -} - -.gwt-TabBar .gwt-TabBarItem-disabled { - cursor: default; - color: #999999; -} - -.gwt-TabPanel { - -} - -.titleLabelWrapper { - position: absolute; - width: 100%; -} - -.titleLabel { - text-align: center; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; - font-size: 18px; - color: #fff; - margin-top: 13px; -} - -.titleLabelRight { - text-align: right; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; - font-size: 18px; - color: #fff; - margin-top: 13px; - margin-right: 12px; -} - -.subTitleLabelWrapper { - position: absolute; - min-width: 200px; - text-align: right; - height: 68px; - right: 0; - top: 0; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; - font-size: 16px; - color: #fff; // - background: url(images/titleLabelWrapper.png) right center no-repeat; -} - -.subTitleLabel { - background: url(images/titleLabelInner.png) left center no-repeat; - height: 26px; - padding: 42px 7px 0 130px; -} - -.gwt-MenuBarPopup .menuPopupTopLeft { - background: none !important; -} - -.gwt-MenuBarPopup .menuPopupTopCenter { - background: none !important; -} - -.gwt-MenuBarPopup .menuPopupTopCenter { - background: none !important; -} - -.gwt-MenuBarPopup .menuPopupTopRight { - background: none !important; -} - -.gwt-MenuBarPopup .menuPopupMiddleLeft { - background: none !important; -} - -.gwt-MenuBarPopup .menuPopupMiddleRight { - background: none !important; -} - -.gwt-MenuBarPopup .menuPopupMiddleRight { - background: none !important; -} - -.gwt-MenuBarPopup .menuPopupBottomCenter { - background: none !important; -} - -.gwt-MenuBarPopup .menuPopupBottomRight { - background: none !important; -} - -.gwt-MenuBarPopup .menuPopupBottomLeft { - background: none !important; -} - -.gwt-MenuBar-vertical .gwt-MenuItem { - padding: 10px; - padding-bottom: 5px; - padding-top: 5px; -} - -.gwt-MenuBar .gwt-MenuItem-selected { - background: none repeat scroll 0 0 #efaa00; -} - -.dateBoxPopup { - z-Index: 2000; -} - -.timeLinePanel { - color: black; - width: 100%; - position: fixed; - bottom: 0; - z-index: 9; - background: #f6f6f6; - border-top: 2px solid #cecece; -} - -.timeLineInnerBgPanel { - -} - -.footerShadowPanel { - position: fixed; - height: 15px; - bottom: 0; - background: url(images/footer_shadow.png) left bottom repeat-x; - width: 100%; - z-index: 10; -} - -.timePanel-controls { - margin: 0; - padding: 7px 12px 0 12px; - height: 29px; -} - -.timePanel-controls-play { - float: left; - padding: 0 10px 0 0; - border-right: 1px solid #fcbb22; - height: 24px; - margin: 0 14px 0 0; -} - -.timePanel-timeslider-play { - float: left; - padding: 10px 4px; - height: 24px; - margin: 5px 14px 5px 0; -} - -.timePanel-controls-playSpeed { - vertical-align: middle; - line-height: 25px; - float: left; - padding: 0 14px 0 0; - margin: 0 14px 0 0; - border-right: 1px solid #fcbb22; - height: 24px; -} - -.timePanel-label { - float: left; -} - -.timePanel-controls-time { - float: left; - padding: 0 0 0 0; -} - -.timePanel-controls-time .dateLabel, .timePanel-controls-time .timeLabel - { - padding: 4px 20px 0 0; - float: left; -} - -.timePanel-controls-timeToStart { - float: left; - padding: 0 0 0 0; - border-left: 1px solid #fcbb22; -} - -.timePanel-controls-timeToStart .timeToStartLabel { - float: left; - padding: 4px 20px 0 20px; -} - -.timePanel-controls-timeDelay { - float: right; - margin: 0 50px 0 0; -} - -.timePanel-controls-playmode { - float: left; - padding: 0 14px 0 0; - margin: 0 14px 0 0; - height: 24px; - border-right: 1px solid #fcbb22; -} - -.timePanel-controls-playmode .playModeLabel { - padding: 4px 0 0 5px; -} - -.timePanelSettings { - float: right; -} - -.timePanelSlider { - margin: 0 12px; - height: 49px; - padding-top: 5px; -} - -.timePanelInnerWrapper { - -} - -.LogoAndTitlePanel { - height: 68px; - font-family: Arial, Verdana, sans-serif; - position: fixed; - width: 100%; - top: 0; - background-image: url(images/white_gradient_header.png), - url(images/header_bg_modern.png); - background-position: center top, 0 0; - background-repeat: no-repeat, repeat-x; - box-shadow: 0 0 20px #858585; - -moz-box-shadow: 0 0 20px #858585; - -webkit-box-shadow: 0 0 20px #858585; - -o-box-shadow: 0 0 20px #858585; - -ms-box-shadow: 0 0 20px #858585; - z-index: 1; -} - -.LogoAndTitlePanel:after { - content: " "; - display: block; - height: 0; - clear: both; - visibility: hidden; -} - -.globalNavigation { - position: absolute; - top: 0; - right: 0; -} - -.globalNavigation-Link, .globalNavigation-Link:visited { - font-family: OpenSansSemiBold, Verdana, Arial, sans-serif; - border: none; - display: inline-block; - padding: 2px 10px 4px; - color: #fff; - text-decoration: none; - -moz-border-radius: 5px; - -webkit-border-radius: 5px; - border-radius: 5px; - -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); - text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25); - border-bottom: 1px solid rgba(0, 0, 0, 0.25); - cursor: pointer; - height: 24px; - white-space: nowrap; -} - -.globalNavigation-Link:active { - top: 1px; - position: relative; - border: none; -} - -.globalNavigation-Link:hover { - background: url(images/btn-cta-orangeleft.png) center -30px repeat-x; - cursor: pointer; -} - -.globalNavigation-leaderBoardGroup a, .globalNavigation-home a { - background: url(images/globalNavigation-home_bg.png) 11px 8px no-repeat; -} - -.globalNavigation-leaderBoard a { - background: url(images/globalNavigation-leaderBoard_bg.png) 11px 8px - no-repeat; -} - -.globalNavigation-leaderBoardGroup, .globalNavigation-leaderBoard, - .globalNavigation-home { - margin: -4px 10px 0 0; - padding: 0px 0px 4px; - float: left; - background: #7c7b7c url(images/alert-overlay.png) repeat-x; -} - -.globalNavigation-leaderBoard { - margin-right: 5px; -} - -.globalNavigation-leaderBoardGroup a, .globalNavigation-leaderBoard a, - .globalNavigation-home a { - color: #fff; - text-decoration: none; - display: block; - padding: 8px 13px 6px 36px; -} - -.sapLogo { - float: left; - margin-top: 13px; - margin-left: 12px; -} - -.sailingAnalyticsLabelPanel { - margin: 10px 0 0 0; -} - -.sailingAnalyticsLabel { - float: left; - color: #fff; - font-size: 14px; - font-weight: bold; - margin: 15px 0 0 0; -} - -.gwt-DialogBox { - line-height: 1.4; - background-color: #cecece; - border: 1px solid #3e3e3e; - border-radius: 4px; - box-shadow: 0px 0px 10px 0px #3e3e3e; -} - -.gwt-PopupPanelGlass { - background-color: rgba(140, 140, 140, 0.5); -} - -.gwt-DialogBox .Caption { - font-size: 14px; - padding: 8px 10px; - background-color: #ededed; - border-bottom: 1px solid #3e3e3e; - font-family: OpenSansSemiBold, Verdana, Arial, sans-serif; -} - -.gwt-DialogBox .dialogContent { - background: #ffffff; - padding: 10px; -} - -.gwt-Button, .gwt-Button:visited { - font-size: 0, 9rem; - display: inline-block; - color: #fff; - border: none; - padding: 5px 10px; - border-radius: 4px; - background-color: #efaa00; - -webkit-transition: background-color 0.2s; - -moz-transition: background-color 0.2s; - transition: background-color 0.2s; - text-decoration: none; - cursor: pointer; -} - -.gwt-Button:active { - border: none; -} - -.gwt-Button:hover { - background-color: #d79900; - outline: none; - text-decoration: none; -} - -.gwt-Button[disabled] { - cursor: not-allowed; - background: #cfcfcf; -} - -.gwt-Button[disabled]:hover { - cursor: default; - background: #cfcfcf; - cursor: not-allowed; -} - -.gwt-Button[disabled]:active { - cursor: default; - background: #cfcfcf; - cursor: not-allowed; -} - -.gwt-ButtonLarge -.gwt-ButtonMedium -.gwt-ButtonSmall - -.gwt-DialogBox .boxPanel { - padding: 0 0 10px 0; -} - -.gwt-DialogBox .boxPanel:after { - display: inline-block; - content: " "; - display: block; - height: 0; - clear: both; - visibility: hidden; -} - -.gwt-DialogBox .settingsDialogLabel { - float: left; - padding: 3px 6px 0 0; -} - -.gwt-DialogBox .settingsDialogValue { - float: right; -} - -.gwt-DialogBox .additionalWidgets { - padding: 5px 0 0 0; - text-align: right; -} - -.gwt-DialogBox .gwt-Label { - line-height: 1.4; -} - -.timingSettings .dialogInnerContent { - padding: 0 9px 0 9px; -} - -.timingSettings .gwt-Label { - float: left; - padding: 0 10px 0 0; -} - -.helpPanel { - margin-top: 15px; -} - -.minMaxBackgroundBar { - left: 0px; - background-image: url(images/yellowBar.png); - background-position: left; - background-repeat: no-repeat; -} - -.minMaxBackgroundBarBad { - left: 0px; - background-image: url(images/redBar.png); - background-position: left; - background-repeat: no-repeat; -} - -.minMaxBackgroundBarGood { - left: 0px; - background-image: url(images/greenBar.png); - background-position: left; - background-repeat: no-repeat; -} - -.inlineButton { - height: 20px; - padding: 0px 10px 3px; - float: left; - background: #de8a2e url(images/alert-overlay.png) repeat-x; - font-family: OpenSansRegular, Arial, Verdana, sans-serif; -} - -.chartCloseButton { - cursor: pointer; - background: url(images/closeButton.png) right center no-repeat; - height: 18px; - width: 60px; - text-align: left; -} - -video { - max-width: 100%; - height: auto; -} - -.pointeredLink { -} - -.betterCheckboxCellSelected { - margin: 0px; - height: 18px; - width: 18px; - border: 1px; - background-color: #acacac; - vertical-align: middle; -} - -.betterCheckboxCellDeselected { - margin-left: 2px; - /* account for the selection border that pushes a selected checkbox to the side */ - margin-top: 0px; - margin-bottom: 0px; - margin-right: 0px; - border: 1px; - height: 18px; - width: 18px; - vertical-align: middle; -} - -.LeaderboardPanel-FilterControl-Panel { - width: 380px; - border-top: 1px solid #ececec; - margin-top: 3px; -} - -.LeaderboardPanel-FilterClear-Button { - margin-top: 2px; - font-size: 10px; - outline: 0; - height: 23px; - background-color: #fff; - border: 1px solid #cecece; - border-radius: 7px; -} - -.LeaderboardPanel-FilterClear-Button:hover { - background-color: #ececec; -} - -.LeaderboardPanel-FilterControl-StatusLabel { - padding: 8px 2px; - font-size: 10px; -} - -label { - padding-left: 4px; +@import url("SettingsDialog.css"); + +body ::-webkit-scrollbar { + width: 16px; + height: 10px; +} + +body ::-webkit-scrollbar-thumb { + min-height: 28px; + padding-top: 100px; + border-left: 6px solid transparent; + border-right: 1px solid transparent; + border-top: 0 solid transparent; + border-bottom: 0 solid transparent; + background-clip: padding-box; + background-color: rgba(0, 0, 0, 0.2); + -webkit-box-shadow: inset 1px 1px 0 rgba(0, 0, 0, 0.1), inset 0 -1px 0 + rgba(0, 0, 0, 0.07); +} + +body ::-webkit-scrollbar-button { + height: 0; + width: 0; +} + +body ::-webkit-scrollbar-track { + border-left: 6px solid transparent; + border-right: 1px solid transparent; +} + +.collapsablePanel { + background: #b6b6b6; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -o-border-radius: 5px; + -ms-border-radius: 5px; + border-radius: 5px; + overflow: hidden; + border: 1px solid #aaaaaa; + margin: 0 0 15px 0; +} + +.collapsablePanel-content { + background: #fff; + margin: 12px; + -webkit-border-radius: 5px; + -moz-border-radius: 5px; + -o-border-radius: 5px; + -ms-border-radius: 5px; + border-radius: 5px; + width: auto !important; + border: 1px solid #aaaaaa; +} + +.collapsablePanel-header { + background: url(images/titlebar-background.png) 0 0 repeat-x; + padding: 0; + margin: 0; + height: 42px; + position: relative; + -webkit-border-top-left-radius: 4px; + -moz-border-top-left-radius: 4px; + -o-border-top-left-radius: 4px; + -ms-border-top-left-radius: 4px; + border-top-left-radius: 4px; + -webkit-border-top-right-radius: 4px; + -moz-border-top-right-radius: 4px; + -o-border-top-right-radius: 4px; + -ms-border-top-right-radius: 4px; + border-top-right-radius: 4px; +} + +.collapsablePanel:last-child { + margin-bottom: 0; +} + +.collapsablePanel-open { + +} + +.collapsablePanel-closed { + +} + +.collapsablePanel-header-text { + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600; + text-transform: uppercase; + margin: 15px 0 0 13px; + float: left; +} + +.collapsablePanel-header-image { + float: left; + margin: 13px 0 0 0; +} + +.collapsablePanel-header-collapse { + height: 100%; + display: block; + padding: 0 0 0 10px; + color: #3b3b3b; +} + +.collapsablePanel-header-collapse:hover { + color: #3b3b3b; +} + +.collapsablePanel-toolbar { + position: absolute; + top: 0; + right: 0; + height: 43px; + width: 43px; + overflow: hidden; +} + +.collapsablePanel-toolbar table { + border-collapse: collapse; +} + +.collapsablePanel-toolbar a { + display: block; + padding: 9px; +} + +.breadcrumbPanel-NextArrow { + display: none; +} + +.breadcrumbPanel-ActiveBreadcrumb { + font-size: 18px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 300; + margin: 5px 10px 0 0; +} + +.breadcrumbPanel-InactiveBreadcrumb { + font-size: 24px; +} + +.breadcrumbPanel-ActiveBreadcrumb a, .breadcrumbPanel-InactiveBreadcrumb a, + .breadcrumbPanel-NextArrow a { + color: #A1A1A1; +} + +.breadcrumbPanel-ActiveBreadcrumb, .breadcrumbPanel-InactiveBreadcrumb, + .breadcrumbPanel-NextArrow { + color: #A1A1A1; +} + +.gwt-ToggleButton { + background: url(images/btn-cta-orangeleft.png) center top repeat-x; + padding: 8px; + margin: 0 11px 0 0; + border: 0; +} + +.gwt-ToggleButton-down { + background: url(images/btn-cta-orangeleft.png) center -30px repeat-x; + color: #fff; +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-Panel { + position: absolute; + right: 10px; + top: 3px; +} + +.gwt-SplitLayoutPanel-EastToggleButton-Panel { + position: absolute; + bottom: 77px; + margin-left: -35px; +} + +.gwt-SplitLayoutPanel-EastToggleButton { + height: 40px; + background-color: white; + border: none; + -webkit-transform: rotate(-90deg); + -moz-transform: rotate(-90deg); + -o-transform: rotate(-90deg); + -ms-transform: rotate(-90deg); + padding-right: 25px; + padding-left: 30px; + margin-left: -15px; + border-bottom: 1px solid #cecece; + border-right: 1px solid #cecece; + border-left: 1px solid #cecece; + border-bottom-right-radius: 10px; + border-bottom-left-radius: 10px; +} + +.gwt-SplitLayoutPanel-EastToggleButton-Open { + background: white url(images/arrows_top_black_small.png) 96% 50% + no-repeat; +} + +.gwt-SplitLayoutPanel-EastToggleButton-Closed { + background: white url(images/arrows_bottom_black_small.png) 96% 50% + no-repeat; +} + +.gwt-SplitLayoutPanel-EastToggleButton:hover { + background-color: #ececec; +} + +.gwt-SplitLayoutPanel-EastToggleButton:focus { + outline: 0 !important; +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton { + height: 33px; + background-color: #fff; + border: 1px solid #cecece; + margin-left: 5px; + padding: 2px 40px; + border-radius: 7px; + margin-top: 0px; +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-Closed { + background: white url(images/arrows_top_black_small.png) 95% 50% + no-repeat; + font-weight: normal; +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton-Open { + background: white url(images/arrows_bottom_black_small.png) 95% 50% + no-repeat; + font-weight: bold; + border-width: 3px; +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton:focus { + outline: 0 !important; +} + +.gwt-SplitLayoutPanel-NorthSouthToggleButton:hover { + background-color: #ececec; +} + +.gwt-SplitLayoutPanel-HDragger { + position: absolute; + height: 60px !important; + width: 25px !important; + margin-left: 3px; + top: 40% !important; + background: #ffffff url(images/thumb_vertical.png) 70% 50% no-repeat; + border: 2px solid #cecece; + border-left: 0px; + cursor: col-resize; + border-top-right-radius: 25px; + border-bottom-right-radius: 25px; + z-index: 1; +} + +.SplitLayoutPanel-Divider-Horizontal { + background: #ffffff; +} + +.SplitLayoutPanel-Divider-Vertical { + background: #ffffff; + border-top: 1px solid #cecece; +} + +.gwt-SplitLayoutPanel-VDragger { + position: absolute; + width: 60px !important; + height: 21px !important; + left: 48% !important; + top: 2px !important; + background: #ffffff url(images/thumb_horz.png) 50% 30% no-repeat; + cursor: row-resize; + border: 2px solid #cecece; + border-bottom: 0px; + border-top-left-radius: 25px; + border-top-right-radius: 25px; + opacity: .7; + z-index: 1; +} + +.gwt-SplitLayoutPanel-CloseAndSettingsButton-Panel { + position: absolute; + right: 2px; + top: 2px; + z-index: 1; +} + +.gwt-SplitLayoutPanel-CloseButton { + margin-left: 10px; + border-top-left-radius: 25px; + border-top-right-radius: 25px; + border-bottom-left-radius: 25px; + border-bottom-right-radius: 25px; + background: white url(images/close_black.png) center no-repeat; + z-index: 1; + width: 28px; + height: 28px; + cursor: pointer; +} + +.gwt-SplitLayoutPanel-CloseButton:focus { + outline: 0 !important; +} + +.gwt-SplitLayoutPanel-CloseButton:hover { + background-color: #ececec; +} + +.gwt-SettingsButton { + border: none; + background: grey url(images/SAP_RV_Settings.png) center no-repeat; + z-index: 1; + width: 28px; + height: 28px; + cursor: pointer; +} + +.gwt-SettingsButton:hover { + background-color: #cecece; +} + +.gwt-SettingsButton:focus { + outline: 0 !important; +} + +.gwt-MapSettingsButton { + margin-right: 30px; + margin-top: 5px; + border-top-left-radius: 25px; + border-top-right-radius: 25px; + border-bottom-left-radius: 25px; + border-bottom-right-radius: 25px; + border: none; + background: white url(images/SAP_RV_SettingsBlack.png) center no-repeat; + z-index: 1; + width: 28px; + height: 28px; + cursor: pointer; +} + +.gwt-ToggleButton-up-hovering, .gwt-ToggleButton-down-hovering { + background: url(images/btn-cta-orangeleft-down.png) center top repeat-x; + color: #fff; +} + +.gwt-TabBar { + margin-top: 20px; + border-bottom: 1px solid #dedede; + margin-bottom: 10px; +} + +.gwt-TabBar .gwt-TabBarFirst { + width: 5px; /* first tab distance from the left */ +} + +.gwt-TabBar .gwt-TabBarRest { + +} + +.gwt-TabBar .gwt-TabBarItem { + margin-left: 4px; + padding: 10px 12px; + cursor: pointer; + cursor: hand; + color: black; + font-weight: normal; + text-align: center; + background: #ebebeb; + border-radius: 4px 4px 0px 0px; + border-left: 1px solid #dedede; + border-top: 1px solid #dedede; + border-right: 1px solid #dedede; +} + +.gwt-TabBarItem:FOCUS { + outline: none; +} + +.gwt-TabBar .gwt-TabBarItem-selected { + cursor: default; + background: white; + color: black; +} + +.gwt-TabBar .gwt-TabBarItem-disabled { + cursor: default; + color: #999999; +} + +.gwt-TabPanel { + +} + +.titleLabelWrapper { + position: absolute; + width: 100%; +} + +.titleLabel { + text-align: center; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600; + font-size: 18px; + color: #fff; + margin-top: 13px; +} + +.titleLabelRight { + text-align: right; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600; + font-size: 18px; + color: #fff; + margin-top: 13px; + margin-right: 12px; +} + +.subTitleLabelWrapper { + position: absolute; + min-width: 200px; + text-align: right; + height: 68px; + right: 0; + top: 0; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600; + font-size: 16px; + color: #fff; // + background: url(images/titleLabelWrapper.png) right center no-repeat; +} + +.subTitleLabel { + background: url(images/titleLabelInner.png) left center no-repeat; + height: 26px; + padding: 42px 7px 0 130px; +} + +.gwt-MenuBarPopup .menuPopupTopLeft { + background: none !important; +} + +.gwt-MenuBarPopup .menuPopupTopCenter { + background: none !important; +} + +.gwt-MenuBarPopup .menuPopupTopCenter { + background: none !important; +} + +.gwt-MenuBarPopup .menuPopupTopRight { + background: none !important; +} + +.gwt-MenuBarPopup .menuPopupMiddleLeft { + background: none !important; +} + +.gwt-MenuBarPopup .menuPopupMiddleRight { + background: none !important; +} + +.gwt-MenuBarPopup .menuPopupMiddleRight { + background: none !important; +} + +.gwt-MenuBarPopup .menuPopupBottomCenter { + background: none !important; +} + +.gwt-MenuBarPopup .menuPopupBottomRight { + background: none !important; +} + +.gwt-MenuBarPopup .menuPopupBottomLeft { + background: none !important; +} + +.gwt-MenuBar-vertical .gwt-MenuItem { + padding: 10px; + padding-bottom: 5px; + padding-top: 5px; +} + +.gwt-MenuBar .gwt-MenuItem-selected { + background: none repeat scroll 0 0 #efaa00; +} + +.dateBoxPopup { + z-Index: 2000; +} + +.timeLinePanel { + color: black; + width: 100%; + position: fixed; + bottom: 0; + z-index: 9; + background: #f6f6f6; + border-top: 2px solid #cecece; +} + +.timeLineInnerBgPanel { + +} + +.footerShadowPanel { + position: fixed; + height: 15px; + bottom: 0; + background: url(images/footer_shadow.png) left bottom repeat-x; + width: 100%; + z-index: 10; +} + +.timePanel-controls { + margin: 0; + padding: 7px 12px 0 12px; + height: 29px; +} + +.timePanel-controls-play { + float: left; + padding: 0 10px 0 0; + border-right: 1px solid #fcbb22; + height: 24px; + margin: 0 14px 0 0; +} + +.timePanel-timeslider-play { + float: left; + padding: 10px 4px; + height: 24px; + margin: 5px 14px 5px 0; +} + +.timePanel-controls-playSpeed { + vertical-align: middle; + line-height: 25px; + float: left; + padding: 0 14px 0 0; + margin: 0 14px 0 0; + border-right: 1px solid #fcbb22; + height: 24px; +} + +.timePanel-label { + float: left; +} + +.timePanel-controls-time { + float: left; + padding: 0 0 0 0; +} + +.timePanel-controls-time .dateLabel, .timePanel-controls-time .timeLabel + { + padding: 4px 20px 0 0; + float: left; +} + +.timePanel-controls-timeToStart { + float: left; + padding: 0 0 0 0; + border-left: 1px solid #fcbb22; +} + +.timePanel-controls-timeToStart .timeToStartLabel { + float: left; + padding: 4px 20px 0 20px; +} + +.timePanel-controls-timeDelay { + float: right; + margin: 0 50px 0 0; +} + +.timePanel-controls-playmode { + float: left; + padding: 0 14px 0 0; + margin: 0 14px 0 0; + height: 24px; + border-right: 1px solid #fcbb22; +} + +.timePanel-controls-playmode .playModeLabel { + padding: 4px 0 0 5px; +} + +.timePanelSettings { + float: right; +} + +.timePanelSlider { + margin: 0 12px; + height: 49px; + padding-top: 5px; +} + +.timePanelInnerWrapper { + +} + +.LogoAndTitlePanel { + height: 68px; + font-family: Arial, Verdana, sans-serif; + position: fixed; + width: 100%; + top: 0; + background-image: url(images/white_gradient_header.png), + url(images/header_bg_modern.png); + background-position: center top, 0 0; + background-repeat: no-repeat, repeat-x; + box-shadow: 0 0 20px #858585; + -moz-box-shadow: 0 0 20px #858585; + -webkit-box-shadow: 0 0 20px #858585; + -o-box-shadow: 0 0 20px #858585; + -ms-box-shadow: 0 0 20px #858585; + z-index: 1; +} + +.LogoAndTitlePanel:after { + content: " "; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.globalNavigation { + position: absolute; + top: 0; + right: 0; +} + +.globalNavigation-Link, .globalNavigation-Link:visited { + font-family: 'Open Sans', Verdana, Arial, sans-serif; + font-weight: 600; + border: none; + display: inline-block; + padding: 2px 10px 4px; + color: #fff; + text-decoration: none; + -moz-border-radius: 5px; + -webkit-border-radius: 5px; + border-radius: 5px; + -moz-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + -webkit-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3); + text-shadow: 0 -1px 1px rgba(0, 0, 0, 0.25); + border-bottom: 1px solid rgba(0, 0, 0, 0.25); + cursor: pointer; + height: 24px; + white-space: nowrap; +} + +.globalNavigation-Link:active { + top: 1px; + position: relative; + border: none; +} + +.globalNavigation-Link:hover { + background: url(images/btn-cta-orangeleft.png) center -30px repeat-x; + cursor: pointer; +} + +.globalNavigation-leaderBoardGroup a, .globalNavigation-home a { + background: url(images/globalNavigation-home_bg.png) 11px 8px no-repeat; +} + +.globalNavigation-leaderBoard a { + background: url(images/globalNavigation-leaderBoard_bg.png) 11px 8px + no-repeat; +} + +.globalNavigation-leaderBoardGroup, .globalNavigation-leaderBoard, + .globalNavigation-home { + margin: -4px 10px 0 0; + padding: 0px 0px 4px; + float: left; + background: #7c7b7c url(images/alert-overlay.png) repeat-x; +} + +.globalNavigation-leaderBoard { + margin-right: 5px; +} + +.globalNavigation-leaderBoardGroup a, .globalNavigation-leaderBoard a, + .globalNavigation-home a { + color: #fff; + text-decoration: none; + display: block; + padding: 8px 13px 6px 36px; +} + +.sapLogo { + float: left; + margin-top: 13px; + margin-left: 12px; +} + +.sailingAnalyticsLabelPanel { + margin: 10px 0 0 0; +} + +.sailingAnalyticsLabel { + float: left; + color: #fff; + font-size: 14px; + font-weight: bold; + margin: 15px 0 0 0; +} + +.gwt-DialogBox { + line-height: 1.4; + background-color: #cecece; + border: 1px solid #3e3e3e; + border-radius: 4px; + box-shadow: 0px 0px 10px 0px #3e3e3e; +} + +.gwt-PopupPanelGlass { + background-color: rgba(140, 140, 140, 0.5); +} + +.gwt-DialogBox .Caption { + font-size: 14px; + padding: 8px 10px; + background-color: #ededed; + border-bottom: 1px solid #3e3e3e; + font-family: 'Open Sans', Verdana, Arial, sans-serif; + font-weight: 600; +} + +.gwt-DialogBox .dialogContent { + background: #ffffff; + padding: 10px; +} + +.gwt-Button, .gwt-Button:visited { + font-size: 0, 9rem; + display: inline-block; + color: #fff; + border: none; + padding: 5px 10px; + border-radius: 4px; + background-color: #efaa00; + -webkit-transition: background-color 0.2s; + -moz-transition: background-color 0.2s; + transition: background-color 0.2s; + text-decoration: none; + cursor: pointer; +} + +.gwt-Button:active { + border: none; +} + +.gwt-Button:hover { + background-color: #d79900; + outline: none; + text-decoration: none; +} + +.gwt-Button[disabled] { + cursor: not-allowed; + background: #cfcfcf; +} + +.gwt-Button[disabled]:hover { + cursor: default; + background: #cfcfcf; + cursor: not-allowed; +} + +.gwt-Button[disabled]:active { + cursor: default; + background: #cfcfcf; + cursor: not-allowed; +} + +.gwt-ButtonLarge +.gwt-ButtonMedium +.gwt-ButtonSmall + +.gwt-DialogBox .boxPanel { + padding: 0 0 10px 0; +} + +.gwt-DialogBox .boxPanel:after { + display: inline-block; + content: " "; + display: block; + height: 0; + clear: both; + visibility: hidden; +} + +.gwt-DialogBox .settingsDialogLabel { + float: left; + padding: 3px 6px 0 0; +} + +.gwt-DialogBox .settingsDialogValue { + float: right; +} + +.gwt-DialogBox .additionalWidgets { + padding: 5px 0 0 0; + text-align: right; +} + +.gwt-DialogBox .gwt-Label { + line-height: 1.4; +} + +.timingSettings .dialogInnerContent { + padding: 0 9px 0 9px; +} + +.timingSettings .gwt-Label { + float: left; + padding: 0 10px 0 0; +} + +.helpPanel { + margin-top: 15px; +} + +.minMaxBackgroundBar { + left: 0px; + background-image: url(images/yellowBar.png); + background-position: left; + background-repeat: no-repeat; +} + +.minMaxBackgroundBarBad { + left: 0px; + background-image: url(images/redBar.png); + background-position: left; + background-repeat: no-repeat; +} + +.minMaxBackgroundBarGood { + left: 0px; + background-image: url(images/greenBar.png); + background-position: left; + background-repeat: no-repeat; +} + +.inlineButton { + height: 20px; + padding: 0px 10px 3px; + float: left; + background: #de8a2e url(images/alert-overlay.png) repeat-x; + font-family: 'Open Sans', Arial, Verdana, sans-serif; +} + +.chartCloseButton { + cursor: pointer; + background: url(images/closeButton.png) right center no-repeat; + height: 18px; + width: 60px; + text-align: left; +} + +video { + max-width: 100%; + height: auto; +} + +.pointeredLink { +} + +.betterCheckboxCellSelected { + margin: 0px; + height: 18px; + width: 18px; + border: 1px; + background-color: #acacac; + vertical-align: middle; +} + +.betterCheckboxCellDeselected { + margin-left: 2px; + /* account for the selection border that pushes a selected checkbox to the side */ + margin-top: 0px; + margin-bottom: 0px; + margin-right: 0px; + border: 1px; + height: 18px; + width: 18px; + vertical-align: middle; +} + +.LeaderboardPanel-FilterControl-Panel { + width: 380px; + border-top: 1px solid #ececec; + margin-top: 3px; +} + +.LeaderboardPanel-FilterClear-Button { + margin-top: 2px; + font-size: 10px; + outline: 0; + height: 23px; + background-color: #fff; + border: 1px solid #cecece; + border-radius: 7px; +} + +.LeaderboardPanel-FilterClear-Button:hover { + background-color: #ececec; +} + +.LeaderboardPanel-FilterControl-StatusLabel { + padding: 8px 2px; + font-size: 10px; +} + +label { + padding-left: 4px; } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/DataMining.css b/java/com.sap.sailing.gwt.ui/DataMining.css index 06a7d5b3111..3706c2632b7 100644 --- a/java/com.sap.sailing.gwt.ui/DataMining.css +++ b/java/com.sap.sailing.gwt.ui/DataMining.css @@ -26,7 +26,7 @@ body { background: url(images/spectatorview-bg.png) no-repeat left top #ebebeb; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; line-height: 1; font-size: 14px; font-weight: 400; @@ -42,7 +42,7 @@ a { table td,select,button { line-height: 1; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-size: 14px; } @@ -161,7 +161,8 @@ input.magnifier { } .chart-importantMessage { - font-family: 'OpenSansSemiBold',Arial,Verdana,sans-serif; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; font-size: 20px; height: 70px; margin: 0 auto; @@ -172,7 +173,8 @@ input.magnifier { } .chart-busyMessage { - font-family: 'OpenSansSemiBold',Arial,Verdana,sans-serif; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; font-size: 20px; height: 70px; margin: 0 auto; @@ -193,7 +195,8 @@ input.magnifier { border-top-left-radius: 3px; color: #FFFFFF; cursor: pointer; - font-family: OpenSansSemiBold,Verdana,Arial,sans-serif; + font-family: 'Open Sans',Verdana,Arial,sans-serif; + font-weight: 600; font-size: 14px; margin: 0 0 0 0; padding: 2px 7px 2px 3px; diff --git a/java/com.sap.sailing.gwt.ui/Leaderboard.css b/java/com.sap.sailing.gwt.ui/Leaderboard.css index ef8a65d5c8f..8ab68acf9b9 100755 --- a/java/com.sap.sailing.gwt.ui/Leaderboard.css +++ b/java/com.sap.sailing.gwt.ui/Leaderboard.css @@ -24,7 +24,7 @@ body { background: #fff; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; line-height: 1; font-size: 14px; font-weight: 400; @@ -81,7 +81,7 @@ input.magnifier{ } body, table td, select, button { - font-family: 'OpenSansLight', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-size: 14px; } @@ -90,7 +90,8 @@ body, table td, select, button { } .abstractChartPanel-importantMessageOfChart { - font-family: 'OpenSansBold',Arial,Verdana,sans-serif; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: bold; font-size: 20px; height: 70px; margin: 0 auto; @@ -105,7 +106,7 @@ body, table td, select, button { margin: 0 auto; width: 400px; text-align: center; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-size: 18px; color: #fff; margin-top: 22px; @@ -113,7 +114,7 @@ body, table td, select, button { .leaderboardHeading { font-size: 16px; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-weight: bold; } @@ -122,7 +123,8 @@ body, table td, select, button { } .bold { - font-family: "OpenSansCondensedBold", sans-serif; + font-family: 'Open Sans Condensed', sans-serif; + font-weight: bold; } .trackingQuality-circle { diff --git a/java/com.sap.sailing.gwt.ui/LeaderboardEditing.html b/java/com.sap.sailing.gwt.ui/LeaderboardEditing.html index 27eccd7d7e3..ea7acd3c454 100755 --- a/java/com.sap.sailing.gwt.ui/LeaderboardEditing.html +++ b/java/com.sap.sailing.gwt.ui/LeaderboardEditing.html @@ -21,7 +21,7 @@ - + diff --git a/java/com.sap.sailing.gwt.ui/LeaderboardNewHome.css b/java/com.sap.sailing.gwt.ui/LeaderboardNewHome.css index 83ea7d43ca9..4aa2705763b 100644 --- a/java/com.sap.sailing.gwt.ui/LeaderboardNewHome.css +++ b/java/com.sap.sailing.gwt.ui/LeaderboardNewHome.css @@ -36,7 +36,7 @@ input.magnifier{ } body, table td, select, button { - font-family: 'OpenSansLight', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-size: 14px; } @@ -45,7 +45,8 @@ body, table td, select, button { } .abstractChartPanel-importantMessageOfChart { - font-family: 'OpenSansBold',Arial,Verdana,sans-serif; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: bold; font-size: 20px; height: 70px; margin: 0 auto; @@ -60,7 +61,7 @@ body, table td, select, button { margin: 0 auto; width: 400px; text-align: center; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-size: 18px; color: #fff; margin-top: 22px; @@ -68,7 +69,7 @@ body, table td, select, button { .leaderboardHeading { font-size: 16px; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-weight: bold; } @@ -77,7 +78,8 @@ body, table td, select, button { } .bold { - font-family: "OpenSansCondensedBold", sans-serif; + font-family: 'Open Sans Condensed', sans-serif; + font-weight: bold; } .trackingQuality-circle { diff --git a/java/com.sap.sailing.gwt.ui/PolarSheets.css b/java/com.sap.sailing.gwt.ui/PolarSheets.css index c419d2c8cd5..e94192e52cb 100644 --- a/java/com.sap.sailing.gwt.ui/PolarSheets.css +++ b/java/com.sap.sailing.gwt.ui/PolarSheets.css @@ -24,7 +24,7 @@ body { background: #fff; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; line-height: 1; font-size: 14px; font-weight: 400; @@ -85,7 +85,8 @@ input.magnifier{ } body, table td, select, button { - font-family: 'OpenSansLight', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 300; font-size: 14px; } @@ -94,7 +95,8 @@ body, table td, select, button { } .abstractChartPanel-importantMessageOfChart { - font-family: 'OpenSansSemiBold',Arial,Verdana,sans-serif; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; font-size: 20px; height: 70px; margin: 0 auto; @@ -109,7 +111,7 @@ body, table td, select, button { margin: 0 auto; width: 400px; text-align: center; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-size: 18px; color: #fff; margin-top: 22px; @@ -117,7 +119,7 @@ body, table td, select, button { .leaderboardHeading { font-size: 16px; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-weight: bold; } diff --git a/java/com.sap.sailing.gwt.ui/RaceBoard.css b/java/com.sap.sailing.gwt.ui/RaceBoard.css index 00bbc007faf..62ba67e0fa7 100644 --- a/java/com.sap.sailing.gwt.ui/RaceBoard.css +++ b/java/com.sap.sailing.gwt.ui/RaceBoard.css @@ -83,11 +83,21 @@ table { padding-left: 10px; } -.MapSimulationLegendParentDiv { +.TrueNorthIndicatorPanelParentDiv { padding-top: 10px; padding-left: 10px; } +.MapSimulationLegendParentDiv { + padding-bottom: 10px; + padding-right: 10px; + pointer-events: none; +} + +.MapSimulationLegend { + pointer-events: all; +} + .RaceMap-HeaderPanel { height: 60px; width: 100%; @@ -142,13 +152,15 @@ img.openColumn { .raceBoardNavigation { float: left; margin: 9px 0 0 12px; - font-family: 'OpenSansLight', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 300; } .raceBoardControls { float: right; margin: 9px 0 0 12px; - font-family: 'OpenSansLight', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 300; } .raceBoardNavigation-navigationitem { @@ -180,7 +192,8 @@ img.openColumn { } .abstractChartPanel-importantMessageOfChart { - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600; font-size: 20px; height: 70px; margin: 0 auto; @@ -239,7 +252,8 @@ img.openColumn { 0 transparent; color: #FFFFFF; cursor: pointer; - font-family: OpenSansSemiBold, Verdana, Arial, sans-serif; + font-family: 'Open Sans', Verdana, Arial, sans-serif; + font-weight: 600; font-size: 14px; margin: 0 0 0 0; padding: 2px 7px 2px 3px; @@ -272,7 +286,8 @@ img.openColumn { 0 transparent; color: #FFFFFF; cursor: pointer; - font-family: OpenSansSemiBold, Verdana, Arial, sans-serif; + font-family: 'Open Sans', Verdana, Arial, sans-serif; + font-weight: 600; font-size: 14px; margin: 0 0 0 0; padding: 2px 7px 2px 3px; @@ -584,6 +599,7 @@ img.openColumn { } .RegattaRaceInformation-Header { + font-family: 'Open Sans', Arial, Verdana, sans-serif; position: relative; left: 140px; top: 11px; diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch b/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch index 2f7207cca0f..d8a99614b75 100755 --- a/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT sdm.launch @@ -1,62 +1,78 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/SailingGWT.launch b/java/com.sap.sailing.gwt.ui/SailingGWT.launch index f08010aaad9..2bbffe904de 100755 --- a/java/com.sap.sailing.gwt.ui/SailingGWT.launch +++ b/java/com.sap.sailing.gwt.ui/SailingGWT.launch @@ -1,75 +1,75 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/SettingsDialog.css b/java/com.sap.sailing.gwt.ui/SettingsDialog.css index cfb8175cc32..babd52430d5 100644 --- a/java/com.sap.sailing.gwt.ui/SettingsDialog.css +++ b/java/com.sap.sailing.gwt.ui/SettingsDialog.css @@ -5,14 +5,16 @@ .dialogInnerHeadline { border-bottom: 1px solid #CCCCCC; - font-family: OpenSansSemiBold,Arial,Verdana,sans-serif; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; height: 21px; padding: 2px 0 0 2px; margin-bottom: 4px; } .dialogInnerHeadlineOther { - font-family: OpenSansSemiBold, Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600; color: #e84a1a; padding-left: 9px; } diff --git a/java/com.sap.sailing.gwt.ui/Simulator.css b/java/com.sap.sailing.gwt.ui/Simulator.css index e3563a2f47e..e7e173f5e83 100644 --- a/java/com.sap.sailing.gwt.ui/Simulator.css +++ b/java/com.sap.sailing.gwt.ui/Simulator.css @@ -32,7 +32,8 @@ .titleLabel { text-align: center; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 font-size: 18px; color: #fff; margin: 23px 0 0 0; @@ -40,7 +41,7 @@ body { background: #ebebeb; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; line-height: 1; font-size: 14px; font-weight: 400; @@ -50,7 +51,8 @@ body { } table td,select,button { - font-family: 'OpenSansLight', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 300; line-height: 1; font-size: 14px; } @@ -64,7 +66,8 @@ table td,select,button { /** Example rules used by the template application (remove for your app) */ h1 { font-size: 2em; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 color: #777777; margin: 40px 0px 70px; text-align: center; @@ -106,7 +109,8 @@ table { /*.leaderboardLabel { border-bottom: 2px solid #007dc0; padding: 0 0 6px 0; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 }*/ .refreshAndSettings { background: #f2f2f2; @@ -144,7 +148,8 @@ input.magnifier { /*.raceBoardNavigation { float: right; margin: 26px 0 0 0; - font-family: 'OpenSansLight', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 300; } .raceBoardNavigation-navigationitem { @@ -167,7 +172,8 @@ input.magnifier { text-decoration: none; }*/ .abstractChartPanel-importantMessageOfChart { - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 font-size: 20px; height: 70px; margin: 0 auto; @@ -186,14 +192,15 @@ input.magnifier { } .innerHeadline { - font-family: OpenSansSemiBold, Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-size: 16px; color: #7c7c7c; margin: 15px 0 } .sectorHeadline { - font-family: OpenSansLight, Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 300; font-size: 16px; } @@ -329,7 +336,8 @@ input.magnifier { #masterPanelLeft .gwt-SliderBar-shell .gwt-SliderBar-ticklabel { font-size: 12px; cursor: default; - font-family: 'OpenSansSemiBold',Arial,Verdana,sans-serif; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; top: 5px; } @@ -338,7 +346,8 @@ input.magnifier { font-size: 12px; color: #bc9434; cursor: default; - font-family: 'OpenSansSemiBold',Arial,Verdana,sans-serif; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; } #masterPanelLeft .gwt-SliderBar-shell-focused { diff --git a/java/com.sap.sailing.gwt.ui/Spectator.css b/java/com.sap.sailing.gwt.ui/Spectator.css index 1f2709e6023..dc81d3c43ff 100644 --- a/java/com.sap.sailing.gwt.ui/Spectator.css +++ b/java/com.sap.sailing.gwt.ui/Spectator.css @@ -26,7 +26,7 @@ body { background: url(images/spectatorview-bg.png) no-repeat left top #ebebeb; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; line-height: 1; font-size: 14px; font-weight: 400; @@ -42,7 +42,7 @@ a { table td,select,button { line-height: 1; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; font-size: 14px; } @@ -186,7 +186,8 @@ input.magnifier { float: left; margin: 5px 5px 5px 0; text-decoration: none; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 } .leaderboardGroupPanel-BoatClass { @@ -196,7 +197,8 @@ input.magnifier { margin: 5px 5px 5px 0; text-decoration: none; white-space: nowrap; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 } .leaderboardGroupPanel-ColorBox { @@ -227,7 +229,8 @@ input.magnifier { text-decoration: none; color: #fff; /*border: 1px solid #a6a6a6;*/ - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 } .leaderboardGroupPanel-LiveRace, .leaderboardGroupPanel-LegendLive { @@ -244,7 +247,8 @@ input.magnifier { text-decoration: none; color: #fff; /*border: 1px solid #a6a6a6;*/ - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 } .leaderboardGroupPanel-ActiveLeaderboard { @@ -260,7 +264,8 @@ input.magnifier { background: url("images/btn-cta-orangeleft.png") repeat-x -10px 0 #e7e7e7; text-decoration: none; color: #fff; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 } .leaderboardGroupPanel-ActiveButton { @@ -274,7 +279,8 @@ input.magnifier { background: url("images/btn-cta-blueleft.png") repeat-x 0px 0 #e7e7e7; text-decoration: none; color: #fff; - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600 display: inline-block; } @@ -352,7 +358,7 @@ input.magnifier { .leaderboardGroupPanel-InactiveRace { background: #c0bebe; - font-family: 'OpenSansRegular', Arial, Verdana, sans-serif + font-family: 'Open Sans', Arial, Verdana, sans-serif } .errorLabel { diff --git a/java/com.sap.sailing.gwt.ui/images/home/autoplay.svg b/java/com.sap.sailing.gwt.ui/images/home/autoplay.svg new file mode 100644 index 00000000000..2ef3036f68d --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/images/home/autoplay.svg @@ -0,0 +1,13 @@ + + + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/images/home/close.svg b/java/com.sap.sailing.gwt.ui/images/home/close.svg new file mode 100644 index 00000000000..0c13dd0797e --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/images/home/close.svg @@ -0,0 +1,9 @@ + + + + + + diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/SharedResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/SharedResources.java index 086fa1a8429..56056fa0c74 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/SharedResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/SharedResources.java @@ -20,6 +20,7 @@ public interface SharedResources extends ClientBundle { String navbar_buttonhidden(); String navbar_buttonactive(); String button(); + String buttontextonly(); String buttonsmall(); String buttonstrong(); String buttoninactive(); @@ -41,6 +42,23 @@ public interface SharedResources extends ClientBundle { String lightbox_overlay(); String lightbox_content(); String lightbox_close(); + String spacer(); + String spacersmall(); + String spacermediumsmall(); + String spacermedium(); + String spacermediumbig(); + String spacerbig(); + String spacermargin(); + String spacermargintopsmall(); + String spacermargintopmediumsmall(); + String spacermargintopmedium(); + String spacermargintopmediumbig(); + String spacermargintopbig(); + String spacermarginbottomsmall(); + String spacermarginbottommediumsmall(); + String spacermarginbottommedium(); + String spacermarginbottommediumbig(); + String spacermarginbottombig(); String stickyheader(); String slideto(); String slidetonav(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/overview/EventOverviewStage.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/overview/EventOverviewStage.ui.xml index 1e1cfa4ae8c..20710c7bc91 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/overview/EventOverviewStage.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/overview/EventOverviewStage.ui.xml @@ -4,6 +4,7 @@ +
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/Regatta.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/Regatta.java index ed1b6eb3225..8fc80fe07df 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/Regatta.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/Regatta.java @@ -4,6 +4,7 @@ import java.util.ArrayList; import java.util.List; import com.google.gwt.core.client.GWT; +import com.google.gwt.dom.client.AnchorElement; import com.google.gwt.dom.client.DivElement; import com.google.gwt.dom.client.HeadingElement; import com.google.gwt.dom.client.SpanElement; @@ -13,6 +14,7 @@ import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.Event; +import com.google.gwt.user.client.EventListener; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.Image; @@ -68,6 +70,8 @@ public class Regatta extends Composite { @UiField Anchor regattaDetailsLink; @UiField Image boatClassImage; + @UiField + AnchorElement boatClassLinkUi; private Presenter presenter; @@ -83,6 +87,7 @@ public class Regatta extends Composite { RegattaResources.INSTANCE.css().ensureInjected(); initWidget(uiBinder.createAndBindUi(this)); + } public void setData(LeaderboardGroupDTO leaderboardGroup, boolean hasMultipleLeaderboardGroups, StrippedLeaderboardDTO leaderboard, @@ -95,6 +100,17 @@ public class Regatta extends Composite { } else { regattaNavigation = presenter.getRegattaNavigation(leaderboard.regattaName != null ? leaderboard.regattaName : leaderboard.name); regattaDetailsLink.setHref(regattaNavigation.getTargetUrl()); + boatClassLinkUi.setHref(regattaNavigation.getTargetUrl()); + Event.sinkEvents(boatClassLinkUi, Event.ONCLICK); + Event.setEventListener(boatClassLinkUi, new EventListener() { + @Override + public void onBrowserEvent(Event event) { + if (LinkUtil.handleLinkClick(event)) { + event.preventDefault(); + regattaNavigation.goToPlace(); + } + } + }); } boolean hasLiveRace = leaderboard.hasLiveRace(presenter.getTimerForClientServerOffset().getLiveTimePointInMillis()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/Regatta.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/Regatta.ui.xml index b3de40450a5..caa49a4963d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/Regatta.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/Regatta.ui.xml @@ -11,9 +11,9 @@
-
+ -
+
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/message/Message.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/message/Message.gss index c43f38e1c89..3da23839c0e 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/message/Message.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/message/Message.gss @@ -1,11 +1,11 @@ .message { font-size: 1rem; - margin-top: 2em; line-height: 1.733333333333333em; background: #f2f2f2; border: 1px solid #dedede; border-radius: 0.333333333333333em; padding: 1.066666666666667em 2.533333333333333em 1.066666666666667em 2.733333333333333em; + margin-bottom: 1.333333333333333em; } .message img { display: inline-block; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/AbstractRaceList.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/AbstractRaceList.java index f4abeac01e9..98c4ff5ee97 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/AbstractRaceList.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/racelist/AbstractRaceList.java @@ -322,6 +322,7 @@ public abstract class AbstractRaceList extends Compos public void render(Context context, SimpleWindDTO value, SafeHtmlBuilder sb) { if (value != null) { SafeStylesBuilder safeStyles = new SafeStylesBuilder(); + safeStyles.trustedNameAndValue("-webkit-transform", "rotate(" + value.getTrueWindFromDeg() + "deg)"); safeStyles.trustedNameAndValue("transform", "rotate(" + value.getTrueWindFromDeg() + "deg)"); safeStyles.width(2.75, Unit.EM).height(2.75, Unit.EM); sb.append(TEMPLATE.windDirection(safeStyles.toSafeStyles())); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/video/Video.gss b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/video/Video.gss index a78f9e1f373..d889bf2bbb7 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/video/Video.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/partials/video/Video.gss @@ -36,7 +36,6 @@ font-size: 1rem; height: 28.333333333333332em; position: relative; - margin-top: 1.333333333333333em; } @media (max-width: 976px) { .video { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaOverviewTabView.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaOverviewTabView.java index acfdb82eed4..9422074fdc5 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaOverviewTabView.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/place/event/regatta/tabs/RegattaOverviewTabView.java @@ -15,7 +15,6 @@ import com.sap.sailing.gwt.home.client.place.event.regatta.RegattaTabView; import com.sap.sailing.gwt.home.client.place.event.regatta.tabs.reload.RefreshManager; import com.sap.sailing.gwt.ui.shared.dispatch.event.GetEventOverviewStageAction; import com.sap.sailing.gwt.ui.shared.dispatch.event.GetLiveRacesForRegattaAction; -import com.sap.sailing.gwt.ui.shared.eventview.EventViewDTO.EventType; /** * Created by pgtaboada on 25.11.14. @@ -32,8 +31,12 @@ public class RegattaOverviewTabView extends Composite implements RegattaTabView< @Override public TabView.State getState() { - return currentPresenter.getCtx().getEventDTO().getType() == EventType.MULTI_REGATTA ? TabView.State.NOT_AVAILABLE_SHOW_NEXT_AVAILABLE - : TabView.State.VISIBLE; + // Made not available until we have something to show + // TODO: implement backend and required UI + // return currentPresenter.getCtx().getEventDTO().getType() == EventType.MULTI_REGATTA ? + // TabView.State.NOT_AVAILABLE_SHOW_NEXT_AVAILABLE + // : TabView.State.VISIBLE; + return TabView.State.NOT_AVAILABLE_SHOW_NEXT_AVAILABLE; } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/dispatch/SimpleDispatch.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/dispatch/SimpleDispatch.java index 75062a7d3fb..401caa60100 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/dispatch/SimpleDispatch.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/dispatch/SimpleDispatch.java @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.home.client.shared.dispatch; import java.util.Date; import com.google.gwt.core.client.GWT; +import com.google.gwt.i18n.client.LocaleInfo; import com.google.gwt.user.client.rpc.AsyncCallback; import com.google.gwt.user.client.rpc.ServiceDefTarget; import com.sap.sailing.gwt.ui.client.RemoteServiceMappingConstants; @@ -32,7 +33,7 @@ public class SimpleDispatch implements DispatchAsync { @Override public > void execute(A action, final AsyncCallback callback) { - dispatchRPC.execute(new RequestWrapper(action), new AsyncCallback>() { + dispatchRPC.execute(new RequestWrapper(action, LocaleInfo.getCurrentLocale().getLocaleName()), new AsyncCallback>() { @Override public void onFailure(Throwable caught) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/footer/Footer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/footer/Footer.java index 8e5f8ea33e1..be8dadc061a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/footer/Footer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/footer/Footer.java @@ -1,14 +1,17 @@ package com.sap.sailing.gwt.home.client.shared.footer; -import java.util.HashMap; -import java.util.Map; -import java.util.Map.Entry; +import java.io.IOException; +import java.util.Arrays; +import java.util.HashSet; +import java.util.Set; import com.google.gwt.core.client.GWT; import com.google.gwt.dom.client.DivElement; import com.google.gwt.event.dom.client.ClickEvent; +import com.google.gwt.event.logical.shared.ValueChangeEvent; import com.google.gwt.http.client.UrlBuilder; import com.google.gwt.i18n.client.LocaleInfo; +import com.google.gwt.text.shared.Renderer; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.uibinder.client.UiHandler; @@ -16,11 +19,13 @@ import com.google.gwt.user.client.Window; import com.google.gwt.user.client.Window.Location; import com.google.gwt.user.client.ui.Anchor; import com.google.gwt.user.client.ui.Composite; +import com.google.gwt.user.client.ui.ValueListBox; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.home.client.app.HomePlacesNavigator; import com.sap.sailing.gwt.home.client.app.PlaceNavigation; import com.sap.sailing.gwt.home.client.place.whatsnew.WhatsNewPlace; import com.sap.sailing.gwt.home.client.place.whatsnew.WhatsNewPlace.WhatsNewNavigationTabs; +import com.sap.sse.common.Util.Pair; public class Footer extends Composite { private static FooterPanelUiBinder uiBinder = GWT.create(FooterPanelUiBinder.class); @@ -30,17 +35,32 @@ public class Footer extends Composite { @UiField Anchor changeLanguageLink; @UiField DivElement languageSelectionDiv; + @UiField(provided = true) ValueListBox> changeLanguageList = new ValueListBox>(new Renderer>() { + @Override + public String render(Pair object) { + if(object == null) { + return ""; + } + if(object.getB() != null) { + return object.getB(); + } + return object.getA(); + } + + @Override + public void render(Pair object, Appendable appendable) throws IOException { + appendable.append(render(object)); + } + }); + + private String otherLanguage; + private final LocaleInfo currentLocale = LocaleInfo.getCurrentLocale(); - private final Map localeAndLanguages; @UiField(provided = true) final PlaceNavigation releaseNotesNavigation; public Footer(final HomePlacesNavigator navigator) { - localeAndLanguages = new HashMap(); - localeAndLanguages.put("en", "English"); - localeAndLanguages.put("de", "Deutsch"); - FooterResources.INSTANCE.css().ensureInjected(); releaseNotesNavigation = navigator.getWhatsNewNavigation(WhatsNewNavigationTabs.SailingAnalytics); @@ -51,26 +71,55 @@ public class Footer extends Composite { @UiHandler("changeLanguageLink") public void changeLanguage(ClickEvent e) { - String selectedLanguage = changeLanguageLink.getText(); - String selectedLocale = null; - for (Entry entry: localeAndLanguages.entrySet()) { - if (entry.getValue().equals(selectedLanguage)) { - selectedLocale = entry.getKey(); - } + if(otherLanguage != null) { + UrlBuilder builder = Location.createUrlBuilder().setParameter("locale", otherLanguage); + Window.Location.replace(builder.buildString()); } - if(selectedLocale != null) { - UrlBuilder builder = Location.createUrlBuilder().setParameter("locale", selectedLocale); - Window.Location.replace(builder.buildString()); + } + + @UiHandler("changeLanguageList") + void onListSelection(ValueChangeEvent> event) { + String selectedLocaleName = event.getValue().getA(); + if(!currentLocale.getLocaleName().equals(selectedLocaleName)) { + UrlBuilder builder = Location.createUrlBuilder().setParameter("locale", selectedLocaleName); + Window.Location.replace(builder.buildString()); } } private void updateUI() { - LocaleInfo currentLocale = LocaleInfo.getCurrentLocale(); - HashMap languagesToSelectFrom = new HashMap(localeAndLanguages); - languagesToSelectFrom.remove(currentLocale.getLocaleName()); - - if(languagesToSelectFrom.size() == 1) { - changeLanguageLink.setText(languagesToSelectFrom.values().iterator().next()); + Set availableLocales = new HashSet(Arrays.asList(LocaleInfo.getAvailableLocaleNames())); + availableLocales.remove("default"); + + if(availableLocales.size() <= 1) { + // only current language (or removed default) available + changeLanguageLink.removeFromParent(); + changeLanguageList.removeFromParent(); + languageSelectionDiv.removeFromParent(); + return; } + if(availableLocales.size() == 2) { + changeLanguageList.removeFromParent(); + // current language + one we can switch to + for(String localeName : availableLocales) { + if(!currentLocale.getLocaleName().equals(localeName)) { + otherLanguage = localeName; + changeLanguageLink.setText(LocaleInfo.getLocaleNativeDisplayName(otherLanguage)); + return; + } + } + return; + } + changeLanguageLink.removeFromParent(); + Set> values = new HashSet<>(); + Pair selectedValue = null; + for(String localeName : availableLocales) { + Pair value = new Pair(localeName, LocaleInfo.getLocaleNativeDisplayName(localeName)); + values.add(value); + if(currentLocale.getLocaleName().equals(localeName)) { + selectedValue = value; + } + } + changeLanguageList.setValue(selectedValue); + changeLanguageList.setAcceptableValues(values); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/footer/Footer.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/footer/Footer.ui.xml index bfae282ac73..93ad10b02e6 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/footer/Footer.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/footer/Footer.ui.xml @@ -5,6 +5,33 @@ + + .languageSelect::-ms-expand { + display: none; + } + .languageSelect { + -webkit-appearance: none; + -moz-appearance: none; + -ms-appearance: none; + appearance: none; + + margin-top: 25px; + color: #efaa00; + line-height: 1.5em; + height: 1.5em; + display: inline-block; + border: transparent; + cursor: pointer; + padding-right: 1.2em; + background: #333; + cursor: pointer; + + background-image:url("data:image/svg+xml;base64,PHN2ZyB4bWxucz0naHR0cDovL3d3dy53My5vcmcvMjAwMC9zdmcnIHZlcnNpb249JzEuMScgaGVpZ2h0PScxOHB4JyB3aWR0aD0nMTZweCc+PHRleHQgeD0nMCcgeT0nMTUnIGZpbGw9JyNlZmFhMDAnIGZvbnQtc2l6ZT0nMTYnPiYjOTY2MDs8L3RleHQ+PC9zdmc+"); + background-position: right center; + background-repeat: no-repeat; + + } +
@@ -33,6 +60,7 @@
: +
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java index 3d8160c4871..90aa89c8b12 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMedia.java @@ -12,8 +12,9 @@ import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.gwt.home.client.app.PlaceNavigator; -import com.sap.sailing.gwt.ui.shared.media.ImageMetadataDTO; -import com.sap.sailing.gwt.ui.shared.media.VideoMetadataDTO; +import com.sap.sailing.gwt.home.client.shared.media.SailingFullscreenViewer; +import com.sap.sailing.gwt.ui.shared.media.SailingImageDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingVideoDTO; import com.sap.sse.gwt.client.controls.carousel.ImageCarousel; public class MainMedia extends Composite { @@ -27,7 +28,7 @@ public class MainMedia extends Composite { DivElement videoLightBoxData; @UiField - ImageCarousel imageCarousel; + ImageCarousel imageCarousel; interface MainMediaUiBinder extends UiBinder { } @@ -37,20 +38,21 @@ public class MainMedia extends Composite { public MainMedia(PlaceNavigator navigator) { MainMediaResources.INSTANCE.css().ensureInjected(); initWidget(uiBinder.createAndBindUi(this)); + imageCarousel.registerFullscreenViewer(new SailingFullscreenViewer()); } - public void setData(Collection videos, ArrayList photos) { - Iterator videoIterator = videos.iterator(); + public void setData(Collection videos, ArrayList photos) { + Iterator videoIterator = videos.iterator(); int videoCount = 0; while(videoCount < MAX_VIDEO_COUNT && videoIterator.hasNext()) { - VideoMetadataDTO videoDTO = videoIterator.next(); + SailingVideoDTO videoDTO = videoIterator.next(); MainMediaVideo video = new MainMediaVideo(videoDTO.getTitle(), videoDTO.getSourceRef(), videoDTO.getMimeType()); videosPanel.add(video); videoCount++; } - for (ImageMetadataDTO image : photos) { - imageCarousel.addImage(image.getSourceRef(), image.getHeightInPx(), image.getWidthInPx()); + for (SailingImageDTO image : photos) { + imageCarousel.addImage(image); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMediaVideo.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMediaVideo.java index dd96543e435..8629f6c77d1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMediaVideo.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/mainmedia/MainMediaVideo.java @@ -9,10 +9,10 @@ import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Composite; import com.google.gwt.user.client.ui.HTMLPanel; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.gwt.home.client.shared.LongNamesUtil; import com.sap.sailing.gwt.ui.client.media.VideoJSPlayer; -import com.sap.sailing.gwt.ui.shared.media.VideoMetadataDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingVideoDTO; +import com.sap.sse.common.media.MimeType; public class MainMediaVideo extends Composite { @@ -64,7 +64,7 @@ public class MainMediaVideo extends Composite { } - public void show(VideoMetadataDTO videoCandidateInfo) { + public void show(SailingVideoDTO videoCandidateInfo) { vJs.setSource(videoCandidateInfo.getSourceRef(), videoCandidateInfo.getMimeType()); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/MediaPage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/MediaPage.java index 38d6eaafc81..cd06d42ab51 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/MediaPage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/MediaPage.java @@ -17,9 +17,9 @@ import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.client.media.GalleryImageHolder; import com.sap.sailing.gwt.ui.client.media.VideoJSPlayer; import com.sap.sailing.gwt.ui.client.media.VideoThumbnail; -import com.sap.sailing.gwt.ui.shared.media.ImageMetadataDTO; import com.sap.sailing.gwt.ui.shared.media.MediaDTO; -import com.sap.sailing.gwt.ui.shared.media.VideoMetadataDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingImageDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingVideoDTO; public class MediaPage extends Composite { private static MediaPageUiBinder uiBinder = GWT.create(MediaPageUiBinder.class); @@ -85,7 +85,7 @@ public class MediaPage extends Composite { break; } - for (final ImageMetadataDTO holder : media.getPhotos()) { + for (final SailingImageDTO holder : media.getPhotos()) { if (holder.getSourceRef() != null) { GalleryImageHolder gih = new GalleryImageHolder(holder); @@ -96,7 +96,7 @@ public class MediaPage extends Composite { gih.addClickHandler(new ClickHandler() { @Override public void onClick(ClickEvent event) { - new FullscreenViewer(holder, media.getPhotos()); + new SailingFullscreenViewer().show(holder, media.getPhotos()); } }); } @@ -113,7 +113,7 @@ public class MediaPage extends Composite { videoListOuterBoxUi.addClassName(res.mediaCss().large3()); } boolean first = true; - for (final VideoMetadataDTO videoCandidateInfo : media.getVideos()) { + for (final SailingVideoDTO videoCandidateInfo : media.getVideos()) { if (first) { putVideoOnDisplay(videoCandidateInfo, false); first = false; @@ -137,7 +137,7 @@ public class MediaPage extends Composite { } } - private void putVideoOnDisplay(final VideoMetadataDTO videoCandidateInfo, boolean autoplay) { + private void putVideoOnDisplay(final SailingVideoDTO videoCandidateInfo, boolean autoplay) { videoDisplayUi = new VideoJSPlayer(true, autoplay); videoDisplayUi.setSource(videoCandidateInfo.getSourceRef(), videoCandidateInfo.getMimeType()); videoDisplayHolderUi.setWidget(videoDisplayUi); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/FullscreenViewer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/SailingFullscreenViewer.java similarity index 85% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/FullscreenViewer.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/SailingFullscreenViewer.java index 5e40efd3637..37ab3706266 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/FullscreenViewer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/SailingFullscreenViewer.java @@ -15,9 +15,10 @@ import com.google.gwt.user.client.Window; import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.PopupPanel.PositionCallback; import com.google.gwt.user.client.ui.RootPanel; -import com.sap.sailing.gwt.ui.shared.media.ImageMetadataDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingImageDTO; +import com.sap.sse.gwt.client.controls.carousel.ImageCarousel.FullscreenViewer; -public class FullscreenViewer { +public class SailingFullscreenViewer implements FullscreenViewer { private PopupPanel popup = new PopupPanel(true) { protected void onPreviewNativeEvent(NativePreviewEvent event) { if (event.getTypeInt() == Event.ONKEYDOWN && event.getNativeEvent().getKeyCode() == KeyCodes.KEY_ESCAPE) { @@ -26,8 +27,8 @@ public class FullscreenViewer { }; }; - public FullscreenViewer(ImageMetadataDTO selected, List images) { - final GalleryPlayer viewer = new GalleryPlayer(selected, images); + public void show(SailingImageDTO selected, List images) { + final SailingGalleryPlayer viewer = new SailingGalleryPlayer(selected, images); Window.addResizeHandler(new ResizeHandler() { @Override @@ -63,4 +64,5 @@ public class FullscreenViewer { } }); } + } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/GalleryPlayer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/SailingGalleryPlayer.java similarity index 85% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/GalleryPlayer.java rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/SailingGalleryPlayer.java index ff1122da1b8..0cd1fb97cb2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/GalleryPlayer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/SailingGalleryPlayer.java @@ -15,14 +15,14 @@ import com.google.gwt.uibinder.client.UiHandler; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.ResizeComposite; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.gwt.ui.shared.media.ImageMetadataDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingImageDTO; import com.sap.sse.gwt.client.controls.carousel.ImageCarousel; -public class GalleryPlayer extends ResizeComposite { - private static GalleryPlayerUiBinder uiBinder = GWT.create(GalleryPlayerUiBinder.class); +public class SailingGalleryPlayer extends ResizeComposite { + private static MyBinder uiBinder = GWT.create(MyBinder.class); private Command closeCommand; - interface GalleryPlayerUiBinder extends UiBinder { + interface MyBinder extends UiBinder { } @UiField @@ -30,19 +30,19 @@ public class GalleryPlayer extends ResizeComposite { @UiField DivElement subSliderUi; - ImageCarousel carousel = new ImageCarousel(); + ImageCarousel carousel = new ImageCarousel<>(); private int selectedIdx; - public GalleryPlayer(ImageMetadataDTO selected, List images) { + public SailingGalleryPlayer(SailingImageDTO selected, List images) { initWidget(uiBinder.createAndBindUi(this)); selectedIdx = Math.max(selectedIdx, images.indexOf(selected)); - for (ImageMetadataDTO i : images) { + for (SailingImageDTO i : images) { mainSliderUi.appendChild(createMainImgElement(i)); subSliderUi.appendChild(createThumbImgElement(i)); } } - private ImageElement createThumbImgElement(ImageMetadataDTO i ) { + private ImageElement createThumbImgElement(SailingImageDTO i ) { ImageElement img = Document.get().createImageElement(); img.setAttribute("src", i.getSourceRef()); img.setHeight(100); @@ -50,7 +50,7 @@ public class GalleryPlayer extends ResizeComposite { return img; } - private DivElement createMainImgElement(ImageMetadataDTO i) { + private DivElement createMainImgElement(SailingImageDTO i) { DivElement img = Document.get().createDivElement(); img.getStyle().setBackgroundImage("url(\"" + i.getSourceRef() + "\")"); img.getStyle().setProperty("backgroundSize", "contain"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/GalleryPlayer.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/SailingGalleryPlayer.ui.xml similarity index 88% rename from java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/GalleryPlayer.ui.xml rename to java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/SailingGalleryPlayer.ui.xml index 53f8f6d4e34..a56c4917dff 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/GalleryPlayer.ui.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/media/SailingGalleryPlayer.ui.xml @@ -31,26 +31,26 @@ } .toolbar { text-align: right; - padding: 10px; + padding: 10px 20px 10px 10px; } .content { width: 100%; height: 100%; - + } + .close { + height: 30px; + width: 30px; } .subcontent { border-top: 1px solid #ebebeb; - - - } - CLOSE + @@ -63,7 +63,7 @@
- +
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaser.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaser.java index 00207af8640..9c5057fd270 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaser.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/client/shared/stage/StageTeaser.java @@ -92,7 +92,7 @@ public abstract class StageTeaser extends Composite implements LazyLoadable { return; } Element element = eventTarget.cast(); - if (stageTeaserBandsPanel.getElement().isOrHasChild(element)) { + if (bandCount.isOrHasChild(element)) { return; } handleUserAction(); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java index bdcd7efa2a3..9760e171076 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesComponent.java @@ -389,6 +389,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component return result; } + private void createColumns() { regattaOverviewListHandler = new ListHandler(regattaOverviewDataProvider.getList()); courseAreaColumn = new TextColumn() { @@ -568,6 +569,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component tooltip); } }; + flagColumn.setCellStyleNames(tableRes.cellTableStyle().flagColumn()); boatClass = new TextColumn() { @Override public String getValue(RegattaOverviewEntryDTO entryDTO) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesTable.css b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesTable.css index 7a8e30b3821..77c87e9b86a 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesTable.css +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesTable.css @@ -1,90 +1,99 @@ -.cellTableWidget { - border-spacing: 0px; - font-size:16px; -} -.cellTableWidget a { - - text-decoration: underline; - cursor: pointer; -} - - -/** v-align images in the table header - * grey background for table header - */ -.cellTableHeader { - position: relative; - vertical-align: bottom; - background-color: #f2f2f2; - background-color: #fff; - padding: 20px 5px; - text-align: left; - border-bottom: 1px solid black; - text-shadow: none; - font-size: 16px; -} - -.cellTableHeader:FOCUS { - outline: none; -} - -.cellTableFirstColumnHeader { - -} - -.cellTableLastColumnHeader { - -} - -.cellTableHeader>span>img:first-child { - position: relative; - vertical-align: middle; -} - -.cellTableEvenRow { - background-color: #fafafa; -} - -.cellTableEvenRowCell { - border: none; - border-bottom: 1px solid #dedede; - padding: 20px 5px 20px 10px; - vertical-align: top; - -} - -.cellTableOddRow { - background-color: #fdfdfd; -} - -.cellTableOddRowCell { - border: none; - border-bottom: 1px solid #dedede; - padding: 20px 5px 20px 10px; - vertical-align: top; - -} - -.cellTableHoveredRow { - background-color: rgba(229, 242, 249, 0.5); - border-bottom: 1px solid #e5e5e5; -} - -.cellTableSelectedRow { - color: #000000; - background-color: rgba(229, 242, 249, 0.5); -} - -.cellTableSelectedRowCell { - border-top: 2px solid #21a0e4 !important; - border-bottom: 2px solid #21a0e4 !important; -} - -.cellTableRaceNameColumnHeader { - background-color: #e0e0e0; - padding: 5px 20px; -} - -.cellTableRaceNameColumn { - background-color: #e0e0e0 !important; +.cellTableWidget { + border-spacing: 0px; + font-size:16px; +} +.cellTableWidget a { + + text-decoration: underline; + cursor: pointer; +} + + +/** v-align images in the table header + * grey background for table header + */ +.cellTableHeader { + position: relative; + vertical-align: bottom; + background-color: #f2f2f2; + background-color: #fff; + padding: 15px 5px; + text-align: left; + border-bottom: 1px solid black; + text-shadow: none; + font-size: 16px; +} + +.cellTableHeader:FOCUS { + outline: none; +} + +.cellTableFirstColumnHeader { + +} + +.cellTableLastColumnHeader { + +} + +.cellTableHeader>span>img:first-child { + position: relative; + vertical-align: middle; +} + +.cellTableEvenRow { + background-color: #fafafa; +} + +.cellTableEvenRowCell { + border: none; + border-bottom: 1px solid #dedede; + padding: 15px 5px 15px 10px; + vertical-align: top; + +} + +.cellTableOddRow { + background-color: #fdfdfd; +} + +.cellTableOddRowCell { + border: none; + border-bottom: 1px solid #dedede; + padding: 15px 5px 15px 10px; + vertical-align: top; + +} + +.cellTableHoveredRow { + background-color: rgba(229, 242, 249, 0.5); + border-bottom: 1px solid #e5e5e5; +} + +.cellTableSelectedRow { + color: #000000; + background-color: rgba(229, 242, 249, 0.5); +} + +.cellTableSelectedRowCell { + border-top: 2px solid #21a0e4 !important; + border-bottom: 2px solid #21a0e4 !important; +} + +.cellTableRaceNameColumnHeader { + background-color: #e0e0e0; + padding: 5px 20px; +} + +.cellTableRaceNameColumn { + background-color: #e0e0e0 !important; +} + +.flagColumn { + padding-top: 0; + padding-bottom: 0; + vertical-align: middle; +} +.flagColumn>div { + display: inline-block; } \ No newline at end of file diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesTableResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesTableResources.java index c702221bec3..0b01dfce1c1 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesTableResources.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/RegattaRaceStatesTableResources.java @@ -1,22 +1,24 @@ -package com.sap.sailing.gwt.regattaoverview.client; - -import com.google.gwt.user.cellview.client.CellTable; - -public interface RegattaRaceStatesTableResources extends CellTable.Resources { - interface AdminConsoleTableStyle extends CellTable.Style { - /** - * Applied to header cells of race columns - */ - String cellTableRaceNameColumnHeader(); - - /** - * Applied to the totals columns - */ - String cellTableRaceNameColumn(); - - } - - @Override - @Source({ CellTable.Style.DEFAULT_CSS, "RegattaRaceStatesTable.css" }) - AdminConsoleTableStyle cellTableStyle(); -} +package com.sap.sailing.gwt.regattaoverview.client; + +import com.google.gwt.user.cellview.client.CellTable; + +public interface RegattaRaceStatesTableResources extends CellTable.Resources { + interface AdminConsoleTableStyle extends CellTable.Style { + /** + * Applied to header cells of race columns + */ + String cellTableRaceNameColumnHeader(); + + /** + * Applied to the totals columns + */ + String cellTableRaceNameColumn(); + + String flagColumn(); + + } + + @Override + @Source({ CellTable.Style.DEFAULT_CSS, "RegattaRaceStatesTable.css" }) + AdminConsoleTableStyle cellTableStyle(); +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/SailingFlagsBuilder.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/SailingFlagsBuilder.java old mode 100755 new mode 100644 index 202519f64b4..97eff4cb9d5 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/SailingFlagsBuilder.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/regattaoverview/client/SailingFlagsBuilder.java @@ -20,13 +20,6 @@ public class SailingFlagsBuilder { private final static FlagImageResolver flagImageResolver = new FlagImageResolver(); interface SailingFlagsTemplates extends SafeHtmlTemplates { - /** - * @param title - * the tool-tip to display for the image on mouse-over - * @param upperFlag - * how to render the image; this needs to be an <img> tag, not enclosed by any other element, - * as returned by {@link ImagesBarCell#makeImagePrototype(ImageResource)} - */ @SafeHtmlTemplates.Template("
{1}
") SafeHtml cell(String title, SafeHtml content); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceImpl.java index e316f94a1da..2b471316693 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceImpl.java @@ -25,9 +25,6 @@ import com.sap.sailing.domain.base.Event; import com.sap.sailing.domain.base.EventBase; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.RemoteSailingServerReference; -import com.sap.sailing.domain.common.ImageSize; -import com.sap.sailing.domain.common.media.MediaType; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; @@ -45,10 +42,10 @@ import com.sap.sailing.gwt.ui.shared.fakeseries.EventSeriesViewDTO.EventSeriesSt import com.sap.sailing.gwt.ui.shared.general.EventMetadataDTO; import com.sap.sailing.gwt.ui.shared.general.EventReferenceDTO; import com.sap.sailing.gwt.ui.shared.general.EventState; -import com.sap.sailing.gwt.ui.shared.media.ImageMetadataDTO; import com.sap.sailing.gwt.ui.shared.media.MediaDTO; import com.sap.sailing.gwt.ui.shared.media.MediaUtils; -import com.sap.sailing.gwt.ui.shared.media.VideoMetadataDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingImageDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingVideoDTO; import com.sap.sailing.gwt.ui.shared.start.EventStageDTO; import com.sap.sailing.gwt.ui.shared.start.StageEventType; import com.sap.sailing.gwt.ui.shared.start.StartViewDTO; @@ -60,6 +57,9 @@ import com.sap.sse.common.Util; import com.sap.sse.common.Util.Pair; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.TimeRangeImpl; +import com.sap.sse.common.media.ImageSize; +import com.sap.sse.common.media.MediaType; +import com.sap.sse.common.media.MimeType; import com.sap.sse.util.ServiceTrackerFactory; /** @@ -189,8 +189,10 @@ public class HomeServiceImpl extends ProxiedRemoteServiceServlet implements Home MimeType type = MediaUtils.detectMimeTypeFromUrl(youTubeRandomUrl.toString()); if (type.mediaType == MediaType.video) { - VideoMetadataDTO candidate = new VideoMetadataDTO(eventRef, youTubeRandomUrl, type, - holder.event.getName()); + SailingVideoDTO candidate = new SailingVideoDTO(eventRef, youTubeRandomUrl.toString(), type, // + holder.event.getEndDate().asDate() // FIXME: using event enddate for now + ); + candidate.setTitle(holder.event.getName()); result.addVideo(candidate); } } @@ -203,9 +205,9 @@ public class HomeServiceImpl extends ProxiedRemoteServiceServlet implements Home } }); - final Set photoGalleryUrls = new HashSet<>(); // using a HashSet here leads to a reasonable + final Set photoGalleryUrls = new HashSet<>(); // using a HashSet here leads to a reasonable // amount of shuffling - final List videoCandidates = new ArrayList<>(); + final List videoCandidates = new ArrayList<>(); for(EventHolder holder : recentEventsOfLast12Month) { if(result.getRecentEvents().size() < MAX_RECENT_EVENTS) { @@ -220,7 +222,7 @@ public class HomeServiceImpl extends ProxiedRemoteServiceServlet implements Home ImageSize imageSize = event.getImageSize(url); if(imageSize != null) { // TODO: do we habe a title? - photoGalleryUrls.add(new ImageMetadataDTO(eventRef, url, imageSize, null)); + photoGalleryUrls.add(new SailingImageDTO(eventRef, url.toString(), imageSize, null)); } } catch (Exception e) { } @@ -229,7 +231,10 @@ public class HomeServiceImpl extends ProxiedRemoteServiceServlet implements Home MimeType type = MediaUtils.detectMimeTypeFromUrl(videoUrl.toString()); if (type.mediaType == MediaType.video) { - VideoMetadataDTO candidate = new VideoMetadataDTO(eventRef, videoUrl, type, holder.event.getName()); + SailingVideoDTO candidate = new SailingVideoDTO(eventRef, videoUrl.toString(), type, // + holder.event.getEndDate().asDate() // FIXME: using event enddate for now + ); + candidate.setTitle(holder.event.getName()); videoCandidates.add(candidate); } } @@ -238,7 +243,7 @@ public class HomeServiceImpl extends ProxiedRemoteServiceServlet implements Home final int numberOfCandidatesAvailable = videoCandidates.size(); if (numberOfCandidatesAvailable <= (MAX_VIDEO_COUNT - result.getVideos().size())) { // add all we have, no randomize - for (VideoMetadataDTO video : videoCandidates) { + for (SailingVideoDTO video : videoCandidates) { result.addVideo(video); } } else { @@ -246,7 +251,7 @@ public class HomeServiceImpl extends ProxiedRemoteServiceServlet implements Home final Random videosRandomizer = new Random(numberOfCandidatesAvailable); randomlyPick: for (int i = 0; i < numberOfCandidatesAvailable; i++) { int nextVideoindex = videosRandomizer.nextInt(numberOfCandidatesAvailable); - final VideoMetadataDTO video = videoCandidates.get(nextVideoindex); + final SailingVideoDTO video = videoCandidates.get(nextVideoindex); result.addVideo(video); if (result.getVideos().size() == MAX_VIDEO_COUNT) { break randomlyPick; @@ -254,12 +259,12 @@ public class HomeServiceImpl extends ProxiedRemoteServiceServlet implements Home } } Random random = new Random(); - List shuffledPhotoGallery = new ArrayList<>(photoGalleryUrls); + List shuffledPhotoGallery = new ArrayList<>(photoGalleryUrls); final int gallerySize = photoGalleryUrls.size(); for (int i = 0; i < gallerySize; i++) { Collections.swap(shuffledPhotoGallery, i, random.nextInt(gallerySize)); } - for (ImageMetadataDTO holder : shuffledPhotoGallery) { + for (SailingImageDTO holder : shuffledPhotoGallery) { result.addPhoto(holder); } // TODO media @@ -429,13 +434,16 @@ public class HomeServiceImpl extends ProxiedRemoteServiceServlet implements Home } catch (InterruptedException | ExecutionException e) { logger.log(Level.FINE, "Was unable to obtain image size for "+url+" earlier.", e); } - ImageMetadataDTO entry = new ImageMetadataDTO(eventRef, url, imageSize, eventName); + SailingImageDTO entry = new SailingImageDTO(eventRef, url.toString(), imageSize, // + event.getEndDate().asDate() // FIXME: using the event end date for now, we need a better solution for migration + ); + entry.setTitle(eventName); media.addPhoto(entry); } for(URL url : event.getVideoURLs()) { MimeType type = MediaUtils.detectMimeTypeFromUrl(url.toString()); if (type.mediaType == MediaType.video) { - VideoMetadataDTO candidate = new VideoMetadataDTO(eventRef, url, type, null); + SailingVideoDTO candidate = new SailingVideoDTO(eventRef, url.toString(), type, null); media.addVideo(candidate); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java index 496f852a088..b5c9696e94b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/server/HomeServiceUtil.java @@ -19,7 +19,6 @@ import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.LeaderboardGroupBase; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.Regatta; -import com.sap.sailing.domain.common.ImageSize; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; import com.sap.sailing.domain.tracking.TrackedRace; @@ -27,6 +26,7 @@ import com.sap.sailing.gwt.ui.shared.eventview.HasRegattaMetadata.RegattaState; import com.sap.sailing.gwt.ui.shared.eventview.RegattaMetadataDTO; import com.sap.sailing.gwt.ui.shared.general.EventState; import com.sap.sse.common.Util; +import com.sap.sse.common.media.ImageSize; public final class HomeServiceUtil { private HomeServiceUtil() { @@ -315,6 +315,9 @@ public final class HomeServiceUtil { boolean first = true; for (Iterator iter = imageURLs.iterator(); iter.hasNext(); ) { URL imageUrl = iter.next(); + if(imageUrl.toString().toLowerCase().contains(THUMBNAIL_IMAGE_URL_SUBSTRING_INDICATOR_CASE_INSENSITIVE)) { + continue; + } if ((first && !iter.hasNext()) || !Util.equalsWithNull(imageUrl, stageImageURL)) { result.add(imageUrl); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java index d62f2f12d55..45df1c68a12 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/DetailTypeFormatter.java @@ -45,7 +45,7 @@ public class DetailTypeFormatter { case GAP_TO_LEADER_IN_SECONDS: return stringMessages.gapToLeaderInSeconds(); case CORRECTED_TIME_TRAVELED: - return stringMessages.correctedTimeTraveled(); + return stringMessages.calculatedTimeTraveled(); case GAP_CHANGE_SINCE_LEG_START_IN_SECONDS: return stringMessages.gapChangeSinceLegStartInSeconds(); case SIDE_TO_WHICH_MARK_AT_LEG_START_WAS_ROUNDED: @@ -60,16 +60,16 @@ public class DetailTypeFormatter { return stringMessages.averageSpeedInKnots(); case RACE_GAP_TO_LEADER_IN_SECONDS: return stringMessages.gapToLeaderInSeconds(); - case RACE_DISTANCE_TO_LEADER_IN_METERS: - return stringMessages.windwardDistanceToLeaderInMeters(); + case RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS: + return stringMessages.windwardDistanceToCompetitorFarthestAheadInMeters(); case RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS: return stringMessages.averageAbsoluteCrossTrackErrorInMeters(); case RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS: return stringMessages.averageSignedCrossTrackErrorInMeters(); case START_TACK: return stringMessages.startTack(); - case WINDWARD_DISTANCE_TO_OVERALL_LEADER: - return stringMessages.windwardDistanceToLeaderInMeters(); + case WINDWARD_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD: + return stringMessages.windwardDistanceToCompetitorFarthestAheadInMeters(); case DISTANCE_TO_START_AT_RACE_START: return stringMessages.distanceToLineAtRaceStart(); case TIME_BETWEEN_RACE_START_AND_COMPETITOR_START: @@ -128,10 +128,10 @@ public class DetailTypeFormatter { return stringMessages.speedOverGroundFiveSecondsBeforeStart(); case RACE_TIME_TRAVELED: return stringMessages.time(); - case RACE_CORRECTED_TIME_TRAVELED: - return stringMessages.correctedTimeTraveled(); - case RACE_CORRECTED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD: - return stringMessages.raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds(); + case RACE_CALCULATED_TIME_TRAVELED: + return stringMessages.calculatedTimeTraveled(); + case RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD: + return stringMessages.raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds(); case RACE_TIME_TRAVELED_DOWNWIND: return stringMessages.timeDownwind(); case RACE_TIME_TRAVELED_REACHING: @@ -170,11 +170,11 @@ public class DetailTypeFormatter { case RACE_SPEED_OVER_GROUND_FIVE_SECONDS_BEFORE_START: return stringMessages.knotsUnit(); - case WINDWARD_DISTANCE_TO_OVERALL_LEADER: + case WINDWARD_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD: case WINDWARD_DISTANCE_TO_GO_IN_METERS: case DISTANCE_TRAVELED: case DISTANCE_TRAVELED_INCLUDING_GATE_START: - case RACE_DISTANCE_TO_LEADER_IN_METERS: + case RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS: case RACE_DISTANCE_TRAVELED: case RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START: case AVERAGE_TACK_LOSS_IN_METERS: @@ -200,13 +200,13 @@ public class DetailTypeFormatter { case ESTIMATED_TIME_TO_NEXT_WAYPOINT_IN_SECONDS: case TIME_TRAVELED: case CORRECTED_TIME_TRAVELED: - case RACE_CORRECTED_TIME_TRAVELED: + case RACE_CALCULATED_TIME_TRAVELED: case RACE_TIME_TRAVELED: case RACE_TIME_TRAVELED_DOWNWIND: case RACE_TIME_TRAVELED_REACHING: case RACE_TIME_TRAVELED_UPWIND: case TIME_BETWEEN_RACE_START_AND_COMPETITOR_START: - case RACE_CORRECTED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD: + case RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD: return stringMessages.secondsUnit(); case TIME_ON_DISTANCE_ALLOWANCE_IN_SECONDS_PER_NAUTICAL_MILE: @@ -289,7 +289,7 @@ public class DetailTypeFormatter { case GAP_TO_LEADER_IN_SECONDS: return stringMessages.gapToLeaderInSecondsTooltip(); case CORRECTED_TIME_TRAVELED: - return stringMessages.correctedTimeTraveledTooltip(); + return stringMessages.calculatedTimeTraveledTooltip(); case JIBE: return stringMessages.jibeTooltip(); case MAXIMUM_SPEED_OVER_GROUND_IN_KNOTS: @@ -304,10 +304,10 @@ public class DetailTypeFormatter { return stringMessages.currentOrAverageSpeedOverGroundInKnotsTooltip(); case RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS: return stringMessages.currentSpeedOverGroundInKnotsTooltip(); - case RACE_DISTANCE_TO_LEADER_IN_METERS: - return stringMessages.windwardDistanceToLeaderInMetersTooltip(); - case WINDWARD_DISTANCE_TO_OVERALL_LEADER: - return stringMessages.windwardDistanceToLeaderInMetersTooltip(); + case RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS: + return stringMessages.windwardDistanceToCompetitorFarthestAheadInMetersTooltip(); + case WINDWARD_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD: + return stringMessages.windwardDistanceToCompetitorFarthestAheadInMetersTooltip(); case DISTANCE_TO_START_AT_RACE_START: return stringMessages.distanceToLineAtRaceStartTooltip(); case TIME_BETWEEN_RACE_START_AND_COMPETITOR_START: @@ -326,10 +326,10 @@ public class DetailTypeFormatter { return stringMessages.raceDistanceTraveledIncludingGateStartTooltip(); case RACE_GAP_TO_LEADER_IN_SECONDS: return stringMessages.gapToLeaderInSecondsTooltip(); - case RACE_CORRECTED_TIME_TRAVELED: - return stringMessages.correctedTimeTraveledTooltip(); - case RACE_CORRECTED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD: - return stringMessages.raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip(); + case RACE_CALCULATED_TIME_TRAVELED: + return stringMessages.calculatedTimeTraveledTooltip(); + case RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD: + return stringMessages.raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip(); case RACE_RANK: return stringMessages.rankTooltip(); case REGATTA_RANK: 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 66dc914fb19..503696829dd 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 @@ -3,6 +3,7 @@ package com.sap.sailing.gwt.ui.client; import com.google.gwt.core.client.GWT; import com.google.gwt.i18n.client.LocalizableResource.DefaultLocale; import com.google.gwt.json.client.JSONValue; +import com.sap.sse.gwt.client.DefaultRule_0_1_2_n; import com.sap.sse.gwt.client.DefaultRule_0_1_n; import com.sap.sse.gwt.client.DefaultRule_1_0n; @@ -93,7 +94,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c String gapToLeaderInSeconds(); String velocityMadeGoodInKnots(); String windwardDistanceToGoInMeters(); - String windwardDistanceToLeaderInMeters(); + String windwardDistanceToCompetitorFarthestAheadInMeters(); String name(); String leaveEmptyForDefault(); String trackNewEvent(); @@ -511,7 +512,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c String penaltyCircleTooltip(); String raceAverageSpeedInKnotsTooltip(); String currentSpeedOverGroundInKnotsTooltip(); - String windwardDistanceToLeaderInMetersTooltip(); + String windwardDistanceToCompetitorFarthestAheadInMetersTooltip(); String raceDistanceTraveledTooltip(); String rankTooltip(); String rankGainTooltip(); @@ -1067,7 +1068,7 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c String addRaces(); String withNamePrefix(); String pleaseSelectASeriesFirst(); - String upcommingEventStartsInDays(@PluralCount(DefaultRule_0_1_n.class) int days); + String upcommingEventStartsInDays(@PluralCount(DefaultRule_0_1_2_n.class) int days); String sharingMailTooltip(); String sharingTwitterTooltip(); String sharingFacebookTooltip(); @@ -1300,10 +1301,10 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages, c String timeOnTimeAndDistanceRankingMetricDescription(); String timeOnTimeFactor(); String timeOnDistanceAllowanceInSecondsPerNauticalMile(); - String correctedTimeTraveled(); - String correctedTimeTraveledTooltip(); + String calculatedTimeTraveled(); + String calculatedTimeTraveledTooltip(); String timeOnTimeFactorTooltip(); String timeOnDistanceAllowanceInSecondsPerNauticalMileTooltip(); - String raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds(); - String raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip(); + String raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds(); + String raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip(); } 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 2695f161d7f..8fc95b51639 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 @@ -86,13 +86,13 @@ currentSpeedOverGroundInKnotsTooltip=The current speed over ground. estimatedTimeToNextWaypointInSeconds=ETA estimatedTimeToNextWaypointInSecondsTooltip=The estimated time that the competitor needs to reach the next waypoint, based on the competitor''s current velocity made good, considering the true wind direction. gapToLeaderInSeconds=Gap to leader -gapToLeaderInSecondsTooltip=Estimates the (corrected) arrival time at the current position of the boat that is farthest ahead,\nbased on average VMG for the current leg and with performance equal to that of the race leader for subsequent legs.\nThe gap tells how much earlier the competitor would have had to be where she currently is\nin order to rank equal to the race leader at the projected arrival at the current position of the boat farthest ahead. +gapToLeaderInSecondsTooltip=Estimates the (calculated) arrival time at the current position of the boat that is farthest ahead,\nbased on average VMG for the current leg and with performance equal to that of the race leader for subsequent legs.\nThe gap tells how much earlier the competitor would have had to be where she currently is\nin order to rank equal to the race leader at the projected arrival at the current position of the boat farthest ahead. velocityMadeGoodInKnots=VMG velocityMadeGoodInKnotsTooltip=The velocity made good, which is the windward/leeward speed for upwind/downwind legs, respectively, or the along-track speed for reaching legs. windwardDistanceToGoInMeters=Windward distance to go windwardDistanceToGoInMetersTooltip=Windward/leeward distance from the competitor''s current position to the end of the leg for upwind/downwind legs, respectively, or the along-track distance for reaching legs. -windwardDistanceToLeaderInMeters=Windward Distance To Leader -windwardDistanceToLeaderInMetersTooltip=Windward/leeward distance of the competitor to the leader for upwind/downwind legs, respectively, or the along-track distance for reaching legs. +windwardDistanceToCompetitorFarthestAheadInMeters=Windward Distance To Competitor Farthest Ahead +windwardDistanceToCompetitorFarthestAheadInMetersTooltip=Windward/leeward distance of the competitor to the competitor farthest ahead in the race for upwind/downwind legs, respectively, or the along-track distance for reaching legs.\nNote that the competitor that is farthest ahead is not necessarily leading the race if handicap ranking is in place. name=Name leaveEmptyForDefault=Leave empty for default trackNewEvent=Track New event @@ -1266,12 +1266,12 @@ orcPerformanceCurveRankingMetric=ORC Performance Curve timeOnTimeAndDistanceRankingMetric=Time-on-Time/Time-on-Distance oneDesignRankingMetricDescription=One-Design: First across the line wins;\nin-race ranks based on windward positions (upwind/downwind) or along-course position (reaching) orcPerformanceCurveRankingMetricDescription=ORC Performance Curve: Sophisticated handicapping scheme that awards time allowances based on a VPP/polar assuming pre-defined legs'' angles to the wind;\nbased on these allowances, an "implied wind" is calculated which is the basis for ranking.\nImplied wind can be calculated at any time in the race and is therefore the basis for in-race ranking. -timeOnTimeAndDistanceRankingMetricDescription=Time-on-Time / Time-on-Distance: Final ranking by corrected time;\ntime is corrected by optionally applying a factor to the actual time (defaulting to 1.0)\nand subtracting an allowance in seconds per nautical mile,\napplied to the course length.\nIn-race ranks based on expected arrival at fastest boat's current position\nassuming current leg's average VMG/VMC. +timeOnTimeAndDistanceRankingMetricDescription=Time-on-Time / Time-on-Distance: Final ranking by calculated time;\ntime is calculated by optionally applying a factor to the actual time (defaulting to 1.0)\nand subtracting an allowance in seconds per nautical mile,\napplied to the course length.\nIn-race ranks based on expected arrival at fastest boat's current position\nassuming current leg's average VMG/VMC. timeOnTimeFactor=ToT Factor timeOnDistanceAllowanceInSecondsPerNauticalMile=ToD Allowance timeOnTimeFactorTooltip=Time-on-Time factor; actual time is multiplied with this factor in calculated time formula -timeOnDistanceAllowanceInSecondsPerNauticalMileTooltip=Time-on-Distance allowance in seconds per nautical mile;\ndistance sailed is multiplied with this allowance, and the resulting seconds are subtracted from the actual time sailed\n(optionally corrected by the time-on-time factor) in the calculated time formula -correctedTimeTraveled=Corrected Time -correctedTimeTraveledTooltip=Corrected time according to competitor''s handicap\nbased on time since start of race and (windward) distance traveled -raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Corr.Time@Fastest -raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Corrected time at estimated arrival at the competitor that is currently farthest ahead in the race;\nestimation based on average VMG in current leg and equal (corrected) performance to fastest boat in all subsequent legs \ No newline at end of file +timeOnDistanceAllowanceInSecondsPerNauticalMileTooltip=Time-on-Distance allowance in seconds per nautical mile;\ndistance sailed is multiplied with this allowance, and the resulting seconds are subtracted from the actual time sailed\n(optionally calculated by applying the time-on-time factor) in the calculated time formula +calculatedTimeTraveled=Calculated Time +calculatedTimeTraveledTooltip=Calculated time according to competitor''s handicap\nbased on time since start of race and (windward) distance traveled +raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Calc.Time @ Fastest +raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Calculated time at estimated arrival at the competitor that is currently farthest ahead in the race;\nestimation based on average VMG in current leg and equal (calculated) performance to fastest boat in all subsequent legs \ 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_cn.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_cn.properties index 54997f9069d..6a65e50fde4 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_cn.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_cn.properties @@ -814,7 +814,7 @@ papaRemoved=Start mode flag - one minute till start showDiagram=显示图表 dontoverwrite=Don''t overwrite storedData=Stored data -windwardDistanceToLeaderInMeters=迎风航段时离开上风第一名的平行距离 +windwardDistanceToCompetitorFarthestAheadInMeters=迎风航段时离开上风第一名的平行距离 hostname=Hostname distanceIncludingGateStartInMeters=距离(起航门标) valueForCompetitorAt={0} at {1}: {2} @@ -829,7 +829,7 @@ windChart=Wind Chart events=活动 racesAreLive=Races {0} are live operatorNotContains=Not Contains -windwardDistanceToLeaderInMetersTooltip=迎/顺风航段时离开上风/下风第一名的平行距离或横风时的实际距离name 名字 +windwardDistanceToCompetitorFarthestAheadInMetersTooltip=迎/顺风航段时离开上风/下风第一名的平行距离或横风时的实际距离name 名字 errorAddingResultImportUrl=Error adding result import URL: {0} noGrouperSelectedError=No grouper type is selected leaderboardsExceptFromSelectedGroup=Leaderboards (except those of selected group) @@ -1082,7 +1082,7 @@ clickAndDragToZoomIn=Click and drag in the plot area to zoom in runAutomaticallyTooltip=Run the query automatically, after you changed e.g. the statistic or the grouping. essThreeDisplayed=Three minutes till start actionEdit=Edit -gapToLeaderInSecondsTooltip=Estimates the (corrected) arrival time at the current position of the boat that is farthest ahead,\nbased on average VMG for the current leg and with performance equal to that of the race leader for subsequent legs.\nThe gap tells how much earlier the competitor would have had to be where she currently is\nin order to rank equal to the race leader at the projected arrival at the current position of the boat farthest ahead. +gapToLeaderInSecondsTooltip=Estimates the (calculated) arrival time at the current position of the boat that is farthest ahead,\nbased on average VMG for the current leg and with performance equal to that of the race leader for subsequent legs.\nThe gap tells how much earlier the competitor would have had to be where she currently is\nin order to rank equal to the race leader at the projected arrival at the current position of the boat farthest ahead. showAllCompetitors=显示所有参赛队 leaderboardNotContainedInLeaderboardGroup=The leaderboard {0} is not contained in leaderboard group {1}. playSpeed=游戏速度 @@ -1257,12 +1257,12 @@ orcPerformanceCurveRankingMetric=ORC Performance Curve timeOnTimeAndDistanceRankingMetric=Time-on-Time/Time-on-Distance oneDesignRankingMetricDescription=One-Design: First across the line wins;\nin-race ranks based on windward positions (upwind/downwind) or along-course position (reaching) orcPerformanceCurveRankingMetricDescription=ORC Performance Curve: Sophisticated handicapping scheme that awards time allowances based on a VPP/polar assuming pre-defined legs'' angles to the wind;\nbased on these allowances, an "implied wind" is calculated which is the basis for ranking.\nImplied wind can be calculated at any time in the race and is therefore the basis for in-race ranking. -timeOnTimeAndDistanceRankingMetricDescription=Time-on-Time / Time-on-Distance: Final ranking by corrected time;\ntime is corrected by optionally applying a factor to the actual time (defaulting to 1.0)\nand subtracting an allowance in seconds per nautical mile,\napplied to the course length.\nIn-race ranks based on expected arrival at fastest boat's current position\nassuming current leg's average VMG/VMC. +timeOnTimeAndDistanceRankingMetricDescription=Time-on-Time / Time-on-Distance: Final ranking by calculated time;\ntime is calculated by optionally applying a factor to the actual time (defaulting to 1.0)\nand subtracting an allowance in seconds per nautical mile,\napplied to the course length.\nIn-race ranks based on expected arrival at fastest boat's current position\nassuming current leg's average VMG/VMC. timeOnTimeFactor=Time-on-time handicap factor timeOnDistanceAllowanceInSecondsPerNauticalMile=Time-on-distance handicap allowance in seconds per nautical mile timeOnTimeFactorTooltip=Time-on-Time factor; actual time is multiplied with this factor in calculated time formula -timeOnDistanceAllowanceInSecondsPerNauticalMileTooltip=Time-on-Distance allowance in seconds per nautical mile;\ndistance sailed is multiplied with this allowance, and the resulting seconds are subtracted from the actual time sailed\n(optionally corrected by the time-on-time factor) in the calculated time formula -correctedTimeTraveled=Corrected Time -correctedTimeTraveledTooltip=Corrected time according to competitor''s handicap\nbased on time since start of race and (windward) distance traveled -raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Corr.Time@Fastest -raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Corrected time at estimated arrival at the competitor that is currently farthest ahead in the race;\nestimation based on average VMG in current leg and equal (corrected) performance to fastest boat in all subsequent legs \ No newline at end of file +timeOnDistanceAllowanceInSecondsPerNauticalMileTooltip=Time-on-Distance allowance in seconds per nautical mile;\ndistance sailed is multiplied with this allowance, and the resulting seconds are subtracted from the actual time sailed\n(optionally calculated by applying the time-on-time factor) in the calculated time formula +calculatedTimeTraveled=Calculated Time +calculatedTimeTraveledTooltip=Calculated time according to competitor''s handicap\nbased on time since start of race and (windward) distance traveled +raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Calc.Time @ Fastest +raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Calculated time at estimated arrival at the competitor that is currently farthest ahead in the race;\nestimation based on average VMG in current leg and equal (calculated) performance to fastest boat in all subsequent legs 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 ef4648959fd..f35de4ba917 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 @@ -86,13 +86,13 @@ currentSpeedOverGroundInKnotsTooltip=Die aktuelle Fahrt über Grund estimatedTimeToNextWaypointInSeconds=ETA estimatedTimeToNextWaypointInSecondsTooltip=Die vorraussichtliche Zeit, die der Teilnehmer benötigen wird,\num den nächsten Wegpunkt zu erreichen gapToLeaderInSeconds=Abstand zum Führenden -gapToLeaderInSecondsTooltip=Schätzt die (korrigierte) Ankunftszeit an der jetzigen Position des bisher nach gesegelter Distanz in Führung liegenden Teilnehmers\nbasierend auf dem VMG des aktuellen Bahnschenkels, danach gleichauf mit dem Führenden.\nDer Zeitabstand besagt dann, wieviel früher der Teilnehmer an seiner jetzigen Position hätte sein müssen, um gemäß dieser Vorhersage gleichauf mit dem Führenden nach korrigierter Zeit zu liegen. +gapToLeaderInSecondsTooltip=Schätzt die (berechnete) Ankunftszeit an der jetzigen Position des bisher nach gesegelter Distanz in Führung liegenden Teilnehmers\nbasierend auf dem VMG des aktuellen Bahnschenkels, danach gleichauf mit dem Führenden.\nDer Zeitabstand besagt dann, wieviel früher der Teilnehmer an seiner jetzigen Position hätte sein müssen, um gemäß dieser Vorhersage gleichauf mit dem Führenden nach korrigierter Zeit zu liegen. velocityMadeGoodInKnots=VMG velocityMadeGoodInKnotsTooltip=Die effektive Geschwindigkeit zum Ziel; auf der Kreuz die windwärtige Geschwindigkeit; auf der Vorwindstrecke die leewärtige Geschwindigkeit windwardDistanceToGoInMeters=Abstand in Windrichtung zur nächsten Bahnmarke windwardDistanceToGoInMetersTooltip=Windwärtige / leewärtige Distanz des Teilnehmerns bis zum Ende des Schenkels auf Kreuz bzw. Vorwindstrecke; Strecke entlang des Schenkels bei Holeschlägen -windwardDistanceToLeaderInMeters=Abstand zum führenden Boot -windwardDistanceToLeaderInMetersTooltip=Windwärtige Distanz des Teilnehmers zum Führenden +windwardDistanceToCompetitorFarthestAheadInMeters=Windärtiger Abstand zum schnellsten Boot +windwardDistanceToCompetitorFarthestAheadInMetersTooltip=Windwärtiger Abstand zum Boot, das bisher am weitesten windwärts gesegelt ist.\nFür Handicap-Wertungen ist das nicht notwendigerweise der nach Verrechnung in Führung liegende Teilnehmer. name=Name leaveEmptyForDefault=Leer für Standardeinstellung trackNewEvent=Track New event @@ -513,7 +513,7 @@ upcoming=demnächst next=gleich inProgress=aktiv passedTo=an {0} gerundet -gapChangeSinceLegStartInSeconds=Gap Änderung +gapChangeSinceLegStartInSeconds=Änderung Abstand zum Führenden gapChangeSinceLegStartInSecondsTooltip=Die Änderung des Abstands zum Führenden seit dem letzten Schenkelstart.\nEine negative Zahl bedeutet,\ndass sich der Abstand verringert hat; eine positive Zahl bedeutet, dass er sich vergrößert hat. sideToWhichMarkAtLegStartWasRounded=Tonne gerundet an sideToWhichMarkAtLegStartWasRoundedTooltip=Seite, an der die Bahnmarke am Schenkelstart gerundet wurde @@ -1261,7 +1261,7 @@ timeOnTimeFactor=ToT Faktor timeOnDistanceAllowanceInSecondsPerNauticalMile=ToD Zuteilung timeOnTimeFactorTooltip=Zeit-auf-Zeit Faktor; tatsächlich gesegelte Zeit wird für die berechnete Zeit mit diesem Faktor multipliziert timeOnDistanceAllowanceInSecondsPerNauticalMileTooltip=Zeit-auf-Distanz Zuteilung in Sekunden pro Seemeile;\ndie gesegelte Distanz wird mit diesem Wert multipliziert, und die sich ergebende Zeit wird von der (ggf. per ToT-Faktor angepassten) Zeit subtrahiert -correctedTimeTraveled=Berechnete Zeit -correctedTimeTraveledTooltip=Berechnete Zeit anhand Teilnehmer-Handicap\nauf der Grundlage von Zeit seit Rennstart und zurückgelegter Distanz -raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Ber.Zeit@Führender -raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Berechnete Zeit bei erwarteter Ankunft am derzeit absolut in Führung liegenden Teilnehmers;\nSchätzung basierend auf durchschnittlichem VMG im aktuellen Bahnschenkel und gleicher verrechneter Geschwindigket wie der absolut in Führung liegende Teilnehmer\nfür alle nachfolgenden Bahnschenkel \ No newline at end of file +calculatedTimeTraveled=Berechnete Zeit +calculatedTimeTraveledTooltip=Berechnete Zeit anhand Teilnehmer-Handicap\nauf der Grundlage von Zeit seit Rennstart und zurückgelegter Distanz +raceCalcluatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Ber.Zeit@Führender +raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Berechnete Zeit bei erwarteter Ankunft am derzeit absolut in Führung liegenden Teilnehmers;\nSchätzung basierend auf durchschnittlichem VMG im aktuellen Bahnschenkel und gleicher verrechneter Geschwindigket wie der absolut in Führung liegende Teilnehmer\nfür alle nachfolgenden Bahnschenkel \ 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_ru.properties b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties index 8107eb03ad0..83bc6022fd8 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/StringMessages_ru.properties @@ -86,13 +86,13 @@ currentSpeedOverGroundInKnotsTooltip=Текущая скорость относ estimatedTimeToNextWaypointInSeconds=Расчетное время прибытия (ETA). estimatedTimeToNextWaypointInSecondsTooltip= Расчетное время, необходимое участнику для прибытия в следующую промежуточную точку маршрута, исходя из скорости лавирования участника и с учетом истинного направление ветра. gapToLeaderInSeconds=Отставание от лидера -gapToLeaderInSecondsTooltip=Estimates the (corrected) arrival time at the current position of the boat that is farthest ahead,\nbased on average VMG for the current leg and with performance equal to that of the race leader for subsequent legs.\nThe gap tells how much earlier the competitor would have had to be where she currently is\nin order to rank equal to the race leader at the projected arrival at the current position of the boat farthest ahead. +gapToLeaderInSecondsTooltip=Estimates the (calculated) arrival time at the current position of the boat that is farthest ahead,\nbased on average VMG for the current leg and with performance equal to that of the race leader for subsequent legs.\nThe gap tells how much earlier the competitor would have had to be where she currently is\nin order to rank equal to the race leader at the projected arrival at the current position of the boat farthest ahead. velocityMadeGoodInKnots=Скорость лавирования (VMG) velocityMadeGoodInKnotsTooltip=Скорость лавирования, представляет собой скорость продвижения на ветер/ по ветру для этапов движения против ветра/ по ветру соответственно, или скорость в направлении движения при лавировании по ветру windwardDistanceToGoInMeters=Расстояние, которое необходимо пройти на ветер windwardDistanceToGoInMetersTooltip=Расстояние движения на ветер/ по ветру от текущего положения участника до конца этапа для этапов движения против ветра/ по ветру соответственно, или скорость в направлении движения при лавировании по ветру. -windwardDistanceToLeaderInMeters=Расстояние на ветер до лидера -windwardDistanceToLeaderInMetersTooltip=Расстояние на ветер/ под ветер от участника до лидера для этапов движения против ветра/ по ветру соответственно или скорость в направлении движения при лавировании по ветру. +windwardDistanceToCompetitorFarthestAheadInMeters=Расстояние на ветер до лидера +windwardDistanceToCompetitorFarthestAheadInMetersTooltip=Расстояние на ветер/ под ветер от участника до лидера для этапов движения против ветра/ по ветру соответственно или скорость в направлении движения при лавировании по ветру. name=Имя leaveEmptyForDefault=Оставьте пустым по умолчанию. trackNewEvent=Отследить новое событие @@ -1204,12 +1204,12 @@ orcPerformanceCurveRankingMetric=ORC Performance Curve timeOnTimeAndDistanceRankingMetric=Time-on-Time/Time-on-Distance oneDesignRankingMetricDescription=One-Design: First across the line wins;\nin-race ranks based on windward positions (upwind/downwind) or along-course position (reaching) orcPerformanceCurveRankingMetricDescription=ORC Performance Curve: Sophisticated handicapping scheme that awards time allowances based on a VPP/polar assuming pre-defined legs'' angles to the wind;\nbased on these allowances, an "implied wind" is calculated which is the basis for ranking.\nImplied wind can be calculated at any time in the race and is therefore the basis for in-race ranking. -timeOnTimeAndDistanceRankingMetricDescription=Time-on-Time / Time-on-Distance: Final ranking by corrected time;\ntime is corrected by optionally applying a factor to the actual time (defaulting to 1.0)\nand subtracting an allowance in seconds per nautical mile,\napplied to the course length.\nIn-race ranks based on expected arrival at fastest boat's current position\nassuming current leg's average VMG/VMC. +timeOnTimeAndDistanceRankingMetricDescription=Time-on-Time / Time-on-Distance: Final ranking by calculated time;\ntime is calculated by optionally applying a factor to the actual time (defaulting to 1.0)\nand subtracting an allowance in seconds per nautical mile,\napplied to the course length.\nIn-race ranks based on expected arrival at fastest boat's current position\nassuming current leg's average VMG/VMC. timeOnTimeFactor=Time-on-time handicap factor timeOnDistanceAllowanceInSecondsPerNauticalMile=Time-on-distance handicap allowance in seconds per nautical mile timeOnTimeFactorTooltip=Time-on-Time factor; actual time is multiplied with this factor in calculated time formula -timeOnDistanceAllowanceInSecondsPerNauticalMileTooltip=Time-on-Distance allowance in seconds per nautical mile;\ndistance sailed is multiplied with this allowance, and the resulting seconds are subtracted from the actual time sailed\n(optionally corrected by the time-on-time factor) in the calculated time formula -correctedTimeTraveled=Corrected Time -correctedTimeTraveledTooltip=Corrected time according to competitor''s handicap\nbased on time since start of race and (windward) distance traveled -raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Corr.Time@Fastest -raceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Corrected time at estimated arrival at the competitor that is currently farthest ahead in the race;\nestimation based on average VMG in current leg and equal (corrected) performance to fastest boat in all subsequent legs \ No newline at end of file +timeOnDistanceAllowanceInSecondsPerNauticalMileTooltip=Time-on-Distance allowance in seconds per nautical mile;\ndistance sailed is multiplied with this allowance, and the resulting seconds are subtracted from the actual time sailed\n(optionally calculated by applying the time-on-time factor) in the calculated time formula +calculatedTimeTraveled=Calculated Time +calculatedTimeTraveledTooltip=Calculated time according to competitor''s handicap\nbased on time since start of race and (windward) distance traveled +raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds=Calc.Time @ Fastest +raceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSecondsTooltip=Calculated time at estimated arrival at the competitor that is currently farthest ahead in the race;\nestimation based on average VMG in current leg and equal (calculated) performance to fastest boat in all subsequent legs diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/GalleryImageHolder.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/GalleryImageHolder.java index 07888d05315..86e8d235299 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/GalleryImageHolder.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/GalleryImageHolder.java @@ -10,7 +10,7 @@ import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.gwt.ui.shared.media.ImageMetadataDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingImageDTO; public class GalleryImageHolder extends Widget implements HasClickHandlers { @@ -22,7 +22,7 @@ public class GalleryImageHolder extends Widget implements HasClickHandlers { @UiField DivElement imageHolderUi; - public GalleryImageHolder(ImageMetadataDTO video) { + public GalleryImageHolder(SailingImageDTO video) { setElement(uiBinder.createAndBindUi(this)); imageHolderUi.getStyle().setBackgroundImage("url(\"" + video.getSourceRef() + "\")"); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaPlayerManagerComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaPlayerManagerComponent.java index bbe7417a4f7..843955e5fa2 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaPlayerManagerComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaPlayerManagerComponent.java @@ -24,7 +24,6 @@ import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.media.MediaTrack; import com.sap.sailing.domain.common.media.MediaTrack.Status; -import com.sap.sailing.domain.common.media.MediaType; import com.sap.sailing.domain.common.security.Roles; import com.sap.sailing.gwt.ui.client.MediaServiceAsync; import com.sap.sailing.gwt.ui.client.RaceTimesInfoProvider; @@ -38,6 +37,7 @@ import com.sap.sailing.gwt.ui.client.media.shared.VideoPlayer; import com.sap.sailing.gwt.ui.client.media.shared.VideoSynchPlayer; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MediaType; import com.sap.sse.common.settings.AbstractSettings; import com.sap.sse.gwt.client.ErrorReporter; import com.sap.sse.gwt.client.dialog.DataEntryDialog.DialogCallback; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaSingleSelectionControl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaSingleSelectionControl.java index 03596683ccc..4f764a42807 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaSingleSelectionControl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/MediaSingleSelectionControl.java @@ -14,8 +14,8 @@ import com.google.gwt.user.client.ui.PopupPanel; import com.google.gwt.user.client.ui.UIObject; import com.google.gwt.user.client.ui.VerticalPanel; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MediaType; import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sse.common.media.MediaType; public class MediaSingleSelectionControl extends AbstractMediaSelectionControl implements CloseHandler { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java index 074085cb40b..90185a31465 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/NewMediaDialog.java @@ -17,12 +17,12 @@ import com.google.gwt.user.client.ui.VerticalPanel; import com.google.gwt.user.client.ui.Widget; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.gwt.ui.client.StringMessages; import com.sap.sailing.gwt.ui.common.client.YoutubeApi; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MimeType; import com.sap.sse.gwt.client.dialog.DataEntryDialog; public class NewMediaDialog extends DataEntryDialog { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoJSPlayer.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoJSPlayer.java index e460bf51f96..e3c3eeb5173 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoJSPlayer.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoJSPlayer.java @@ -9,9 +9,9 @@ import com.google.gwt.dom.client.VideoElement; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.domain.common.media.MediaSubType; -import com.sap.sailing.domain.common.media.MediaType; -import com.sap.sailing.domain.common.media.MimeType; +import com.sap.sse.common.media.MediaSubType; +import com.sap.sse.common.media.MediaType; +import com.sap.sse.common.media.MimeType; public class VideoJSPlayer extends Widget { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoThumbnail.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoThumbnail.java index 9a31f35489a..0b4ac3b0e83 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoThumbnail.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/media/VideoThumbnail.java @@ -11,9 +11,9 @@ import com.google.gwt.event.shared.HandlerRegistration; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.uibinder.client.UiField; import com.google.gwt.user.client.ui.Widget; -import com.sap.sailing.domain.common.media.MediaSubType; import com.sap.sailing.gwt.ui.common.client.YoutubeApi; -import com.sap.sailing.gwt.ui.shared.media.VideoMetadataDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingVideoDTO; +import com.sap.sse.common.media.MediaSubType; public class VideoThumbnail extends Widget implements HasClickHandlers { @@ -28,7 +28,7 @@ public class VideoThumbnail extends Widget implements HasClickHandlers { @UiField ImageElement thumbnailUi; - public VideoThumbnail(VideoMetadataDTO video) { + public VideoThumbnail(SailingVideoDTO video) { setElement(uiBinder.createAndBindUi(this)); getElement().addClassName("videoThumbnail"); captionUi.setInnerText(video.getTitle()); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorRaceChart.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorRaceChart.java index a2d0ca9c751..28ed045dec9 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorRaceChart.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/AbstractCompetitorRaceChart.java @@ -539,7 +539,7 @@ public abstract class AbstractCompetitorRaceChart() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorRaceChartSettingsComponent.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorRaceChartSettingsComponent.java index 40b0e69edc0..fa00a8e87d6 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorRaceChartSettingsComponent.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/charts/MultiCompetitorRaceChartSettingsComponent.java @@ -25,7 +25,7 @@ public class MultiCompetitorRaceChartSettingsComponent extends AbstractChartSett this.initialDetailType = settings.getDetailType(); availableDetailsTypes = new ArrayList(); - availableDetailsTypes.add(DetailType.WINDWARD_DISTANCE_TO_OVERALL_LEADER); + availableDetailsTypes.add(DetailType.WINDWARD_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD); availableDetailsTypes.add(DetailType.DISTANCE_TRAVELED); availableDetailsTypes.add(DetailType.DISTANCE_TRAVELED_INCLUDING_GATE_START); availableDetailsTypes.add(DetailType.VELOCITY_MADE_GOOD_IN_KNOTS); 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 3dae1691e7e..f3ad76391d5 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 @@ -315,6 +315,13 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe * the last {@link #timeChanged(Date, Date)} event. Therefore, the ranks listed here correspond to the {@link #timer}'s time. */ private LinkedHashMap quickRanks; + + /** + * Taken from {@link RaceMapDataDTO#competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber}; tells the + * windward distances traveled and the leg numbers in which the respective competitor is. + */ + private LinkedHashMap competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber; + private final CombinedWindPanel combinedWindPanel; @@ -504,7 +511,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe map.setControls(ControlPosition.LEFT_TOP, combinedWindPanel); combinedWindPanel.getParent().addStyleName("CombinedWindPanelParentDiv"); map.setControls(ControlPosition.LEFT_TOP, trueNorthIndicatorPanel); - trueNorthIndicatorPanel.getParent().addStyleName("CombinedWindPanelParentDiv"); + trueNorthIndicatorPanel.getParent().addStyleName("TrueNorthIndicatorPanelParentDiv"); RaceMap.this.raceMapImageManager.loadMapIcons(map); map.setSize("100%", "100%"); @@ -844,6 +851,8 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe public void onSuccess(RaceMapDataDTO raceMapDataDTO) { if (map != null && raceMapDataDTO != null) { quickRanks = raceMapDataDTO.quickRanks; + competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber = + raceMapDataDTO.competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber; if (showViewSimulation && settings.isShowSimulationOverlay()) { lastLegNumber = raceMapDataDTO.coursePositions.currentLegNumber; simulationOverlay.updateLeg(Math.max(lastLegNumber,1), /* clearCanvas */ false, raceMapDataDTO.simulationResultVersion); @@ -1153,15 +1162,16 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe /** * Returns a pair whose first component is the leg number (one-based) of the competitor returned as the second component. */ - private com.sap.sse.common.Util.Pair getLeadingVisibleCompetitorWithOneBasedLegNumber( + private com.sap.sse.common.Util.Pair getFarthestAheadVisibleCompetitorWithOneBasedLegNumber( Iterable competitorsToShow) { CompetitorDTO leadingCompetitorDTO = null; int legOfLeaderCompetitor = -1; // this only works because the quickRanks are sorted - for (QuickRankDTO quickRank : quickRanks.values()) { - if (Util.contains(competitorsToShow, quickRank.competitor)) { - leadingCompetitorDTO = quickRank.competitor; - legOfLeaderCompetitor = quickRank.legNumberOneBased; + for (Entry competitorsByWindwardDistanceTraveledAndOneBasedLegNumber : + competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber.entrySet()) { + if (Util.contains(competitorsToShow, competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getKey())) { + leadingCompetitorDTO = competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getKey(); + legOfLeaderCompetitor = competitorsByWindwardDistanceTraveledAndOneBasedLegNumber.getValue(); return new com.sap.sse.common.Util.Pair(legOfLeaderCompetitor, leadingCompetitorDTO); } } @@ -1173,7 +1183,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe boolean drawAdvantageLine = false; if (settings.getHelpLinesSettings().isVisible(HelpLineTypes.ADVANTAGELINE)) { // find competitor with highest rank - com.sap.sse.common.Util.Pair visibleLeaderInfo = getLeadingVisibleCompetitorWithOneBasedLegNumber(competitorsToShow); + com.sap.sse.common.Util.Pair visibleLeaderInfo = getFarthestAheadVisibleCompetitorWithOneBasedLegNumber(competitorsToShow); // the boat fix may be null; may mean that no positions were loaded yet for the leading visible boat; // don't show anything GPSFixDTO lastBoatFix = null; @@ -1374,7 +1384,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe private void showStartAndFinishLines(final CoursePositionsDTO courseDTO) { if (map != null && courseDTO != null && lastRaceTimesInfo != null) { - com.sap.sse.common.Util.Pair leadingVisibleCompetitorInfo = getLeadingVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow()); + com.sap.sse.common.Util.Pair leadingVisibleCompetitorInfo = getFarthestAheadVisibleCompetitorWithOneBasedLegNumber(getCompetitorsToShow()); int legOfLeadingCompetitor = leadingVisibleCompetitorInfo == null ? -1 : leadingVisibleCompetitorInfo.getA(); int numberOfLegs = lastRaceTimesInfo.legInfos.size(); // draw the start line diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java index d58c115ab70..b7221912f0e 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardPanel.java @@ -861,9 +861,9 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS return new DetailType[] { DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_DISTANCE_TRAVELED, DetailType.RACE_DISTANCE_TRAVELED_INCLUDING_GATE_START, - DetailType.RACE_TIME_TRAVELED, DetailType.RACE_CORRECTED_TIME_TRAVELED, - DetailType.RACE_CORRECTED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, - DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_DISTANCE_TO_LEADER_IN_METERS, + DetailType.RACE_TIME_TRAVELED, DetailType.RACE_CALCULATED_TIME_TRAVELED, + DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, + DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, DetailType.NUMBER_OF_MANEUVERS, DetailType.DISPLAY_LEGS, DetailType.CURRENT_LEG, DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, DetailType.RACE_AVERAGE_SIGNED_CROSS_TRACK_ERROR_IN_METERS, @@ -873,8 +873,7 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS DetailType.SPEED_OVER_GROUND_WHEN_PASSING_START, DetailType.DISTANCE_TO_STARBOARD_END_OF_STARTLINE_WHEN_PASSING_START_IN_METERS, DetailType.START_TACK, - DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL, - DetailType.RACE_CORRECTED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD}; + DetailType.RACE_RATIO_BETWEEN_TIME_SINCE_LAST_POSITION_FIX_AND_AVERAGE_SAMPLING_INTERVAL}; } public static DetailType[] getAvailableOverallDetailColumnTypes() { @@ -939,8 +938,8 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS result.put(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, new FormattedDoubleDetailTypeColumn(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, new CurrentSpeedOverGroundInKnots(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this)); - result.put(DetailType.RACE_DISTANCE_TO_LEADER_IN_METERS, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TO_LEADER_IN_METERS, new RaceDistanceToLeaderInMeters(), + result.put(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, new RaceDistanceToCompetitorFarthestAheadInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this)); result.put(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, new FormattedDoubleDetailTypeColumn(DetailType.RACE_AVERAGE_ABSOLUTE_CROSS_TRACK_ERROR_IN_METERS, new RaceAverageAbsoluteCrossTrackErrorInMeters(), @@ -978,12 +977,12 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS result.put(DetailType.RACE_TIME_TRAVELED, new TimeTraveledRaceColumn(getLeaderboardPanel(), this, stringMessages, LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LEG_DETAIL_COLUMN_HEADER_STYLE, LEG_DETAIL_COLUMN_STYLE)); - result.put(DetailType.RACE_CORRECTED_TIME_TRAVELED, - new FormattedDoubleDetailTypeColumn(DetailType.RACE_CORRECTED_TIME_TRAVELED, new RaceCorrectedTimeTraveledInSeconds(), + result.put(DetailType.RACE_CALCULATED_TIME_TRAVELED, + new FormattedDoubleDetailTypeColumn(DetailType.RACE_CALCULATED_TIME_TRAVELED, new RaceCalculatedTimeTraveledInSeconds(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this)); - result.put(DetailType.RACE_CORRECTED_TIME_TRAVELED, new FormattedDoubleDetailTypeColumn( - DetailType.RACE_CORRECTED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, - new RaceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds(), + result.put(DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, new FormattedDoubleDetailTypeColumn( + DetailType.RACE_CALCULATED_TIME_AT_ESTIMATED_ARRIVAL_AT_COMPETITOR_FARTHEST_AHEAD, + new RaceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this)); return result; } @@ -1280,26 +1279,26 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS } } - private class RaceCorrectedTimeTraveledInSeconds implements LegDetailField { + private class RaceCalculatedTimeTraveledInSeconds implements LegDetailField { @Override public Double get(LeaderboardRowDTO row) { Double result = null; LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); if (fieldsForRace != null) { - result = fieldsForRace.correctedTime == null ? null : fieldsForRace.correctedTime.asSeconds(); + result = fieldsForRace.calculatedTime == null ? null : fieldsForRace.calculatedTime.asSeconds(); } return result; } } - private class RaceCorrectedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds implements LegDetailField { + private class RaceCalculatedTimeAtEstimatedArrivalAtCompetitorFarthestAheadInSeconds implements LegDetailField { @Override public Double get(LeaderboardRowDTO row) { Double result = null; LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); if (fieldsForRace != null) { - result = fieldsForRace.correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead == null ? null - : fieldsForRace.correctedTimeAtEstimatedArrivalAtCompetitorFarthestAhead.asSeconds(); + result = fieldsForRace.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead == null ? null + : fieldsForRace.calculatedTimeAtEstimatedArrivalAtCompetitorFarthestAhead.asSeconds(); } return result; } @@ -1378,17 +1377,18 @@ public class LeaderboardPanel extends SimplePanel implements TimeListener, PlayS } /** - * Computes the windward distance to the overall leader in meters + * Computes the windward distance to the competitor farthest ahead in the race (not necessarily the leader with + * handicap scoring) in meters * * @author Axel Uhl (D043530) */ - private class RaceDistanceToLeaderInMeters implements LegDetailField { + private class RaceDistanceToCompetitorFarthestAheadInMeters implements LegDetailField { @Override public Double get(LeaderboardRowDTO row) { Double result = null; LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName()); - if (fieldsForRace != null && fieldsForRace.windwardDistanceToOverallLeaderInMeters != null) { - result = fieldsForRace.windwardDistanceToOverallLeaderInMeters; + if (fieldsForRace != null && fieldsForRace.windwardDistanceToCompetitorFarthestAheadInMeters != null) { + result = fieldsForRace.windwardDistanceToCompetitorFarthestAheadInMeters; } return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsFactory.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsFactory.java index f4036493976..e2d4275010b 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsFactory.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboard/LeaderboardSettingsFactory.java @@ -67,7 +67,7 @@ public class LeaderboardSettingsFactory { List raceDetails = new ArrayList(); raceDetails.add(DetailType.RACE_DISTANCE_TRAVELED); raceDetails.add(DetailType.RACE_AVERAGE_SPEED_OVER_GROUND_IN_KNOTS); - raceDetails.add(DetailType.RACE_DISTANCE_TO_LEADER_IN_METERS); + raceDetails.add(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS); raceDetails.add(DetailType.NUMBER_OF_MANEUVERS); raceDetails.add(DetailType.DISPLAY_LEGS); final List overallDetails = createOverallDetailsForShowRegattaRank(showRegattaRank); @@ -78,7 +78,7 @@ public class LeaderboardSettingsFactory { /* refresh interval */ null, /* name of race to sort */ nameOfRaceToSort, /* ascending */ true, /* updateUponPlayStateChange */ true, raceColumnSelection.getType(), /*showAddedScores*/ false, /*showOverallRacesCompleted*/ false, - /*showCompetitorSailIdColumns*/showCompetitorSailIdColumn, /*showCompetitorFullNameColumn*/showCompetitorNameColumn); + /*showCompetitorSailIdColumns*/showCompetitorSailIdColumn, /*showCompetitorFullNameColumn*/showCompetitorNameColumn); break; case Replay: settings = createNewDefaultSettings(namesOfRaceColumnsToShow, namesOfRacesToShow, nameOfRaceToSort, /* autoExpandFirstRace */ diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java index 2045cf771ea..a780595fd77 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/leaderboardedit/LeaderboardEditPage.java @@ -1,43 +1,58 @@ -package com.sap.sailing.gwt.ui.leaderboardedit; - -import java.util.List; - -import com.google.gwt.user.client.Window; -import com.google.gwt.user.client.rpc.AsyncCallback; -import com.google.gwt.user.client.ui.Label; -import com.google.gwt.user.client.ui.RootPanel; -import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; -import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel; -import com.sap.sse.gwt.client.async.AsyncActionsExecutor; -import com.sap.sse.gwt.client.async.MarkedAsyncCallback; - -public class LeaderboardEditPage extends AbstractSailingEntryPoint { - @Override - protected void doOnModuleLoad() { - super.doOnModuleLoad(); - - sailingService.getLeaderboardNames(new MarkedAsyncCallback>( - new AsyncCallback>() { - @Override - public void onSuccess(List leaderboardNames) { - String leaderboardName = Window.Location.getParameter("name"); - if (leaderboardNames.contains(leaderboardName)) { - LogoAndTitlePanel logoAndTitlePanel = new LogoAndTitlePanel(getStringMessages().editScores(), getStringMessages(), LeaderboardEditPage.this, getUserService()); - logoAndTitlePanel.addStyleName("LogoAndTitlePanel"); - EditableLeaderboardPanel leaderboardPanel = new EditableLeaderboardPanel(sailingService, new AsyncActionsExecutor(), leaderboardName, null, - LeaderboardEditPage.this, getStringMessages(), userAgent); - leaderboardPanel.ensureDebugId("EditableLeaderboardPanel"); - RootPanel.get().add(logoAndTitlePanel); - RootPanel.get().add(leaderboardPanel); - } else { - RootPanel.get().add(new Label(getStringMessages().noSuchLeaderboard())); - } - } - @Override - public void onFailure(Throwable t) { - reportError("Error trying to obtain list of leaderboard names: "+t.getMessage()); - } - })); - } - -} +package com.sap.sailing.gwt.ui.leaderboardedit; + +import java.util.List; + +import com.google.gwt.dom.client.Style.FontWeight; +import com.google.gwt.dom.client.Style.Position; +import com.google.gwt.dom.client.Style.Unit; +import com.google.gwt.user.client.Window; +import com.google.gwt.user.client.rpc.AsyncCallback; +import com.google.gwt.user.client.ui.Label; +import com.google.gwt.user.client.ui.RootPanel; +import com.google.gwt.user.client.ui.Widget; +import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint; +import com.sap.sse.gwt.client.async.AsyncActionsExecutor; +import com.sap.sse.gwt.client.async.MarkedAsyncCallback; +import com.sap.sse.gwt.theme.client.component.sapheader2.SAPHeader2; + +public class LeaderboardEditPage extends AbstractSailingEntryPoint { + @Override + protected void doOnModuleLoad() { + super.doOnModuleLoad(); + + sailingService.getLeaderboardNames(new MarkedAsyncCallback>( + new AsyncCallback>() { + @Override + public void onSuccess(List leaderboardNames) { + String leaderboardName = Window.Location.getParameter("name"); + if (leaderboardNames.contains(leaderboardName)) { + EditableLeaderboardPanel leaderboardPanel = new EditableLeaderboardPanel(sailingService, new AsyncActionsExecutor(), leaderboardName, null, + LeaderboardEditPage.this, getStringMessages(), userAgent); + leaderboardPanel.ensureDebugId("EditableLeaderboardPanel"); + RootPanel.get().add(initHeader()); + RootPanel.get().add(leaderboardPanel); + } else { + RootPanel.get().add(new Label(getStringMessages().noSuchLeaderboard())); + } + } + @Override + public void onFailure(Throwable t) { + reportError("Error trying to obtain list of leaderboard names: "+t.getMessage()); + } + })); + } + + private Widget initHeader() { + Label title = new Label(getStringMessages().editScores()); + title.getElement().getStyle().setColor("white"); + title.getElement().getStyle().setFontSize(20, Unit.PX); + title.getElement().getStyle().setFontWeight(FontWeight.BOLD); + title.getElement().getStyle().setMarginTop(16, Unit.PX); + SAPHeader2 header = new SAPHeader2(getStringMessages().sapSailingAnalytics(), title, false); + header.getElement().getStyle().setPosition(Position.FIXED); + header.getElement().getStyle().setTop(0, Unit.PX); + header.getElement().getStyle().setWidth(100, Unit.PCT); + header.getElement().getStyle().setZIndex(1337); + return header; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/QuickRanksLiveCache.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/QuickRanksLiveCache.java index f39269e5d8b..321ad0e4756 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/QuickRanksLiveCache.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/QuickRanksLiveCache.java @@ -4,7 +4,6 @@ import java.lang.ref.Reference; import java.lang.ref.ReferenceQueue; import java.lang.ref.WeakReference; import java.util.HashMap; -import java.util.List; import java.util.Map; import java.util.logging.Level; import java.util.logging.Logger; @@ -13,6 +12,7 @@ import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; import com.sap.sailing.gwt.ui.shared.QuickRankDTO; +import com.sap.sailing.gwt.ui.shared.QuickRanksDTO; import com.sap.sailing.server.masterdata.DummyTrackedRace; import com.sap.sse.util.SmartFutureCache; import com.sap.sse.util.SmartFutureCache.AbstractCacheUpdater; @@ -50,7 +50,7 @@ public class QuickRanksLiveCache { private final WeakReference stopRef = new WeakReference(dummyTrackedRace); - private final SmartFutureCache, CalculateOrPurge> cache; + private final SmartFutureCache cache; private final SailingServiceImpl service; @@ -72,19 +72,19 @@ public class QuickRanksLiveCache { public QuickRanksLiveCache(final SailingServiceImpl service) { this.service = service; - cache = new SmartFutureCache, CalculateOrPurge>( - new AbstractCacheUpdater, CalculateOrPurge>() { + cache = new SmartFutureCache( + new AbstractCacheUpdater() { @Override - public List computeCacheUpdate(RegattaAndRaceIdentifier key, + public QuickRanksDTO computeCacheUpdate(RegattaAndRaceIdentifier key, CalculateOrPurge updateInterval) throws Exception { logger.fine("Computing cache update for live QuickRanks of race "+key); - final List result; + final QuickRanksDTO quickRanks; if (updateInterval == CalculateOrPurge.PURGE) { - result = null; + quickRanks = null; } else { - result = service.computeQuickRanks(key, /* time point; null means live */ null); + quickRanks = service.computeQuickRanks(key, /* time point; null means live */ null); } - return result; + return quickRanks; } }, getClass().getName()); fromRefToRaceIdentifier = new HashMap<>(); @@ -118,8 +118,8 @@ public class QuickRanksLiveCache { dummyTrackedRace = null; // release the dummy tracked race, causing the stopRef to be enqueued } - public List get(RegattaAndRaceIdentifier raceIdentifier) { - List result = cache.get(raceIdentifier, false); + public QuickRanksDTO get(RegattaAndRaceIdentifier raceIdentifier) { + QuickRanksDTO result = cache.get(raceIdentifier, false); if (result == null) { TrackedRace trackedRace = service.getExistingTrackedRace(raceIdentifier); if (trackedRace != null) { 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 2e7994fdf09..ce2e70937bb 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 @@ -238,6 +238,7 @@ import com.sap.sailing.domain.racelogtracking.DeviceMapping; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingImpl; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.ranking.RankingMetricsFactory; import com.sap.sailing.domain.regattalike.HasRegattaLike; import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; @@ -303,6 +304,7 @@ import com.sap.sailing.gwt.ui.shared.MarkPassingTimesDTO; import com.sap.sailing.gwt.ui.shared.MarkpassingManeuverDTO; import com.sap.sailing.gwt.ui.shared.PathDTO; import com.sap.sailing.gwt.ui.shared.QuickRankDTO; +import com.sap.sailing.gwt.ui.shared.QuickRanksDTO; import com.sap.sailing.gwt.ui.shared.RaceCourseDTO; import com.sap.sailing.gwt.ui.shared.RaceGroupDTO; import com.sap.sailing.gwt.ui.shared.RaceGroupSeriesDTO; @@ -1570,7 +1572,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S fromPerCompetitorIdAsString, toPerCompetitorIdAsString, extrapolate); final CoursePositionsDTO coursePositions = getCoursePositions(raceIdentifier, date); final List courseSidelines = getCourseSidelines(raceIdentifier, date); - final List quickRanks = getQuickRanks(raceIdentifier, date); + final QuickRanksDTO quickRanks = getQuickRanks(raceIdentifier, date); int simulationResultVersion = 0; if (simulationLegIdentifier != null) { SimulationService simulationService = getService().getSimulationService(); @@ -2029,9 +2031,10 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S * null means "live" and is then replaced by "now" minus the tracked race's * {@link TrackedRace#getDelayToLiveInMillis() delay}. */ - public List computeQuickRanks(RegattaAndRaceIdentifier raceIdentifier, TimePoint timePoint) + public QuickRanksDTO computeQuickRanks(RegattaAndRaceIdentifier raceIdentifier, TimePoint timePoint) throws NoWindException { - List result = new ArrayList(); + final List result = new ArrayList<>(); + final List competitorDTOsInOrderOfWindwardDistanceTraveledFarthestFirst; TrackedRace trackedRace = getExistingTrackedRace(raceIdentifier); if (trackedRace != null) { final TimePoint actualTimePoint; @@ -2052,12 +2055,17 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S } rank++; } + final Iterable competitorsInOrderOfWindwardDistanceTraveledFarthestFirst = getService() + .getCompetitorInOrderOfWindwardDistanceTraveledFarthestFirst(trackedRace, actualTimePoint); + competitorDTOsInOrderOfWindwardDistanceTraveledFarthestFirst = convertToCompetitorDTOs(competitorsInOrderOfWindwardDistanceTraveledFarthestFirst); + } else { + competitorDTOsInOrderOfWindwardDistanceTraveledFarthestFirst = Collections.emptyList(); } - return result; + return new QuickRanksDTO(result, competitorDTOsInOrderOfWindwardDistanceTraveledFarthestFirst); } - private List getQuickRanks(RegattaAndRaceIdentifier raceIdentifier, Date date) throws NoWindException { - final List result; + private QuickRanksDTO getQuickRanks(RegattaAndRaceIdentifier raceIdentifier, Date date) throws NoWindException { + final QuickRanksDTO result; if (date == null) { result = quickRanksLiveCache.get(raceIdentifier); } else { @@ -2091,7 +2099,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Map windTrackInfoDTOs = new HashMap(); result.windTrackInfoByWindSource = windTrackInfoDTOs; - for(WindSource windSource: trackedRace.getWindSources()) { + for (WindSource windSource: trackedRace.getWindSources()) { windTrackInfoDTOs.put(windSource, new WindTrackInfoDTO()); } windTrackInfoDTOs.put(new WindSourceImpl(WindSourceType.COMBINED), new WindTrackInfoDTO()); @@ -2720,13 +2728,15 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S break; case GAP_TO_LEADER_IN_SECONDS: if (trackedLeg != null) { - final Duration gapToLeaderInOwnTime = trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric().getGapToLeaderInOwnTime(competitor, timePoint, cache); + final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint); + final Duration gapToLeaderInOwnTime = trackedLeg.getTrackedLeg().getTrackedRace().getRankingMetric().getGapToLeaderInOwnTime(rankingInfo, competitor, cache); result = gapToLeaderInOwnTime == null ? null : gapToLeaderInOwnTime.asSeconds(); } break; - case WINDWARD_DISTANCE_TO_OVERALL_LEADER: + case WINDWARD_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD: if (trackedLeg != null) { - Distance distanceToLeader = trackedLeg.getWindwardDistanceToOverallLeader(timePoint, WindPositionMode.LEG_MIDDLE); + final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint); + Distance distanceToLeader = trackedLeg.getWindwardDistanceToCompetitorFarthestAhead(timePoint, WindPositionMode.LEG_MIDDLE, rankingInfo); result = (distanceToLeader == null) ? null : distanceToLeader.getMeters(); } break; @@ -4772,9 +4782,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S for (Regatta regatta : allRegattas) { DynamicTrackedRegatta trackedRegatta = getService().getTrackedRegatta(regatta); if (trackedRegatta != null) { - Iterable trackedRaces = trackedRegatta.getTrackedRaces(); - for (TrackedRace trackedRace : trackedRaces) { - result.add((DynamicTrackedRace) trackedRace); + trackedRegatta.lockTrackedRacesForRead(); + try { + Iterable trackedRaces = trackedRegatta.getTrackedRaces(); + for (TrackedRace trackedRace : trackedRaces) { + result.add((DynamicTrackedRace) trackedRace); + } + } finally { + trackedRegatta.unlockTrackedRacesAfterRead(); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/dispatch/DispatchRPCImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/dispatch/DispatchRPCImpl.java index 97c051d69fa..3514b566ec4 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/dispatch/DispatchRPCImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/dispatch/DispatchRPCImpl.java @@ -28,7 +28,7 @@ public class DispatchRPCImpl extends ProxiedRemoteServiceServlet implements Disp @Override public > ResultWrapper execute(RequestWrapper request) throws DispatchException { - R executionResult = request.getAction().execute(new DispatchContextImpl(request.getCurrentClientTime(), racingEventServiceTracker.getService())); + R executionResult = request.getAction().execute(new DispatchContextImpl(request.getCurrentClientTime(), racingEventServiceTracker.getService(), request.getClientLocaleName())); return new ResultWrapper(executionResult); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java index 5d70be2d170..6b03faa0e68 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java @@ -5,10 +5,12 @@ import java.util.HashMap; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Map.Entry; import com.google.gwt.user.client.rpc.IsSerializable; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.gwt.ui.client.SailingServiceAsync; +import com.sap.sse.common.Util; /** * A compact representation of a {@link RaceMapDataDTO} that represents competitors by their ID only instead of a @@ -25,21 +27,30 @@ public class CompactRaceMapDataDTO implements IsSerializable { private CoursePositionsDTO coursePositions; private List courseSidelines; private List quickRanks; + private LinkedHashMap competitorsInOrderOfWindwardDistanceTraveledFarthestFirstIdAsStringAndOneBasedLegNumber; private int simulationResultVersion; CompactRaceMapDataDTO() {} public CompactRaceMapDataDTO(Map> boatPositions, CoursePositionsDTO coursePositions, - List courseSidelines, List quickRanks, int simulationResultVersion) { + List courseSidelines, QuickRanksDTO quickRanks, int simulationResultVersion) { this.boatPositionsByCompetitorIdAsString = new HashMap>(); for (Map.Entry> e : boatPositions.entrySet()) { this.boatPositionsByCompetitorIdAsString.put(e.getKey().getIdAsString(), e.getValue()); } - this.quickRanks = new ArrayList(quickRanks == null ? 0 : quickRanks.size()); + this.quickRanks = new ArrayList(quickRanks == null ? 0 : Util.size(quickRanks.getQuickRanks())); + this.competitorsInOrderOfWindwardDistanceTraveledFarthestFirstIdAsStringAndOneBasedLegNumber = new LinkedHashMap<>(); + Map competitorIdAsStringToOneBasedLegNumber = new HashMap<>(); if (quickRanks != null) { - for (QuickRankDTO quickRank : quickRanks) { + for (QuickRankDTO quickRank : quickRanks.getQuickRanks()) { this.quickRanks.add(new CompactQuickRankDTO(quickRank.competitor.getIdAsString(), quickRank.rank, quickRank.legNumberOneBased)); + competitorIdAsStringToOneBasedLegNumber.put(quickRank.competitor.getIdAsString(), quickRank.legNumberOneBased); + } + for (CompetitorDTO competitorInOrderOfWindwardDistanceTraveled : quickRanks.getCompetitorsInOrderOfWindwardDistanceTraveledFarthestFirst()) { + this.competitorsInOrderOfWindwardDistanceTraveledFarthestFirstIdAsStringAndOneBasedLegNumber.put( + competitorInOrderOfWindwardDistanceTraveled.getIdAsString(), + competitorIdAsStringToOneBasedLegNumber.get(competitorInOrderOfWindwardDistanceTraveled.getIdAsString())); } } this.courseSidelines = courseSidelines; @@ -70,6 +81,13 @@ public class CompactRaceMapDataDTO implements IsSerializable { result.boatPositions.put(competitor, e.getValue()); } } + result.competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber = new LinkedHashMap<>(); + for (Entry i : competitorsInOrderOfWindwardDistanceTraveledFarthestFirstIdAsStringAndOneBasedLegNumber.entrySet()) { + CompetitorDTO c = competitorsByIdAsString.get(i.getKey()); + if (c != null) { + result.competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber.put(c, i.getValue()); + } + } result.simulationResultVersion = simulationResultVersion; return result; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java index 34f2f065980..cd88ab3025c 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/EventBaseDTO.java @@ -8,8 +8,8 @@ import java.util.Map; import java.util.UUID; import com.google.gwt.user.client.rpc.IsSerializable; -import com.sap.sailing.domain.common.ImageSize; import com.sap.sailing.domain.common.dto.NamedDTO; +import com.sap.sse.common.media.ImageSize; public class EventBaseDTO extends NamedDTO implements IsSerializable { private static final long serialVersionUID = 818666323178097939L; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/QuickRanksDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/QuickRanksDTO.java new file mode 100755 index 00000000000..bff0c8c32d5 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/QuickRanksDTO.java @@ -0,0 +1,39 @@ +package com.sap.sailing.gwt.ui.shared; + +import com.sap.sailing.domain.common.dto.CompetitorDTO; + +/** + * Combines a list of {@link QuickRankDTO} objects telling about the (calculated, according to any handicap ranking metric + * in place)) ranks of the competitors with an information about the boat farthest ahead in the race. The latter is useful, + * e.g., to draw an advantage line on this boat leading the race without considering any handicap ranking metric. + * + * @author Axel Uhl (D043530) + * + */ +public class QuickRanksDTO { + /** + * Lists the competitors in the order of estimated calculated time when reaching the competitor farthest ahead. + * This is the prediction for the ultimate calculated ranking. + */ + private final Iterable quickRanksAfterCalculatedTime; + + /** + * Lists the competitors according to their windward distance traveled. The first boat (rank 1) is the + * competitor farthest ahead. + */ + private final Iterable competitorsInOrderOfWindwardDistanceTraveledFarthestFirst; + + public QuickRanksDTO(Iterable quickRanks, Iterable competitorsInOrderOfWindwardDistanceTraveledFarthestFirst) { + super(); + this.quickRanksAfterCalculatedTime = quickRanks; + this.competitorsInOrderOfWindwardDistanceTraveledFarthestFirst = competitorsInOrderOfWindwardDistanceTraveledFarthestFirst; + } + + public Iterable getQuickRanks() { + return quickRanksAfterCalculatedTime; + } + + public Iterable getCompetitorsInOrderOfWindwardDistanceTraveledFarthestFirst() { + return competitorsInOrderOfWindwardDistanceTraveledFarthestFirst; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceMapDataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceMapDataDTO.java index 99b1c143aec..78c702ebf85 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceMapDataDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/RaceMapDataDTO.java @@ -12,5 +12,6 @@ public class RaceMapDataDTO implements IsSerializable { public CoursePositionsDTO coursePositions; public List courseSidelines; public LinkedHashMap quickRanks; + public LinkedHashMap competitorsInOrderOfWindwardDistanceTraveledWithOneBasedLegNumber; public int simulationResultVersion; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/DispatchContext.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/DispatchContext.java index d6bb1a2b309..8ea041e4819 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/DispatchContext.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/DispatchContext.java @@ -1,9 +1,18 @@ package com.sap.sailing.gwt.ui.shared.dispatch; -import com.google.gwt.core.shared.GwtIncompatible; +import java.util.Date; +import java.util.Locale; + import com.sap.sailing.server.RacingEventService; -@GwtIncompatible + public interface DispatchContext { + RacingEventService getRacingEventService(); + + String getClientLocaleName(); + + Locale getClientLocale(); + + Date getCurrentClientTime(); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/DispatchContextImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/DispatchContextImpl.java index 6da87d5a722..07bdf1c75dc 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/DispatchContextImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/DispatchContextImpl.java @@ -1,6 +1,7 @@ package com.sap.sailing.gwt.ui.shared.dispatch; import java.util.Date; +import java.util.Locale; import com.google.gwt.core.shared.GwtIncompatible; import com.sap.sailing.server.RacingEventService; @@ -9,11 +10,13 @@ import com.sap.sailing.server.RacingEventService; public class DispatchContextImpl implements DispatchContext { private final RacingEventService racingEventService; private final Date currentClientTime; - private final Date currentServerTime = new Date(); +// private final Date currentServerTime = new Date(); + private String clientLocaleName; - public DispatchContextImpl(Date currentClientTime, RacingEventService racingEventService) { + public DispatchContextImpl(Date currentClientTime, RacingEventService racingEventService, String clientLocaleName) { this.currentClientTime = currentClientTime; this.racingEventService = racingEventService; + this.clientLocaleName = clientLocaleName; } @Override @@ -21,11 +24,22 @@ public class DispatchContextImpl implements DispatchContext { return racingEventService; } + @Override public Date getCurrentClientTime() { return currentClientTime; } - public Date getCurrentServerTime() { - return currentServerTime; +// public Date getCurrentServerTime() { +// return currentServerTime; +// } + + @Override + public String getClientLocaleName() { + return clientLocaleName; + } + + @Override + public Locale getClientLocale() { + return Locale.forLanguageTag(clientLocaleName); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/RequestWrapper.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/RequestWrapper.java index 3cfec4c936f..0a7be0417ff 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/RequestWrapper.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/RequestWrapper.java @@ -8,15 +8,18 @@ public class RequestWrapper> implements Is private Date currentClientTime = new Date(); + private String clientLocaleName; + private A action; @SuppressWarnings("unused") private RequestWrapper() { } - public RequestWrapper(A action) { + public RequestWrapper(A action, String clientLocaleName) { super(); this.action = action; + this.clientLocaleName = clientLocaleName; } public A getAction() { @@ -26,4 +29,8 @@ public class RequestWrapper> implements Is public Date getCurrentClientTime() { return currentClientTime; } + + public String getClientLocaleName() { + return clientLocaleName; + } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/EventOverviewVideoStageDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/EventOverviewVideoStageDTO.java index 3b44f820004..c1f607ed632 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/EventOverviewVideoStageDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/EventOverviewVideoStageDTO.java @@ -1,6 +1,6 @@ package com.sap.sailing.gwt.ui.shared.dispatch.event; -import com.sap.sailing.domain.common.media.MimeType; +import com.sap.sse.common.media.MimeType; public class EventOverviewVideoStageDTO implements EventOverviewStageContentDTO { public enum Type { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetEventOverviewStageAction.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetEventOverviewStageAction.java index 8591ce2195e..d1009001f1d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetEventOverviewStageAction.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/dispatch/event/GetEventOverviewStageAction.java @@ -5,7 +5,6 @@ import java.util.UUID; import com.google.gwt.core.shared.GwtIncompatible; import com.sap.sailing.domain.base.Event; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.gwt.server.HomeServiceUtil; import com.sap.sailing.gwt.ui.shared.dispatch.Action; import com.sap.sailing.gwt.ui.shared.dispatch.DispatchContext; @@ -14,6 +13,7 @@ import com.sap.sailing.gwt.ui.shared.general.EventState; import com.sap.sailing.gwt.ui.shared.media.MediaUtils; import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MimeType; public class GetEventOverviewStageAction implements Action> { private UUID eventId; @@ -52,6 +52,7 @@ public class GetEventOverviewStageAction implements Action videoURLs = event.getVideoURLs(); for (int i = Util.size(videoURLs) - 1; i >= 0; i--) { + // TODO fmittag/pgt: implement locale specific live stream using context.getClientLocaleName() or context.getClientLocale() String videoUrl = Util.get(videoURLs, i).toString(); MimeType type = MediaUtils.detectMimeTypeFromUrl(videoUrl); if (type == MimeType.youtube || type == MimeType.vimeo || type == MimeType.mp4) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/AbstractMediaDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/AbstractMediaDTO.java deleted file mode 100644 index 5123b305423..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/AbstractMediaDTO.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.sap.sailing.gwt.ui.shared.media; - -import java.io.Serializable; -import java.net.URL; -import java.util.HashSet; - -import com.google.gwt.core.shared.GwtIncompatible; -import com.sap.sailing.domain.common.media.MediaCategory; -import com.sap.sailing.domain.common.media.MimeType; -import com.sap.sailing.gwt.ui.shared.general.EventReferenceDTO; - -public class AbstractMediaDTO implements Serializable { - private static final long serialVersionUID = 1L; - - private String title; - - private MimeType mimeType; - - private MediaCategory category; - - private HashSet tags = new HashSet(); - - private EventReferenceDTO eventRef; - - private String sourceRef; - - - protected AbstractMediaDTO() { - } - - @GwtIncompatible - public AbstractMediaDTO(EventReferenceDTO eventRef, URL url, MimeType mimeType, String title) { - this(eventRef, url.toString(), mimeType, title); - } - - @GwtIncompatible - public AbstractMediaDTO(EventReferenceDTO eventRef, String sourceRef, MimeType mimeType, String title) { - this.eventRef = eventRef; - this.title = title; - this.mimeType = mimeType; - this.sourceRef = sourceRef; - } - - public String getTitle() { - return title; - } - - public MimeType getMimeType() { - return mimeType; - } - - public MediaCategory getCategory() { - return category; - } - - public HashSet getTags() { - return tags; - } - - public String getSourceRef() { - return sourceRef; - } - - public void setCategory(MediaCategory category) { - this.category = category; - } - - public EventReferenceDTO getEventRef() { - return eventRef; - } - -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/ImageMetadataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/ImageMetadataDTO.java deleted file mode 100644 index cd1ae3a5071..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/ImageMetadataDTO.java +++ /dev/null @@ -1,72 +0,0 @@ -package com.sap.sailing.gwt.ui.shared.media; - -import java.net.URL; -import java.util.Date; - -import com.google.gwt.core.shared.GwtIncompatible; -import com.sap.sailing.domain.common.ImageSize; -import com.sap.sailing.domain.common.media.MimeType; -import com.sap.sailing.gwt.ui.shared.general.EventReferenceDTO; - -public class ImageMetadataDTO extends AbstractMediaDTO { - - private static final long serialVersionUID = 1L; - - private int widthInPx; - private int heightInPx; - - private String subtitle; - private Date createdAtDate; - private String author; - - protected ImageMetadataDTO() { - } - - @GwtIncompatible - public ImageMetadataDTO(EventReferenceDTO eventRef, URL imageURL, ImageSize size, String title) { - super(eventRef, imageURL.toString(), MimeType.image, title); - this.widthInPx = size.getWidth(); - this.heightInPx = size.getHeight(); - } - - public String getSubtitle() { - return subtitle; - } - - public void setSubtitle(String subtitle) { - this.subtitle = subtitle; - } - - public Date getCreatedAtDate() { - return createdAtDate; - } - - public void setCreatedAtDate(Date createdAtDate) { - this.createdAtDate = createdAtDate; - } - - public String getAuthor() { - return author; - } - - public void setAuthor(String author) { - this.author = author; - } - - public int getWidthInPx() { - return widthInPx; - } - - public void setWidthInPx(int widthInPx) { - this.widthInPx = widthInPx; - } - - public int getHeightInPx() { - return heightInPx; - } - - public void setHeightInPx(int heightInPx) { - this.heightInPx = heightInPx; - } - -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaDTO.java index 702a0985ecc..cb4b30dac40 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaDTO.java @@ -6,22 +6,22 @@ import java.util.ArrayList; public class MediaDTO implements Serializable { private static final long serialVersionUID = 4932700214423641309L; - private ArrayList photos = new ArrayList<>(); - private ArrayList videos = new ArrayList<>(); + private ArrayList photos = new ArrayList<>(); + private ArrayList videos = new ArrayList<>(); - public ArrayList getPhotos() { + public ArrayList getPhotos() { return photos; } - public void addPhoto(ImageMetadataDTO photo) { + public void addPhoto(SailingImageDTO photo) { photos.add(photo); } - public ArrayList getVideos() { + public ArrayList getVideos() { return videos; } - public void addVideo(VideoMetadataDTO video) { + public void addVideo(SailingVideoDTO video) { videos.add(video); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaUtils.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaUtils.java index d017473629d..e61f0c0eb93 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaUtils.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/MediaUtils.java @@ -3,7 +3,7 @@ package com.sap.sailing.gwt.ui.shared.media; import java.util.regex.Pattern; import com.google.gwt.core.shared.GwtIncompatible; -import com.sap.sailing.domain.common.media.MimeType; +import com.sap.sse.common.media.MimeType; /** * Temporary solution for mimetype detection for current videos. diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/SailingImageDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/SailingImageDTO.java new file mode 100644 index 00000000000..02da494bb86 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/SailingImageDTO.java @@ -0,0 +1,28 @@ +package com.sap.sailing.gwt.ui.shared.media; + +import java.util.Date; + +import com.sap.sailing.gwt.ui.shared.general.EventReferenceDTO; +import com.sap.sse.common.media.ImageMetadataDTO; +import com.sap.sse.common.media.ImageSize; + +public class SailingImageDTO extends ImageMetadataDTO { + + private static final long serialVersionUID = 1L; + + private EventReferenceDTO eventRef; + + @SuppressWarnings("unused") + private SailingImageDTO() { + } + + public SailingImageDTO(EventReferenceDTO eventRef, String imageURL, ImageSize size, Date createdAtDate) { + super(imageURL, size, createdAtDate); + this.eventRef = eventRef; + + } + + public EventReferenceDTO getEventRef() { + return eventRef; + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/SailingVideoDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/SailingVideoDTO.java new file mode 100644 index 00000000000..bbfc2ebe186 --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/SailingVideoDTO.java @@ -0,0 +1,29 @@ +package com.sap.sailing.gwt.ui.shared.media; + +import java.util.Date; + +import com.sap.sailing.gwt.ui.shared.general.EventReferenceDTO; +import com.sap.sse.common.media.MimeType; +import com.sap.sse.common.media.VideoMetadataDTO; + +public class SailingVideoDTO extends VideoMetadataDTO { + + private static final long serialVersionUID = 1L; + + private EventReferenceDTO eventRef; + + @SuppressWarnings("unused") + private SailingVideoDTO() { + } + + public SailingVideoDTO(EventReferenceDTO eventRef, String srcReference, MimeType mimeType, Date createdAtDate) { + super(srcReference, mimeType, createdAtDate); + this.eventRef = eventRef; + } + + + public EventReferenceDTO getEventRef() { + return eventRef; + } + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/VideoMetadataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/VideoMetadataDTO.java deleted file mode 100644 index 7545cfb79e8..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/media/VideoMetadataDTO.java +++ /dev/null @@ -1,45 +0,0 @@ -package com.sap.sailing.gwt.ui.shared.media; - -import java.net.URL; - -import com.google.gwt.core.shared.GwtIncompatible; -import com.sap.sailing.domain.common.media.MimeType; -import com.sap.sailing.gwt.ui.shared.general.EventReferenceDTO; - -public class VideoMetadataDTO extends AbstractMediaDTO { - - private static final long serialVersionUID = 1L; - - private int length; - private String thumbnailRef; - - @SuppressWarnings("unused") - private VideoMetadataDTO() { - } - - @GwtIncompatible - public VideoMetadataDTO(EventReferenceDTO eventRef, URL url, MimeType mimeType, String title) { - this(eventRef, url.toString(), mimeType, title); - } - - @GwtIncompatible - public VideoMetadataDTO(EventReferenceDTO eventRef, String url, MimeType mimeType, String title) { - super(eventRef, url, mimeType, title); - } - - public void setLength(int length) { - this.length = length; - } - - public int getLength() { - return length; - } - - public void setThumbnailRef(String thumbnailRef) { - this.thumbnailRef = thumbnailRef; - } - - public String getThumbnailRef() { - return thumbnailRef; - } -} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java index 8bbec2b38fb..4708aec29fa 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/racemap/RaceSimulationOverlay.java @@ -41,7 +41,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { public static final String GET_SIMULATION_CATEGORY = "getSimulation"; private final String textColor = "Black"; - private final String textFont = "10pt OpenSansRegular"; + private final String textFont = "10pt 'Open Sans'"; private String algorithmTimedOutText = "out-of-bounds"; private String mixedLegText = "ambiguous wind"; private int xOffset = 0; @@ -115,7 +115,7 @@ public class RaceSimulationOverlay extends FullCanvasOverlay { new SettingsDialog(component, stringMessages).show(); } });*/ - map.setControls(ControlPosition.LEFT_TOP, simulationLegend); + map.setControls(ControlPosition.RIGHT_BOTTOM, simulationLegend); simulationLegend.getParent().addStyleName("MapSimulationLegendParentDiv"); } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/start/StartViewDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/start/StartViewDTO.java index bf20c850c26..fb2d448a9ae 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/start/StartViewDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/start/StartViewDTO.java @@ -7,8 +7,8 @@ import java.util.List; import com.google.gwt.user.client.rpc.IsSerializable; import com.sap.sailing.gwt.ui.shared.eventlist.EventListEventDTO; -import com.sap.sailing.gwt.ui.shared.media.ImageMetadataDTO; -import com.sap.sailing.gwt.ui.shared.media.VideoMetadataDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingImageDTO; +import com.sap.sailing.gwt.ui.shared.media.SailingVideoDTO; public class StartViewDTO implements IsSerializable { @@ -16,8 +16,8 @@ public class StartViewDTO implements IsSerializable { private ArrayList recentEvents = new ArrayList(); - private ArrayList photos = new ArrayList<>(); - private HashSet videos = new HashSet<>(); + private ArrayList photos = new ArrayList<>(); + private HashSet videos = new HashSet<>(); public List getStageEvents() { return stageEvents; @@ -35,19 +35,19 @@ public class StartViewDTO implements IsSerializable { this.recentEvents.add(event); } - public ArrayList getPhotos() { + public ArrayList getPhotos() { return photos; } - public void addPhoto(ImageMetadataDTO photo) { + public void addPhoto(SailingImageDTO photo) { photos.add(photo); } - public Collection getVideos() { + public Collection getVideos() { return videos; } - public void addVideo(VideoMetadataDTO video) { + public void addVideo(SailingVideoDTO video) { videos.add(video); } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java index 39c00a6c558..43b0aa7834d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/PathLegendCanvasOverlay.java @@ -39,7 +39,7 @@ public class PathLegendCanvasOverlay extends FullCanvasOverlay { private double rectHeight = 20; private String textColor = "Black"; - private String textFont = "10pt OpenSansRegular"; + private String textFont = "10pt 'Open Sans'"; private String algorithmTimedOutText = "out-of-bounds"; private String mixedLegText = "ambiguous wind"; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/util/ColorPaletteGenerator.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/util/ColorPaletteGenerator.java index f2c85b4dfd6..935a9582eed 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/util/ColorPaletteGenerator.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/simulator/util/ColorPaletteGenerator.java @@ -3,7 +3,7 @@ package com.sap.sailing.gwt.ui.simulator.util; public class ColorPaletteGenerator implements ColorPalette { //private String[] colors = {"Green","Red","Blue","Yellow","Black"}; - private String[] colors = {"#00ED00","#FFBB00","#FFEA00","#0bf","#0bf","#808080","Blue","Blue","Yellow","Black"}; + private String[] colors = {"#00ED00","#FFBB00","#FFEA00","#0bf","#00e6ff","#808080","Blue","Blue","Yellow","Black"}; private int currentIndex = -1; public ColorPaletteGenerator() { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupFullTable.css b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupFullTable.css index f3ba5599048..98e016904c3 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupFullTable.css +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/spectator/LeaderboardGroupFullTable.css @@ -1,61 +1,62 @@ -@CHARSET "ISO-8859-1"; - -.cellTableWidget { - margin: 0; - table-layout: auto; - border-collapse: collapse; - /*border-spacing: 1em 0.5em;*/ - border: none; -} - -.cellTableHeader { - background: url(images/spectator-table-bg.png) 0 0 repeat-x !important; - - font-family: 'OpenSansSemiBold', Arial, Verdana, sans-serif; - padding: 10px 15px; - border: 1px solid #aeaeaf !important; - border-left: none !important; - border-right: none !important; - text-shadow: none !important; -} - -.cellTableFirstColumnHeader { - -} - -.cellTableLastColumnHeader { - -} - -.cellTableEvenRow { /* tr */ - background: #dddbdb; -} - -.cellTableEvenRowCell { /* td */ - border: none; - background: none; - vertical-align: top; - padding: 2px 3px; -} - -.cellTableOddRow { /* tr */ - background: none; -} - -.cellTableOddRowCell { /* td */ - border: none; - background: none; - vertical-align: top; - padding: 2px 3px; -} - -.cellTableHoveredRow { - -} - -.cellTableSelectedRow { -} - -.cellTableSelectedRowCell { - -} +@CHARSET "ISO-8859-1"; + +.cellTableWidget { + margin: 0; + table-layout: auto; + border-collapse: collapse; + /*border-spacing: 1em 0.5em;*/ + border: none; +} + +.cellTableHeader { + background: url(images/spectator-table-bg.png) 0 0 repeat-x !important; + + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: 600; + padding: 10px 15px; + border: 1px solid #aeaeaf !important; + border-left: none !important; + border-right: none !important; + text-shadow: none !important; +} + +.cellTableFirstColumnHeader { + +} + +.cellTableLastColumnHeader { + +} + +.cellTableEvenRow { /* tr */ + background: #dddbdb; +} + +.cellTableEvenRowCell { /* td */ + border: none; + background: none; + vertical-align: top; + padding: 2px 3px; +} + +.cellTableOddRow { /* tr */ + background: none; +} + +.cellTableOddRowCell { /* td */ + border: none; + background: none; + vertical-align: top; + padding: 2px 3px; +} + +.cellTableHoveredRow { + +} + +.cellTableSelectedRow { +} + +.cellTableSelectedRowCell { + +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/main.gss b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/main.gss index 076037ca0cb..e09d28752bc 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/main.gss +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/main.gss @@ -129,6 +129,14 @@ figcaption { outline: none; text-decoration: none; } +.buttontextonly { + color: #333; + background: none; + -webkit-transition: none; + -moz-transition: none; + transition: none; + font-weight: normal; +} .buttonsmall { visibility: inherit; } @@ -338,8 +346,7 @@ figcaption { transform: translate(-50%, -50%); } .mainsection { - padding-top: 5.333333333333333em; - padding-bottom: 5.333333333333333em; + padding: 5.333333333333333em 0; } .mainsection_header { text-align: center; @@ -400,6 +407,61 @@ figcaption { cursor: pointer; z-index: 2; } +.spacer { + width: 100%; + border: 0; + margin: 0; + padding: 0; + display: block; +} +.spacersmall { + height: 1em; +} +.spacermediumsmall { + height: 1.333333333333333em; +} +.spacermedium { + height: 2em; +} +.spacermediumbig { + height: 4em; +} +.spacerbig { + height: 5.333333333333333em; +} +.spacermargin { + font-size: inherit; +} +.spacermargintopsmall { + margin-top: 1em; +} +.spacermargintopmediumsmall { + margin-top: 1.333333333333333em; +} +.spacermargintopmedium { + margin-top: 2em; +} +.spacermargintopmediumbig { + margin-top: 4em; +} +.spacermargintopbig { + margin-top: 5.333333333333333em; +} +.spacermarginbottomsmall { + margin-bottom: 1em; +} +.spacermarginbottommediumsmall { + margin-bottom: 1.333333333333333em; +} +.spacermarginbottommedium { + margin-bottom: 2em; +} +.spacermarginbottommediumbig { + margin-bottom: 4em; +} +.spacermarginbottombig { + margin-bottom: 5.333333333333333em; +} .stickyheader { background: inherit; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/LeaderboardEditing.gwt.xml b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/LeaderboardEditing.gwt.xml index 843d73a389f..b84aa6731de 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/LeaderboardEditing.gwt.xml +++ b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/ui/LeaderboardEditing.gwt.xml @@ -9,6 +9,7 @@ + diff --git a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/MediaDBTest.java b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/MediaDBTest.java index fbb932a9baa..2bf60fa11ab 100644 --- a/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/MediaDBTest.java +++ b/java/com.sap.sailing.media.persistence.test/src/com/sap/sailing/media/persistence/test/MediaDBTest.java @@ -13,13 +13,13 @@ import com.mongodb.MongoException; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.domain.persistence.media.MediaDB; import com.sap.sailing.domain.persistence.media.MediaDBFactory; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MimeType; import static org.junit.Assert.*; import static org.hamcrest.Matchers.*; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java index 89f69beab4c..6913ca6b081 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/impl/PolarDataServiceImpl.java @@ -10,6 +10,7 @@ import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.SpeedWithConfidence; +import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.ManeuverType; @@ -20,6 +21,7 @@ import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.confidence.impl.BearingWithConfidenceImpl; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; +import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; @@ -60,9 +62,20 @@ public class PolarDataServiceImpl implements PolarDataService { } @Override - public SpeedWithBearingWithConfidence getAverageSpeedWithBearing(BoatClass boatClass, - Speed windSpeed, LegType legType, Tack tack, boolean useRegressionForSpeed) throws NotEnoughDataHasBeenAddedException { - return polarDataMiner.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, tack, useRegressionForSpeed); + public SpeedWithBearingWithConfidence getAverageSpeedWithBearing(BoatClass boatClass, Speed windSpeed, + LegType legType, Tack tack, boolean useRegressionForSpeed) throws NotEnoughDataHasBeenAddedException { + SpeedWithBearingWithConfidence averageSpeedAndCourseOverGround = polarDataMiner + .getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, useRegressionForSpeed); + if (tack == Tack.PORT) { + //Negative twa + DegreeBearingImpl bearing = new DegreeBearingImpl(-averageSpeedAndCourseOverGround.getObject().getBearing() + .getDegrees()); + KnotSpeedWithBearingImpl speed = new KnotSpeedWithBearingImpl(averageSpeedAndCourseOverGround.getObject() + .getKnots(), bearing); + averageSpeedAndCourseOverGround = new SpeedWithBearingWithConfidenceImpl(speed, + averageSpeedAndCourseOverGround.getConfidence(), null); + } + return averageSpeedAndCourseOverGround; } @@ -139,21 +152,21 @@ public class PolarDataServiceImpl implements PolarDataService { } @Override - public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) + public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException { - return polarDataMiner.getSpeedRegressionFunction(boatClass, legType, tack); + return polarDataMiner.getSpeedRegressionFunction(boatClass, legType); } @Override - public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) + public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException { - return polarDataMiner.getAngleRegressionFunction(boatClass, legType, tack); + return polarDataMiner.getAngleRegressionFunction(boatClass, legType); } @Override public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, double trueWindAngle) throws NotEnoughDataHasBeenAddedException { - return polarDataMiner.getSpeedRegressionFunction(boatClass, trueWindAngle); + return polarDataMiner.getSpeedRegressionFunction(boatClass, Math.abs(trueWindAngle)); } @Override @@ -171,14 +184,9 @@ public class PolarDataServiceImpl implements PolarDataService { if (boatClass == null || windSpeed == null) { throw new IllegalArgumentException("Boatclass and windspeed cannot be null."); } - SpeedWithBearingWithConfidence port = getAverageSpeedWithBearing(boatClass, windSpeed, legType, Tack.PORT); - SpeedWithBearingWithConfidence starboard = getAverageSpeedWithBearing(boatClass, windSpeed, legType, - Tack.STARBOARD); - double angleSumInDeg = Math.abs(port.getObject().getBearing().getDegrees()) - + Math.abs(starboard.getObject().getBearing().getDegrees()); - Bearing bearing = new DegreeBearingImpl(angleSumInDeg); - double confidence = (port.getConfidence() + starboard.getConfidence()) / 2; - BearingWithConfidence bearingWithConfidence = new BearingWithConfidenceImpl(bearing, confidence, + SpeedWithBearingWithConfidence speed = polarDataMiner.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, true); + Bearing bearing = new DegreeBearingImpl(speed.getObject().getBearing().getDegrees() * 2); + BearingWithConfidence bearingWithConfidence = new BearingWithConfidenceImpl(bearing, speed.getConfidence(), null); return bearingWithConfidence; } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java index 9f7fff45ac9..d21684e62de 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/AngleAndSpeedRegression.java @@ -8,9 +8,11 @@ import org.apache.commons.math.analysis.polynomials.PolynomialFunction; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.impl.SpeedWithBearingWithConfidenceImpl; import com.sap.sailing.domain.common.Bearing; +import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.Position; 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.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; @@ -30,7 +32,7 @@ public class AngleAndSpeedRegression { private double maxWindSpeedInKnots = -1; public void addData(WindWithConfidence> windSpeed, - BearingWithConfidence angleToTheWind, SpeedWithBearingWithConfidence boatSpeed) { + BearingWithConfidence angleToTheWind, SpeedWithBearingWithConfidence boatSpeed) { double windSpeedInKnots = windSpeed.getObject().getKnots(); if (windSpeedInKnots > maxWindSpeedInKnots) { maxWindSpeedInKnots = windSpeedInKnots; @@ -51,7 +53,7 @@ public class AngleAndSpeedRegression { return new SpeedWithBearingWithConfidenceImpl(speedWithBearing, Math.min(1, speedRegression.getNumberOfAddedPoints() / 100.0), null); } - public Set> estimateTrueWindSpeedAndAngleCandidates(Speed speedOverGround) throws NotEnoughDataHasBeenAddedException { + public Set> estimateTrueWindSpeedAndAngleCandidates(Speed speedOverGround, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { double[] coefficiants = speedRegression.getOrCreatePolynomialFunction().getCoefficients(); CubicEquation equation = new CubicEquation(coefficiants[2], coefficiants[1], coefficiants[0], -speedOverGround.getKnots()); @@ -64,6 +66,9 @@ public class AngleAndSpeedRegression { boolean angleFound; try { angle = angleRegression.getOrCreatePolynomialFunction().value(windSpeedCandidateInKnots); + if ((tack == Tack.PORT && legType == LegType.UPWIND) || (tack == Tack.STARBOARD && legType == LegType.DOWNWIND)) { + angle = -angle; + } angleFound = true; } catch(NotEnoughDataHasBeenAddedException e) { angleFound = false; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/CubicRegressionPerCourseProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/CubicRegressionPerCourseProcessor.java index c462f523d7f..fe92c251752 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/CubicRegressionPerCourseProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/CubicRegressionPerCourseProcessor.java @@ -45,12 +45,13 @@ public class CubicRegressionPerCourseProcessor implements Processor getAverageSpeedAndCourseOverGround(BoatClass boatClass, Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { - GroupKey key = createGroupKey(boatClass, legType, tack); + public SpeedWithBearingWithConfidence getAverageSpeedAndCourseOverGround(BoatClass boatClass, + Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException { + GroupKey key = createGroupKey(boatClass, legType); SpeedWithBearingWithConfidence estimatedSpeedAndAngle = null; if (regressions.containsKey(key)) { estimatedSpeedAndAngle = regressions.get(key).estimateSpeedAndAngle(windSpeed); @@ -62,11 +63,11 @@ public class CubicRegressionPerCourseProcessor implements Processor> estimateTrueWindSpeedAndAngleCandidates(BoatClass boatClass, Speed speedOverGround, LegType legType, Tack tack) { - GroupKey key = createGroupKey(boatClass, legType, tack); + GroupKey key = createGroupKey(boatClass, legType); Set> result = new HashSet<>(); if (regressions.containsKey(key)) { try { - result = regressions.get(key).estimateTrueWindSpeedAndAngleCandidates(speedOverGround); + result = regressions.get(key).estimateTrueWindSpeedAndAngleCandidates(speedOverGround, legType, tack); } catch (NotEnoughDataHasBeenAddedException e) { // Return empty result } @@ -76,20 +77,14 @@ public class CubicRegressionPerCourseProcessor implements Processor windSpeedClusterGroup; private final Set windSourcesToExcludeForSpeed; private final ClusterGroup angleClusterGroup; - private final BearingWithConfidence trueWindAngle; + private final BearingWithConfidence absTrueWindAngle; private final WindWithConfidence> wind; private final SpeedWithBearingWithConfidence boatSpeed; + private LegType legType; public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor, ClusterGroup windSpeedClusterGroup, ClusterGroup angleClusterGroup) { @@ -48,7 +49,7 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe this.windSpeedClusterGroup = windSpeedClusterGroup; this.angleClusterGroup = angleClusterGroup; this.windSourcesToExcludeForSpeed = collectWindSourcesToIgnoreForSpeed(); - this.trueWindAngle = computeTrueWindAngle(); + this.absTrueWindAngle = computeTrueWindAngleAbsolute(); this.wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint(), windSourcesToExcludeForSpeed); this.boatSpeed = computeBoatSpeed(); } @@ -65,19 +66,25 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe return competitor; } - public BearingWithConfidence getAngleToTheWind() { - return trueWindAngle; + public BearingWithConfidence getAbsoluteAngleToTheWind() { + return absTrueWindAngle; } - private BearingWithConfidence computeTrueWindAngle() { - SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint()); + private BearingWithConfidence computeTrueWindAngleAbsolute() { + BearingWithConfidenceImpl result = null; + Bearing bearing = null; + try { + TrackedLegOfCompetitor currentLeg = race.getCurrentLeg(competitor, fix.getTimePoint()); + if (currentLeg != null) { + Bearing realBearing = currentLeg.getBeatAngle(fix.getTimePoint()); + bearing = realBearing == null ? null : new DegreeBearingImpl(Math.abs(realBearing.getDegrees())); + } + } catch (NoWindException e) { + bearing = null; + } WindWithConfidence> wind = race.getWindWithConfidence(fix.getPosition(), fix.getTimePoint()); - - BearingWithConfidenceImpl result = null; - if (wind != null && boatSpeed != null) { - Bearing bearing = boatSpeed.getBearing(); - Bearing difference = wind.getObject().getFrom().getDifferenceTo(bearing); - result = new BearingWithConfidenceImpl(difference, wind.getConfidence(), 0); + if (bearing != null && wind != null) { + result = new BearingWithConfidenceImpl(bearing, wind.getConfidence(), null); } return result; } @@ -133,29 +140,30 @@ public class GPSFixMovingWithPolarContext implements MovingAveragePolarClusterKe return race.getRace().getBoatClass(); } - @Override - public Tack getTack() { - return getTack(getAngleToTheWind().getObject()); - } - - public static Tack getTack(Bearing angleDifferenceWindToBoat) { - return angleDifferenceWindToBoat.getDegrees() <= 0 ? Tack.PORT : Tack.STARBOARD; - } - @Override public LegType getLegType() { - TimePoint timePoint = fix.getTimePoint(); - try { - final TrackedLeg currentLeg = race.getCurrentLeg(timePoint); - return currentLeg==null?null:currentLeg.getLegType(timePoint); - } catch (NoWindException e) { - return null; + LegType result = null; + if (legType == null) { + TimePoint timePoint = fix.getTimePoint(); + try { + TrackedLegOfCompetitor currentLegOfCompetitor = race.getCurrentLeg(getCompetitor(), timePoint); + if (currentLegOfCompetitor != null) { + final TrackedLeg currentLeg = currentLegOfCompetitor.getTrackedLeg(); + legType = currentLeg == null ? null : currentLeg.getLegType(timePoint); + } + } catch (NoWindException e) { + legType = null; + } + result = legType; + } else { + result = legType; } + return result; } @Override public Cluster getAngleCluster() { - return angleClusterGroup.getClusterFor(getAngleToTheWind().getObject()); + return angleClusterGroup.getClusterFor(getAbsoluteAngleToTheWind().getObject()); } } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/TackAndLegTypePolarClusterKey.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/LegTypePolarClusterKey.java similarity index 54% rename from java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/TackAndLegTypePolarClusterKey.java rename to java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/LegTypePolarClusterKey.java index c783abcc6bf..4a60733b411 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/TackAndLegTypePolarClusterKey.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/LegTypePolarClusterKey.java @@ -1,13 +1,9 @@ package com.sap.sailing.polars.mining; import com.sap.sailing.domain.common.LegType; -import com.sap.sailing.domain.common.Tack; import com.sap.sse.datamining.shared.annotations.Dimension; -public interface TackAndLegTypePolarClusterKey extends BasePolarClusterKey { - - @Dimension(messageKey = "tack") - Tack getTack(); +public interface LegTypePolarClusterKey extends BasePolarClusterKey { @Dimension(messageKey = "legType") LegType getLegType(); diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAveragePolarClusterKey.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAveragePolarClusterKey.java index c54228800de..7665f0a59f9 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAveragePolarClusterKey.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAveragePolarClusterKey.java @@ -4,7 +4,7 @@ import com.sap.sailing.domain.common.Speed; import com.sap.sse.datamining.data.Cluster; import com.sap.sse.datamining.shared.annotations.Dimension; -public interface MovingAveragePolarClusterKey extends TackAndLegTypePolarClusterKey { +public interface MovingAveragePolarClusterKey extends LegTypePolarClusterKey { /** * diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAverageProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAverageProcessor.java index 529f749f480..b424d18a2dd 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAverageProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAverageProcessor.java @@ -6,14 +6,13 @@ import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.Speed; -import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sse.datamining.components.Processor; import com.sap.sse.datamining.impl.components.GroupedDataEntry; public interface MovingAverageProcessor extends Processor, Void>{ SpeedWithBearingWithConfidence getAverageSpeedAndCourseOverGround(BoatClass boatClass, - Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException; + Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException; Set getAvailableBoatClasses(); } \ No newline at end of file diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAverageProcessorImpl.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAverageProcessorImpl.java index d389c4833de..ea352b76270 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAverageProcessorImpl.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/MovingAverageProcessorImpl.java @@ -16,7 +16,6 @@ import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.Position; 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.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.impl.DegreeBearingImpl; import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; @@ -77,7 +76,7 @@ public class MovingAverageProcessorImpl implements MovingAverageProcessor { averageAngleContainers.put(key, averageAngleContainer); } } - BearingWithConfidence angleToTheWind = fix.getAngleToTheWind(); + BearingWithConfidence angleToTheWind = fix.getAbsoluteAngleToTheWind(); WindWithConfidence> windSpeed = fix.getWind(); SpeedWithBearingWithConfidence boatSpeedWithConfidence = fix.getBoatSpeed(); availableBoatClasses.add(element.getDataEntry().getBoatClass()); @@ -177,21 +176,20 @@ public class MovingAverageProcessorImpl implements MovingAverageProcessor { @Override public SpeedWithBearingWithConfidence getAverageSpeedAndCourseOverGround(BoatClass boatClass, - Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { - SpeedWithBearingWithConfidence result = estimateSpeedAndAngle(boatClass, windSpeed, legType, tack); + Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException { + SpeedWithBearingWithConfidence result = estimateSpeedAndAngle(boatClass, windSpeed, legType); return result; } - private SpeedWithBearingWithConfidence estimateSpeedAndAngle(BoatClass boatClass, Speed windSpeed, LegType legType, Tack tack) throws NotEnoughDataHasBeenAddedException { - GroupKey key = createGroupKey(boatClass, windSpeed, legType, tack); + private SpeedWithBearingWithConfidence estimateSpeedAndAngle(BoatClass boatClass, Speed windSpeed, LegType legType) throws NotEnoughDataHasBeenAddedException { + GroupKey key = createGroupKey(boatClass, windSpeed, legType); Bearing angleDiffTrueWindToBoat = estimateAngle(key); SpeedWithConfidence estimatedSpeed = estimateBoatSpeed(key).getSpeedWithConfidence(); SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(estimatedSpeed.getObject().getKnots(), angleDiffTrueWindToBoat); return new SpeedWithBearingWithConfidenceImpl(speedWithBearing, estimatedSpeed.getConfidence(), null); } - private GroupKey createGroupKey(final BoatClass boatClass, final Speed windSpeed, final LegType legType, - final Tack tack) { + private GroupKey createGroupKey(final BoatClass boatClass, final Speed windSpeed, final LegType legType) { MovingAveragePolarClusterKey key = new MovingAveragePolarClusterKey() { @Override @@ -204,11 +202,6 @@ public class MovingAverageProcessorImpl implements MovingAverageProcessor { return speedClusterGroup.getClusterFor(windSpeed); } - @Override - public Tack getTack() { - return tack; - } - @Override public LegType getLegType() { return legType; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java index 19f4ef9b83e..daed33d8c9d 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataDimensionCollectionFactory.java @@ -6,7 +6,6 @@ import java.util.Collection; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.common.Bearing; import com.sap.sailing.domain.common.LegType; -import com.sap.sailing.domain.common.Tack; import com.sap.sse.datamining.data.Cluster; import com.sap.sse.datamining.factories.FunctionFactory; import com.sap.sse.datamining.functions.Function; @@ -30,13 +29,9 @@ public class PolarDataDimensionCollectionFactory { private static void addTackAndLegTypeDimensions(Collection> dimensions, FunctionFactory functionFactory) throws NoSuchMethodException { - Function tackFunction = functionFactory - .createMethodWrappingFunction(MovingAveragePolarClusterKey.class.getMethod("getTack", - new Class[0])); Function legTypeFunction = functionFactory .createMethodWrappingFunction(MovingAveragePolarClusterKey.class.getMethod("getLegType", new Class[0])); - dimensions.add(tackFunction); dimensions.add(legTypeFunction); } diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java index afd9517b42e..58c6d535ed6 100755 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarDataMiner.java @@ -38,8 +38,8 @@ import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl; import com.sap.sailing.domain.common.impl.PolarSheetsDataImpl; import com.sap.sailing.domain.common.impl.PolarSheetsHistogramDataImpl; import com.sap.sailing.domain.common.impl.WindSpeedSteppingWithMaxDistance; -import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.tracking.TrackedRace; import com.sap.sailing.polars.impl.CubicEquation; import com.sap.sse.datamining.components.FilterCriterion; @@ -212,7 +212,7 @@ public class PolarDataMiner { private ClusterGroup createAngleClusterGroup() { - return new BearingClusterGroup(-180, 180, 5); + return new BearingClusterGroup(0, 180, 5); } public void addFix(GPSFixMoving fix, Competitor competitor, TrackedRace trackedRace) { @@ -326,8 +326,14 @@ public class PolarDataMiner { SpeedWithConfidence speedWithConfidence; try { int convertedAngle = convertAngleIfNecessary(angle); - speedWithConfidence = speedRegressionPerAngleClusterProcessor.estimateBoatSpeed(boatClass, + SpeedWithConfidence regressionResult = speedRegressionPerAngleClusterProcessor.estimateBoatSpeed(boatClass, new KnotSpeedImpl(windSpeed), new DegreeBearingImpl(convertedAngle)); + if (regressionResult.getConfidence() > 0.1) { + speedWithConfidence = regressionResult; + } else { + // Low confidence. So put in 0 speed for chart + speedWithConfidence = new SpeedWithConfidenceImpl(new KnotSpeedImpl(0), regressionResult.getConfidence(), null); + } } catch (NotEnoughDataHasBeenAddedException e) { // No data so put in a 0 speed with 0 confidence speedWithConfidence = new SpeedWithConfidenceImpl(new KnotSpeedImpl(0), 0, null); @@ -392,24 +398,24 @@ public class PolarDataMiner { } public SpeedWithBearingWithConfidence getAverageSpeedAndCourseOverGround(BoatClass boatClass, - Speed windSpeed, LegType legType, Tack tack, boolean useRegressionForSpeed) throws NotEnoughDataHasBeenAddedException { + Speed windSpeed, LegType legType, boolean useRegressionForSpeed) throws NotEnoughDataHasBeenAddedException { SpeedWithBearingWithConfidence averageSpeedAndCourseOverGround = null; if (useRegressionForSpeed) { - averageSpeedAndCourseOverGround = cubicRegressionPerCourseProcessor.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, tack); + averageSpeedAndCourseOverGround = cubicRegressionPerCourseProcessor.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType); } else { - averageSpeedAndCourseOverGround = movingAverageProcessor.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType, tack); + averageSpeedAndCourseOverGround = movingAverageProcessor.getAverageSpeedAndCourseOverGround(boatClass, windSpeed, legType); } return averageSpeedAndCourseOverGround; } - public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) + public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException { - return cubicRegressionPerCourseProcessor.getSpeedRegressionFunction(boatClass, legType, tack); + return cubicRegressionPerCourseProcessor.getSpeedRegressionFunction(boatClass, legType); } - public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType, Tack tack) + public PolynomialFunction getAngleRegressionFunction(BoatClass boatClass, LegType legType) throws NotEnoughDataHasBeenAddedException { - return cubicRegressionPerCourseProcessor.getAngleRegressionFunction(boatClass, legType, tack); + return cubicRegressionPerCourseProcessor.getAngleRegressionFunction(boatClass, legType); } public PolynomialFunction getSpeedRegressionFunction(BoatClass boatClass, double trueWindAngle) diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java index 646a0a9975e..ac8fa458c92 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/PolarFixFilterCriteria.java @@ -3,11 +3,13 @@ package com.sap.sailing.polars.mining; import java.util.Collection; import java.util.Iterator; import java.util.NavigableSet; +import java.util.logging.Logger; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.Course; import com.sap.sailing.domain.base.SpeedWithBearingWithConfidence; import com.sap.sailing.domain.base.Waypoint; +import com.sap.sailing.domain.common.LegType; import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.confidence.BearingWithConfidence; import com.sap.sailing.domain.common.tracking.GPSFixMoving; @@ -22,6 +24,8 @@ import com.sap.sse.datamining.components.FilterCriterion; public class PolarFixFilterCriteria implements FilterCriterion { + private static final Logger logger = Logger.getLogger(PolarFixFilterCriteria.class.getName()); + /** * 0 if every competitor should be included. * 1 if only the leading competitor should be included and so on. @@ -57,11 +61,39 @@ public class PolarFixFilterCriteria implements FilterCriterion 70) { + logger.warning(String.format( + "Very wide upwind fix input to polar data container. Race: %s, Competitor: %s, Timepoint: %s, " + + "TWA: %s", element.getRace().getRace().getName(), element.getCompetitor().getName(), + element.getFix().getTimePoint(), twa)); + } else if (Math.abs(twa) < 35) { + logger.warning(String.format( + "Very narrow upwind fix input to polar data container. Race: %s, Competitor: %s, Timepoint: %s, " + + "TWA: %s", element.getRace().getRace().getName(), element.getCompetitor().getName(), + element.getFix().getTimePoint(), twa)); + } + } + } + private boolean importantDataIsNotNull(GPSFixMovingWithPolarContext element) { - BearingWithConfidence angleToTheWind = element.getAngleToTheWind(); + BearingWithConfidence angleToTheWind = element.getAbsoluteAngleToTheWind(); WindWithConfidence> windSpeed = element.getWind(); SpeedWithBearingWithConfidence boatSpeedWithConfidence = element.getBoatSpeed(); boolean result = false; diff --git a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedRegressionPerAngleClusterProcessor.java b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedRegressionPerAngleClusterProcessor.java index 0b8267c03e9..e1fb5b9e1b4 100644 --- a/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedRegressionPerAngleClusterProcessor.java +++ b/java/com.sap.sailing.polars/src/com/sap/sailing/polars/mining/SpeedRegressionPerAngleClusterProcessor.java @@ -26,9 +26,11 @@ import com.sap.sse.datamining.shared.GroupKey; public class SpeedRegressionPerAngleClusterProcessor implements Processor, Void>{ -private static final Logger logger = Logger.getLogger(CubicRegressionPerCourseProcessor.class.getName()); + private static final Logger logger = Logger.getLogger(CubicRegressionPerCourseProcessor.class.getName()); private final Map regressions = new HashMap<>(); + + private final Map fixCountPerBoatClass = new HashMap<>(); private final ClusterGroup angleClusterGroup; @@ -46,6 +48,13 @@ private static final Logger logger = Logger.getLogger(CubicRegressionPerCoursePr public void processElement(GroupedDataEntry element) { GroupKey key = element.getKey(); IncrementalLeastSquares regression; + BoatClass boatClass = element.getDataEntry().getBoatClass(); + synchronized (fixCountPerBoatClass) { + if (!fixCountPerBoatClass.containsKey(boatClass)) { + fixCountPerBoatClass.put(boatClass, 0L); + } + fixCountPerBoatClass.put(boatClass, fixCountPerBoatClass.get(boatClass) + 1); + } synchronized (regressions) { regression = regressions.get(key); if (regression == null) { @@ -66,18 +75,29 @@ private static final Logger logger = Logger.getLogger(CubicRegressionPerCoursePr public SpeedWithConfidence estimateBoatSpeed(BoatClass boatClass, Speed windSpeed, Bearing trueWindAngle) throws NotEnoughDataHasBeenAddedException { double speedSum = 0; double numberOfSpeeds = 0; - for (int i = -5; i <= 5; i++) { - GroupKey key = createGroupKey(boatClass, new DegreeBearingImpl(trueWindAngle.getDegrees() + i)); + long fixCount = 0; + for (int i = -2; i <= 2; i++) { + GroupKey key = createGroupKey(boatClass, new DegreeBearingImpl(Math.abs(trueWindAngle.getDegrees()) + i)); if (regressions.containsKey(key)) { - speedSum += regressions.get(key).getOrCreatePolynomialFunction().value(windSpeed.getKnots()); - numberOfSpeeds++; + IncrementalLeastSquares incrementalLeastSquares = regressions.get(key); + fixCount = fixCount + incrementalLeastSquares.getNumberOfAddedPoints(); + if (fixCount > 10) { + speedSum += incrementalLeastSquares.getOrCreatePolynomialFunction().value(windSpeed.getKnots()); + numberOfSpeeds++; + } } } - if (numberOfSpeeds < 1) { + fixCount = (long) (fixCount / numberOfSpeeds); + long fixCountOverall = 0; + if (numberOfSpeeds < 2 || fixCount < 10) { throw new NotEnoughDataHasBeenAddedException("Not enough data has been added to Per Course Regressions"); + } else { + synchronized (fixCountPerBoatClass) { + fixCountOverall = fixCountPerBoatClass.get(boatClass); + } } Speed speed = new KnotSpeedImpl(speedSum / numberOfSpeeds); - return new SpeedWithConfidenceImpl(speed, /*FIXME*/ 0.5, null); + return new SpeedWithConfidenceImpl(speed, Math.min(1, 5.0 * ((double) fixCount / fixCountOverall)), null); } private GroupKey createGroupKey(final BoatClass boatClass, final Bearing angle) { diff --git a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/EventBaseJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/EventBaseJsonSerializer.java index d5d682ce31e..82c729f0025 100644 --- a/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/EventBaseJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization.shared.android/src/com/sap/sailing/server/gateway/serialization/impl/EventBaseJsonSerializer.java @@ -11,8 +11,8 @@ import org.json.simple.JSONObject; import com.sap.sailing.domain.base.EventBase; import com.sap.sailing.domain.base.LeaderboardGroupBase; import com.sap.sailing.domain.base.Venue; -import com.sap.sailing.domain.common.ImageSize; import com.sap.sailing.server.gateway.serialization.JsonSerializer; +import com.sap.sse.common.media.ImageSize; public class EventBaseJsonSerializer implements JsonSerializer { private static final Logger logger = Logger.getLogger(EventBaseJsonSerializer.class.getName()); diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/DeviceAndSessionIdentifierWithGPSFixesDeserializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/DeviceAndSessionIdentifierWithGPSFixesDeserializer.java index ad613c2641a..20ce4a34a7c 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/DeviceAndSessionIdentifierWithGPSFixesDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/DeviceAndSessionIdentifierWithGPSFixesDeserializer.java @@ -59,4 +59,4 @@ implements JsonDeserializer>(deviceId, sessionId, fixes); } -} +} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/GPSFixMovingJsonDeserializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/GPSFixMovingJsonDeserializer.java index aeb5720c792..1817f561e86 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/GPSFixMovingJsonDeserializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/deserialization/impl/GPSFixMovingJsonDeserializer.java @@ -37,4 +37,4 @@ public class GPSFixMovingJsonDeserializer extends TypeBasedJsonDeserializer { return result; } -} +} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GPSFixMovingJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GPSFixMovingJsonSerializer.java index b6a10d5ead2..6691aec6488 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GPSFixMovingJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GPSFixMovingJsonSerializer.java @@ -18,4 +18,4 @@ public class GPSFixMovingJsonSerializer implements JsonSerializer return result; } -} +} diff --git a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GPSFixMovingNmeaDTOJsonSerializer.java b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GPSFixMovingNmeaDTOJsonSerializer.java index 2b77796f60e..0b6c5f45d3f 100644 --- a/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GPSFixMovingNmeaDTOJsonSerializer.java +++ b/java/com.sap.sailing.server.gateway.serialization/src/com/sap/sailing/server/gateway/serialization/impl/GPSFixMovingNmeaDTOJsonSerializer.java @@ -33,4 +33,4 @@ public class GPSFixMovingNmeaDTOJsonSerializer implements JsonSerializer { return result; } -} +} diff --git a/java/com.sap.sailing.server.gateway/main.css b/java/com.sap.sailing.server.gateway/main.css index c2de8aa273b..62189661f94 100644 --- a/java/com.sap.sailing.server.gateway/main.css +++ b/java/com.sap.sailing.server.gateway/main.css @@ -1,80 +1,80 @@ -/* line 2, _reset.sass */ -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; -} - -body { - height: 100%; - color: #222222; - font-size: 14px; - font-family: OpenSansRegular, Arial, Verdana, sans-serif; - font-weight: normal; - margin: 10px; - padding: 0px; - line-height: 18px; -} - -h1 { - font-size: 16px; - font-family: OpenSansRegular, Arial, Verdana, sans-serif; - font-weight: bold; - margin-top: 10px; - margin-bottom: 10px; -} - -h2 { - font-size: 16px; - font-family: OpenSansRegular, Arial, Verdana, sans-serif; - font-weight: normal; - margin-top: 5px; - margin-bottom: 10px; -} - -table td, table th -{ - padding: 5px; -} - -table { - border-spacing:0; - border-collapse:collapse; -} - -table thead tr td { - background-color: #eeeeee; -} - -table tbody tr:nth-child(odd) td { - background-color: #cccccc; -} - -table tbody tr:nth-child(even) td { - background-color: #dddddd; -} - -a { - text-decoration: none; - color: #0066b3; -} - -a:hover { - text-decoration: underline; -} - +/* line 2, _reset.sass */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; +} + +body { + height: 100%; + color: #222222; + font-size: 14px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: normal; + margin: 10px; + padding: 0px; + line-height: 18px; +} + +h1 { + font-size: 16px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: bold; + margin-top: 10px; + margin-bottom: 10px; +} + +h2 { + font-size: 16px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: normal; + margin-top: 5px; + margin-bottom: 10px; +} + +table td, table th +{ + padding: 5px; +} + +table { + border-spacing:0; + border-collapse:collapse; +} + +table thead tr td { + background-color: #eeeeee; +} + +table tbody tr:nth-child(odd) td { + background-color: #cccccc; +} + +table tbody tr:nth-child(even) td { + background-color: #dddddd; +} + +a { + text-decoration: none; + color: #0066b3; +} + +a:hover { + text-decoration: underline; +} + diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/RegattaAndRaceDataJsonGetServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/RegattaAndRaceDataJsonGetServlet.java index ab154994dbd..50709bc5dc8 100755 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/RegattaAndRaceDataJsonGetServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/RegattaAndRaceDataJsonGetServlet.java @@ -28,6 +28,7 @@ import com.sap.sailing.domain.common.Tack; import com.sap.sailing.domain.common.Wind; import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.TrackedLeg; import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor; @@ -208,6 +209,7 @@ public class RegattaAndRaceDataJsonGetServlet extends AbstractJsonHttpServlet { System.out.println("Blocking at "+trackedRace.getUpdateCount()+" waiting for "+sinceUpdate); trackedRace.waitForNextUpdate(sinceUpdate); } + final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint); JSONObject jsonRace = new JSONObject(); jsonRace.put("name", trackedRace.getRace().getName()); jsonRace.put("startoftracking", trackedRace.getStartOfTracking() == null ? 0l : trackedRace @@ -316,7 +318,7 @@ public class RegattaAndRaceDataJsonGetServlet extends AbstractJsonHttpServlet { } } jsonCompetitorInLeg.put("gapToLeaderInSeconds", - trackedLegOfCompetitor.getGapToLeader(timePoint, WindPositionMode.LEG_MIDDLE)); + trackedLegOfCompetitor.getGapToLeader(timePoint, rankingInfo, WindPositionMode.LEG_MIDDLE)); final Duration estimatedTimeToNextMarkInSeconds = trackedLegOfCompetitor .getEstimatedTimeToNextMark(timePoint, WindPositionMode.EXACT); jsonCompetitorInLeg.put("estimatedTimeToNextMarkInSeconds", diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CourseJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CourseJsonExportServlet.java index f75a4505233..f0db237c5a2 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CourseJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/CourseJsonExportServlet.java @@ -87,7 +87,7 @@ public class CourseJsonExportServlet extends AbstractJsonHttpServlet { result = new JSONObject(); } + setJsonResponseHeader(response); result.writeJSONString(response.getWriter()); - response.setContentType("application/json"); } } diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarkPositionsJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarkPositionsJsonExportServlet.java index ff5cf988a06..04912c301cf 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarkPositionsJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarkPositionsJsonExportServlet.java @@ -98,8 +98,8 @@ public class MarkPositionsJsonExportServlet extends AbstractJsonHttpServlet { } } + setJsonResponseHeader(response); result.writeJSONString(response.getWriter()); - response.setContentType("application/json"); } @SuppressWarnings("unused") //unused since course area has no position yet diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarksJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarksJsonExportServlet.java index 189084e79e5..8b108f5c4fb 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarksJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/MarksJsonExportServlet.java @@ -80,8 +80,8 @@ public class MarksJsonExportServlet extends AbstractJsonHttpServlet { } } + setJsonResponseHeader(response); result.writeJSONString(response.getWriter()); - response.setContentType("application/json"); } private UUID toUUID(String value) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java index 024f7890e7f..91487d6bc84 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/impl/rc/RaceGroupJsonExportServlet.java @@ -78,8 +78,8 @@ public class RaceGroupJsonExportServlet extends AbstractJsonHttpServlet { } } + setJsonResponseHeader(response); result.writeJSONString(response.getWriter()); - response.setContentType("application/json"); } private UUID toUUID(String value) { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java index 4a0a4ed6ec2..020b2dc782b 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/PolarResource.java @@ -85,16 +85,15 @@ public class PolarResource extends AbstractSailingServerResource { StringBuilder stringBuilder = new StringBuilder(); for (BoatClass boatClass : boatClasses) { for (LegType legType : new LegType[] { LegType.UPWIND, LegType.DOWNWIND }) { - for (Tack tack : new Tack[] { Tack.PORT, Tack.STARBOARD }) { - try { - PolynomialFunction speedFunction = polarDataService.getSpeedRegressionFunction(boatClass, legType, tack); - stringBuilder.append("Speed: " + boatClass + " " + legType + " " + tack + ": " + speedFunction.toString() + "\n"); - PolynomialFunction angleFunction = polarDataService.getAngleRegressionFunction(boatClass, legType, tack); - stringBuilder.append("Angle: " + boatClass + " " + legType + " " + tack + ": " + angleFunction.toString() + "\n"); - } catch (NotEnoughDataHasBeenAddedException e) { - stringBuilder.append("No data for " + boatClass + " " + legType + " " + tack + "\n"); - } - } + try { + PolynomialFunction speedFunction = polarDataService.getSpeedRegressionFunction(boatClass, legType); + stringBuilder.append("Speed: " + boatClass + " " + legType + ": " + speedFunction.toString() + "\n"); + PolynomialFunction angleFunction = polarDataService.getAngleRegressionFunction(boatClass, legType); + stringBuilder.append("Angle: " + boatClass + " " + legType + ": " + angleFunction.toString() + "\n"); + } catch (NotEnoughDataHasBeenAddedException e) { + stringBuilder.append("No data for " + boatClass + " " + legType + "\n"); + } + } for (double trueWindAngle = -177.5; trueWindAngle < 180; trueWindAngle = trueWindAngle + 5) { try { diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java index 5668f8b5530..6db76168f62 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/RegattasResource.java @@ -38,6 +38,7 @@ import com.sap.sailing.domain.common.WindSource; import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.TrackedLeg; @@ -783,7 +784,7 @@ public class RegattasResource extends AbstractSailingServerResource { TrackedRace trackedRace = findTrackedRace(regattaName, raceName); TimePoint timePoint = trackedRace.getTimePointOfNewestEvent() == null ? MillisecondsTimePoint.now() : trackedRace.getTimePointOfNewestEvent(); - + final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint); JSONObject jsonRaceResults = new JSONObject(); jsonRaceResults.put("name", trackedRace.getRace().getName()); jsonRaceResults.put("regatta", regatta.getName()); @@ -889,7 +890,7 @@ public class RegattasResource extends AbstractSailingServerResource { } } jsonCompetitorInLeg.put("gapToLeader-s", - trackedLegOfCompetitor.getGapToLeader(timePoint, WindPositionMode.LEG_MIDDLE)); + trackedLegOfCompetitor.getGapToLeader(timePoint, rankingInfo, WindPositionMode.LEG_MIDDLE)); jsonCompetitorInLeg.put("started", trackedLegOfCompetitor.hasStartedLeg(timePoint)); jsonCompetitorInLeg.put("finished", trackedLegOfCompetitor.hasFinishedLeg(timePoint)); jsonCompetitors.add(jsonCompetitorInLeg); @@ -935,7 +936,7 @@ public class RegattasResource extends AbstractSailingServerResource { TimePoint timePoint = trackedRace.getTimePointOfNewestEvent() == null ? MillisecondsTimePoint.now() : trackedRace.getTimePointOfNewestEvent(); // if(trackedRace.isLive(timePoint)) { - + final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint); JSONObject jsonLiveData = new JSONObject(); jsonLiveData.put("name", trackedRace.getRace().getName()); jsonLiveData.put("regatta", regatta.getName()); @@ -990,16 +991,16 @@ public class RegattasResource extends AbstractSailingServerResource { } Duration gapToLeader = currentLegOfCompetitor.getGapToLeader(timePoint, - WindPositionMode.LEG_MIDDLE); + rankingInfo, WindPositionMode.LEG_MIDDLE); if (gapToLeader != null) { jsonCompetitorInLeg.put("gapToLeader-s", roundDouble(gapToLeader.asSeconds(), 2)); } - Distance windwardDistanceToOverallLeader = currentLegOfCompetitor - .getWindwardDistanceToOverallLeader(timePoint, WindPositionMode.LEG_MIDDLE); - if (windwardDistanceToOverallLeader != null) { + Distance windwardDistanceToCompetitorFarthestAhead = currentLegOfCompetitor + .getWindwardDistanceToCompetitorFarthestAhead(timePoint, WindPositionMode.LEG_MIDDLE, rankingInfo); + if (windwardDistanceToCompetitorFarthestAhead != null) { jsonCompetitorInLeg.put("gapToLeader-m", - roundDouble(windwardDistanceToOverallLeader.getMeters(), 2)); + roundDouble(windwardDistanceToCompetitorFarthestAhead.getMeters(), 2)); } jsonCompetitorInLeg.put("finished", false); } else { diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java index 099d29a7093..48a8ddd7da4 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/InitialLoadReplicationObjectIdentityTest.java @@ -49,7 +49,6 @@ import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindSourceImpl; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; @@ -72,6 +71,7 @@ import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util.Pair; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MimeType; import com.sap.sse.replication.ReplicationMasterDescriptor; import com.sap.sse.replication.impl.ReplicationReceiver; import com.sap.sse.replication.testsupport.AbstractServerReplicationTestSetUp.ReplicationServiceTestImpl; diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java index 78740e6641d..ac9529085c3 100644 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/MediaReplicationTest.java @@ -13,11 +13,11 @@ import org.junit.Test; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MimeType; public class MediaReplicationTest extends AbstractServerReplicationTest { diff --git a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java index 1cc657c1968..1110d087034 100755 --- a/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java +++ b/java/com.sap.sailing.server.replication.test/src/com/sap/sailing/server/replication/test/TrackRaceReplicationTest.java @@ -142,8 +142,8 @@ public class TrackRaceReplicationTest extends AbstractServerReplicationTest { master.apply(new ConnectTrackedRaceToLeaderboardColumn(leaderboardName, columnName, defaultFleet.getName(), new RegattaNameAndRaceName("Academy Tracking 2011 (STG)", "weym470may122011"))); startTracking(); - assertNotNull(masterColumn.getTrackedRace(defaultFleet)); // ensure the re-assignment worked on the master Thread.sleep(1000); + assertNotNull(masterColumn.getTrackedRace(defaultFleet)); // ensure the re-assignment worked on the master TrackedRace replicaTrackedRace = replica.getTrackedRace(raceIdentifier); assertNotNull(replicaTrackedRace); assertNotSame(masterTrackedRace, replicaTrackedRace); diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MasterDataImporterMediaTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MasterDataImporterMediaTest.java index ece6b4602e6..65ac05d2262 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MasterDataImporterMediaTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MasterDataImporterMediaTest.java @@ -27,7 +27,6 @@ import org.mockito.Mockito; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.persistence.impl.MongoObjectFactoryImpl; @@ -37,6 +36,7 @@ import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.server.RacingEventService; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MimeType; public class MasterDataImporterMediaTest { diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MediaLibaryTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MediaLibaryTest.java index 06eeaeed184..c2f1770f50c 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MediaLibaryTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MediaLibaryTest.java @@ -12,13 +12,13 @@ import org.junit.Test; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.TimeRange; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; import com.sap.sse.common.impl.TimeRangeImpl; +import com.sap.sse.common.media.MimeType; import static org.junit.Assert.*; import static org.hamcrest.Matchers.*; diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MediaMasterDataExportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MediaMasterDataExportTest.java index bf052d616b7..3f3dca1378b 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MediaMasterDataExportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/impl/MediaMasterDataExportTest.java @@ -33,7 +33,6 @@ import com.sap.sailing.domain.common.RaceIdentifier; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaNameAndRaceName; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.LeaderboardGroup; @@ -52,6 +51,7 @@ import com.sap.sse.common.Duration; import com.sap.sse.common.TimePoint; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MimeType; public class MediaMasterDataExportTest { diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java index 933c245ada8..4bf9378e56b 100755 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/MasterDataImportTest.java @@ -93,7 +93,6 @@ import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl; import com.sap.sailing.domain.common.impl.WindImpl; import com.sap.sailing.domain.common.impl.WindSourceWithAdditionalID; import com.sap.sailing.domain.common.media.MediaTrack; -import com.sap.sailing.domain.common.media.MimeType; import com.sap.sailing.domain.common.tracking.GPSFix; import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.leaderboard.FlexibleLeaderboard; @@ -128,6 +127,7 @@ import com.sap.sse.common.TimePoint; import com.sap.sse.common.Util; import com.sap.sse.common.impl.MillisecondsDurationImpl; import com.sap.sse.common.impl.MillisecondsTimePoint; +import com.sap.sse.common.media.MimeType; import com.sap.sse.mongodb.MongoDBConfiguration; import com.sap.sse.mongodb.MongoDBService; diff --git a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java index 6d857720fb3..b74ea269f82 100644 --- a/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java +++ b/java/com.sap.sailing.server.test/src/com/sap/sailing/server/test/RaceTrackerStartStopTest.java @@ -123,10 +123,15 @@ public class RaceTrackerStartStopTest { // of the tracked event assertNotNull(regatta.getRaceByName(RACENAME2)); boolean foundTrackedRaceForRaceDef2 = false; - for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) { - if (trackedRace.getRace().getName().equals(RACENAME2)) { - foundTrackedRaceForRaceDef2 = true; + trackedRegatta.lockTrackedRacesForRead(); + try { + for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) { + if (trackedRace.getRace().getName().equals(RACENAME2)) { + foundTrackedRaceForRaceDef2 = true; + } } + } finally { + trackedRegatta.unlockTrackedRacesAfterRead(); } assertTrue(foundTrackedRaceForRaceDef2); // The raceTracker2 and raceTracker3 should currently not be in track mode. @@ -161,10 +166,15 @@ public class RaceTrackerStartStopTest { // from the tracked event assertNull(regatta.getRaceByName(RACENAME2)); boolean foundTrackedRaceForRaceDef2 = false; - for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) { - if (trackedRace.getRace().getName().equals(RACENAME2)) { - foundTrackedRaceForRaceDef2 = true; + trackedRegatta.lockTrackedRacesForRead(); + try { + for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) { + if (trackedRace.getRace().getName().equals(RACENAME2)) { + foundTrackedRaceForRaceDef2 = true; + } } + } finally { + trackedRegatta.unlockTrackedRacesAfterRead(); } assertFalse(foundTrackedRaceForRaceDef2); // The trackers map should still contain the raceTrackers diff --git a/java/com.sap.sailing.server/SailingServer (No Proxy).launch b/java/com.sap.sailing.server/SailingServer (No Proxy).launch index d1cc35750e5..7614e7a8e9a 100644 --- a/java/com.sap.sailing.server/SailingServer (No Proxy).launch +++ b/java/com.sap.sailing.server/SailingServer (No Proxy).launch @@ -1,38 +1,38 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java index d6b9c3c0bbc..f0aa9d9f5d6 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java @@ -16,6 +16,7 @@ import java.util.concurrent.ConcurrentHashMap; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.race.RaceLog; import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent; +import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.CompetitorStore; import com.sap.sailing.domain.base.CourseArea; import com.sap.sailing.domain.base.DomainFactory; @@ -606,4 +607,6 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch ClassLoader getCombinedMasterDataClassLoader(); + Iterable getCompetitorInOrderOfWindwardDistanceTraveledFarthestFirst(TrackedRace trackedRace, TimePoint timePoint); + } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardSearchResultBaseRanker.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardSearchResultBaseRanker.java index a891fc249fe..d1121fb3bd4 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardSearchResultBaseRanker.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/LeaderboardSearchResultBaseRanker.java @@ -37,10 +37,15 @@ public class LeaderboardSearchResultBaseRanker startOfTrackingTimes = new ArrayList<>(); - for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) { - if (trackedRace.getStartOfTracking() != null) { - startOfTrackingTimes.add(trackedRace.getStartOfTracking()); + trackedRegatta.lockTrackedRacesForRead(); + try { + for (TrackedRace trackedRace : trackedRegatta.getTrackedRaces()) { + if (trackedRace.getStartOfTracking() != null) { + startOfTrackingTimes.add(trackedRace.getStartOfTracking()); + } } + } finally { + trackedRegatta.unlockTrackedRacesAfterRead(); } final TimePoint result; if (startOfTrackingTimes.isEmpty()) { diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 484bfb7ab97..657e50f24b8 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -132,6 +132,7 @@ import com.sap.sailing.domain.racelog.RaceLogIdentifier; import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.ranking.RankingMetricConstructor; import com.sap.sailing.domain.regattalike.IsRegattaLike; import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; @@ -1893,11 +1894,17 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes regatta.setEndDate(endDate); if (regatta.useStartTimeInference() != useStartTimeInference) { regatta.setUseStartTimeInference(useStartTimeInference); - if (getTrackedRegatta(regatta) != null) { - for (DynamicTrackedRace trackedRace : getTrackedRegatta(regatta).getTrackedRaces()) { - // the start times of the regatta's tracked races now have to be re-evaluated the next time they are - // queried - trackedRace.invalidateStartTime(); + final DynamicTrackedRegatta trackedRegatta = getTrackedRegatta(regatta); + if (trackedRegatta != null) { + trackedRegatta.lockTrackedRacesForRead(); + try { + for (DynamicTrackedRace trackedRace : trackedRegatta.getTrackedRaces()) { + // the start times of the regatta's tracked races now have to be re-evaluated the next time they + // are queried + trackedRace.invalidateStartTime(); + } + } finally { + trackedRegatta.unlockTrackedRacesAfterRead(); } } } @@ -1923,10 +1930,19 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes TrackedRace trackedRace = getExistingTrackedRace(regatta, race); if (trackedRace != null) { TrackedRegatta trackedRegatta = getTrackedRegatta(regatta); + final boolean isTrackedRacesEmpty; if (trackedRegatta != null) { - trackedRegatta.removeTrackedRace(trackedRace); + trackedRegatta.lockTrackedRacesForWrite(); + try { + trackedRegatta.removeTrackedRace(trackedRace); + isTrackedRacesEmpty = Util.isEmpty(trackedRegatta.getTrackedRaces()); + } finally { + trackedRegatta.unlockTrackedRacesAfterWrite(); + } + } else { + isTrackedRacesEmpty = false; } - if (Util.isEmpty(trackedRegatta.getTrackedRaces())) { + if (isTrackedRacesEmpty) { removeTrackedRegatta(regatta); } // remove tracked race from RaceColumns of regatta @@ -3146,10 +3162,15 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes for (Regatta regatta : allRegattas) { DynamicTrackedRegatta trackedRegatta = getTrackedRegatta(regatta); if (trackedRegatta != null) { - Iterable trackedRaces = trackedRegatta.getTrackedRaces(); - for (TrackedRace trackedRace : trackedRaces) { - trackedRace.setPolarDataService(service); - service.insertExistingFixes(trackedRace); + trackedRegatta.lockTrackedRacesForRead(); + try { + Iterable trackedRaces = trackedRegatta.getTrackedRaces(); + for (TrackedRace trackedRace : trackedRaces) { + trackedRace.setPolarDataService(service); + service.insertExistingFixes(trackedRace); + } + } finally { + trackedRegatta.unlockTrackedRacesAfterRead(); } } } @@ -3161,4 +3182,14 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes setPolarDataService(null); } } + + @Override + public Iterable getCompetitorInOrderOfWindwardDistanceTraveledFarthestFirst(TrackedRace trackedRace, TimePoint timePoint) { + final RankingInfo rankingInfo = trackedRace.getRankingMetric().getRankingInfo(timePoint); + final List result = new ArrayList<>(); + Util.addAll(trackedRace.getRace().getCompetitors(), result); + result.sort((c1, c2) -> rankingInfo.getCompetitorRankingInfo().apply(c2).getWindwardDistanceSailed().compareTo( + rankingInfo.getCompetitorRankingInfo().apply(c1).getWindwardDistanceSailed())); + return result; + } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java index 3635e75c91d..e0218afdb82 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/masterdata/DummyTrackedRace.java @@ -34,6 +34,7 @@ import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException; import com.sap.sailing.domain.polars.PolarDataService; import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.ranking.RankingMetric; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.CourseDesignChangedListener; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.LineDetails; @@ -339,7 +340,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { } @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) { return null; } @@ -589,12 +590,6 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { public void setPolarDataService(PolarDataService polarDataService) { } - @Override - public Distance getCorrectedWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode) { - return null; - } - @Override public Duration getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException, NoWindException { @@ -602,8 +597,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials { } @Override - public Distance getWindwardDistanceToOverallLeader(Competitor competitor, TimePoint timePoint, - WindPositionMode windPositionMode, WindLegTypeAndLegBearingCache cache) { + public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, + WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) { return null; } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java index 919354bbdfc..4308830717e 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/operationaltransformation/ImportMasterDataOperation.java @@ -406,8 +406,13 @@ public class ImportMasterDataOperation extends TrackedRegatta trackedRegatta = toState.getTrackedRegatta(existingRegatta); List toRemove = new ArrayList(); if (trackedRegatta != null) { - for (TrackedRace race : trackedRegatta.getTrackedRaces()) { - toRemove.add(race); + trackedRegatta.lockTrackedRacesForRead(); + try { + for (TrackedRace race : trackedRegatta.getTrackedRaces()) { + toRemove.add(race); + } + } finally { + trackedRegatta.unlockTrackedRacesAfterRead(); } for (TrackedRace raceToRemove : toRemove) { trackedRegatta.removeTrackedRace(raceToRemove); diff --git a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java index dff311526c0..fb121b0e1ec 100644 --- a/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java +++ b/java/com.sap.sailing.simulator/src/com/sap/sailing/simulator/impl/PolarDiagramGPS.java @@ -65,8 +65,9 @@ public class PolarDiagramGPS extends PolarDiagramBase { beatStar = null; } if (beatStar != null) { - Bearing avgBeatAngle = new DegreeBearingImpl((beatStar.getBearing().getDegrees() + (360 - beatPort - .getBearing().getDegrees()) % 360) / 2.0); + double beatStarAngle = Math.abs((beatStar.getBearing().getDegrees() + 180.0) % 360.0 - 180.0); + double beatPortAngle = Math.abs((beatPort.getBearing().getDegrees() + 180.0) % 360.0 - 180.0); + Bearing avgBeatAngle = new DegreeBearingImpl((beatStarAngle + beatPortAngle) / 2.0); beatAngles.add(avgBeatAngle); Speed avgBeatSpeed = new KnotSpeedImpl((beatStar.getKnots() + beatPort.getKnots()) / 2.0); beatSpeed.add(avgBeatSpeed); @@ -93,8 +94,9 @@ public class PolarDiagramGPS extends PolarDiagramBase { jibeStar = null; } if (jibeStar != null) { - Bearing avgJibeAngle = new DegreeBearingImpl((jibeStar.getBearing().getDegrees() + (360 - jibePort - .getBearing().getDegrees()) % 360) / 2.0); + double jibeStarAngle = Math.abs((jibeStar.getBearing().getDegrees() + 180.0) % 360.0 - 180.0); + double jibePortAngle = Math.abs((jibePort.getBearing().getDegrees() + 180.0) % 360.0 - 180.0); + Bearing avgJibeAngle = new DegreeBearingImpl((jibeStarAngle + jibePortAngle) / 2.0); jibeAngles.add(avgJibeAngle); Speed avgJibeSpeed = new KnotSpeedImpl((jibeStar.getKnots() + jibePort.getKnots()) / 2.0); jibeSpeed.add(avgJibeSpeed); diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.eot b/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.eot new file mode 100644 index 00000000000..fd9cd07f214 Binary files /dev/null and b/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.eot differ diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.svg b/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.svg new file mode 100644 index 00000000000..96687610c5c --- /dev/null +++ b/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.ttf b/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.ttf new file mode 100644 index 00000000000..f5a171c3ba1 Binary files /dev/null and b/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.ttf differ diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.woff b/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.woff new file mode 100644 index 00000000000..6bd7654c3fc Binary files /dev/null and b/java/com.sap.sailing.www/fonts/open-sans-condensed-bold-webfont.woff differ diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.eot b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.eot new file mode 100644 index 00000000000..56f04c01e5f Binary files /dev/null and b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.eot differ diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.svg b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.svg new file mode 100644 index 00000000000..117a67e1be2 --- /dev/null +++ b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.ttf b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.ttf new file mode 100644 index 00000000000..2b2cbb254d3 Binary files /dev/null and b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.ttf differ diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.woff b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.woff new file mode 100644 index 00000000000..2409ef16f61 Binary files /dev/null and b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-italic-webfont.woff differ diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.eot b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.eot new file mode 100644 index 00000000000..b2c34dc5e5d Binary files /dev/null and b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.eot differ diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.svg b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.svg new file mode 100644 index 00000000000..2037b144c62 --- /dev/null +++ b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.svg @@ -0,0 +1 @@ + \ No newline at end of file diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.ttf b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.ttf new file mode 100644 index 00000000000..044dc1f957d Binary files /dev/null and b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.ttf differ diff --git a/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.woff b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.woff new file mode 100644 index 00000000000..7564c664211 Binary files /dev/null and b/java/com.sap.sailing.www/fonts/open-sans-condensed-light-webfont.woff differ diff --git a/java/com.sap.sailing.www/fonts/OpenSans-Light-webfont.eot b/java/com.sap.sailing.www/fonts/opensans-light-webfont.eot similarity index 100% rename from java/com.sap.sailing.www/fonts/OpenSans-Light-webfont.eot rename to java/com.sap.sailing.www/fonts/opensans-light-webfont.eot diff --git a/java/com.sap.sailing.www/fonts/OpenSans-Light-webfont.svg b/java/com.sap.sailing.www/fonts/opensans-light-webfont.svg similarity index 100% rename from java/com.sap.sailing.www/fonts/OpenSans-Light-webfont.svg rename to java/com.sap.sailing.www/fonts/opensans-light-webfont.svg diff --git a/java/com.sap.sailing.www/fonts/OpenSans-Light-webfont.ttf b/java/com.sap.sailing.www/fonts/opensans-light-webfont.ttf similarity index 100% rename from java/com.sap.sailing.www/fonts/OpenSans-Light-webfont.ttf rename to java/com.sap.sailing.www/fonts/opensans-light-webfont.ttf diff --git a/java/com.sap.sailing.www/fonts/OpenSans-Light-webfont.woff b/java/com.sap.sailing.www/fonts/opensans-light-webfont.woff similarity index 100% rename from java/com.sap.sailing.www/fonts/OpenSans-Light-webfont.woff rename to java/com.sap.sailing.www/fonts/opensans-light-webfont.woff diff --git a/java/com.sap.sailing.www/main.css b/java/com.sap.sailing.www/main.css index 7b8f32e0f7a..90cf3fb18d2 100644 --- a/java/com.sap.sailing.www/main.css +++ b/java/com.sap.sailing.www/main.css @@ -1,235 +1,240 @@ -/* line 2, _reset.sass */ -html, body, div, span, applet, object, iframe, -h1, h2, h3, h4, h5, h6, p, blockquote, pre, -a, abbr, acronym, address, big, cite, code, -del, dfn, em, img, ins, kbd, q, s, samp, -small, strike, strong, sub, sup, tt, var, -b, u, i, center, -dl, dt, dd, ol, ul, li, -fieldset, form, label, legend, -table, caption, tbody, tfoot, thead, tr, th, td, -article, aside, canvas, details, embed, -figure, figcaption, footer, header, hgroup, -menu, nav, output, ruby, section, summary, -time, mark, audio, video { - margin: 0; - padding: 0; - border: 0; - font-size: 100%; - font: inherit; - vertical-align: baseline; } - -/* HTML5 display-role reset for older browsers */ -/* line 23, _reset.sass */ -article, aside, details, figcaption, figure, -footer, header, hgroup, menu, nav, section { - display: block; } - -/* line 27, _reset.sass */ -body { - line-height: 1; } - -/* line 30, _reset.sass */ -ol, ul { - list-style: none; } - -/* line 33, _reset.sass */ -blockquote, q { - quotes: none; } - -/* line 36, _reset.sass */ -blockquote:before, blockquote:after, -q:before, q:after { - content: ""; - content: none; } - -/* line 41, _reset.sass */ -table { - border-collapse: collapse; - border-spacing: 0; } - -/* line 7, site.css.sass */ -html { - height: 100%; } - -/* line 10, site.css.sass */ -body { - height: 100%; - color: #222222; - font-size: 14px; - font-family: OpenSansRegular, Arial, Verdana, sans-serif; - font-weight: normal; - background-image: url(../images/bg-image.jpg); - background-attachment: fixed; - background-color: #222222; - background-position: 50% 0%; - background-repeat: no-repeat no-repeat; - margin: 0px; - padding: 0px; - line-height: 18px; } - -/* line 27, site.css.sass */ -ul { - font-size: 12px; - padding-left: 0px; - margin-bottom: 25px; - margin-left: 30px; } - -/* line 34, site.css.sass */ -a { - text-decoration: none; - color: #0066b3; } - /* line 38, site.css.sass */ - a:hover { - text-decoration: underline; } - -/* line 42, site.css.sass */ -#yield_content { - width: 950px; - margin: 0 auto; - min-height: 100%; } - #yield_content #logoAndTitlePanel { - height: 52px; - } - #yield_content .sapLogo { - height: 52px; - float: left; - } - #yield_content .headNavigation { - float: right; - margin: 25px 0 0 0; - padding: 0; } - #yield_content .headNavigation a { - font-family: 'OpenSansBold', Arial, Verdana, sans-serif; - color: #fff; } - /* line 47, site.css.sass */ - #yield_content #pageHeadline { - color: white; - margin: 10px 0 10px 0; - font-size: 18px; } - /* line 53, site.css.sass */ - #yield_content #keyVisual { - height: 240px; - position: relative; - background: white; } - /* line 58, site.css.sass */ - #yield_content #keyVisual #infoBox { - width: 502px; - height: 100px; - background: #999999; - position: absolute; - right: 0; - bottom: 0; - padding: 10px; } - /* line 67, site.css.sass */ - #yield_content #keyVisual #infoBox p { - color: white; - font-size: 12px; } - #yield_content #keyVisual #infoBox a { - text-decoration: none; - float: right; - background: #fcb913; - color: white; - font-size: 18px; - font-family: OpenSansRegular, Arial, Verdana, sans-serif; - padding: 6px 12px 6px 12px; } - - /* line 72, site.css.sass */ - #yield_content #mainContent { - padding: 0 10px 10px 10px; - background: white; } - /* line 79, site.css.sass */ - #yield_content #mainContent .innerContent .timeBar { - background: url(../images/timeBar-bg.png) 0 0 repeat-y; } - /* line 82, site.css.sass */ - #yield_content #mainContent .innerContent .timeBar .raceSection { - margin: 0 0 20px 0; - *display: inline-block; } - /* line 3, _helpers.sass */ - #yield_content #mainContent .innerContent .timeBar .raceSection:after { - content: " "; - display: block; - height: 0; - clear: both; - visibility: hidden; } - /* line 85, site.css.sass */ - #yield_content #mainContent .innerContent .timeBar .raceSection .date { - font-family: OpenSansRegular, Arial, Verdana, sans-serif; - font-size: 12px; - margin-left: 35px; } - /* line 91, site.css.sass */ - #yield_content #mainContent .innerContent .timeBar .raceSection .raceTimepoint { - background: url(../images/raceTimepoint-bg.png) left top no-repeat; - width: 29px; - height: 54px; - float: left; - margin: -16px 0 0 -5px; } - /* line 97, site.css.sass */ - #yield_content #mainContent .innerContent .timeBar .raceSection .racePanel { - background: url(../images/racePanel-bg.png) left bottom repeat-x #fcb913; - width: 169px; - min-height: 36px; - margin: 2px 10px 0 -5px; - float: left; - padding: 6px 0 6px 13px; } - /* line 104, site.css.sass */ - #yield_content #mainContent .innerContent .timeBar .raceSection .racePanel .raceName { - font-size: 12px; - font-family: OpenSansBold, Arial, Verdana, sans-serif; - color: white; } - /* line 109, site.css.sass */ - #yield_content #mainContent .innerContent .timeBar .raceSection .racePanel .raceLocation { - font-size: 12px; - font-family: OpenSansRegular, Arial, Verdana, sans-serif; - color: white; } - #yield_content #mainContent .innerContent .timeBar .raceSection a:hover { - text-decoration: none; } - /* line 114, site.css.sass */ - #yield_content #mainContent .innerContent .timeBar .raceDescription { - padding: 4px; - font-family: OpenSansRegular, Arial, Verdana, sans-serif; - font-size: 12px; - font-color: #222222; } - /* line 121, site.css.sass */ - #yield_content #mainContent .articleHeadline { - font-size: 18px; - font-family: OpenSansBold, Arial, Verdana, sans-serif; - color: #222222; - border-bottom: 2px solid #222222; - clear: both; - padding: 0 0 5px; - margin: 0 0 15px; } - /* line 131, site.css.sass */ - #yield_content #mainContent .articleSubheadline { - font-size: 14px; - font-family: OpenSansBold, Arial, Verdana, sans-serif; - color: #222222; - margin: 0 0 6px 0; } - /* line 138, site.css.sass */ - #yield_content #mainContent h4 { - margin: 0 0 10px; - font-size: 12px; - font-family: OpenSansBold, Arial, Verdana, sans-serif; } - /* line 144, site.css.sass */ - #yield_content #spaceBar { - height: 80px; } - -/* line 148, site.css.sass */ -#footer { - color: white; - width: 940px; - height: 20px; - margin: -40px auto 0 auto; - background: url(../images/bkg-footer1.png); - padding: 10px; - font-size: 11px; } - /* line 157, site.css.sass */ - #footer a { - color: white; - font-size: 11px; } - /* line 162, site.css.sass */ - #footer .regularNav { - margin: 0; } - /* line 164, site.css.sass */ - #footer .regularNav li { - display: inline; } +/* line 2, _reset.sass */ +html, body, div, span, applet, object, iframe, +h1, h2, h3, h4, h5, h6, p, blockquote, pre, +a, abbr, acronym, address, big, cite, code, +del, dfn, em, img, ins, kbd, q, s, samp, +small, strike, strong, sub, sup, tt, var, +b, u, i, center, +dl, dt, dd, ol, ul, li, +fieldset, form, label, legend, +table, caption, tbody, tfoot, thead, tr, th, td, +article, aside, canvas, details, embed, +figure, figcaption, footer, header, hgroup, +menu, nav, output, ruby, section, summary, +time, mark, audio, video { + margin: 0; + padding: 0; + border: 0; + font-size: 100%; + font: inherit; + vertical-align: baseline; } + +/* HTML5 display-role reset for older browsers */ +/* line 23, _reset.sass */ +article, aside, details, figcaption, figure, +footer, header, hgroup, menu, nav, section { + display: block; } + +/* line 27, _reset.sass */ +body { + line-height: 1; } + +/* line 30, _reset.sass */ +ol, ul { + list-style: none; } + +/* line 33, _reset.sass */ +blockquote, q { + quotes: none; } + +/* line 36, _reset.sass */ +blockquote:before, blockquote:after, +q:before, q:after { + content: ""; + content: none; } + +/* line 41, _reset.sass */ +table { + border-collapse: collapse; + border-spacing: 0; } + +/* line 7, site.css.sass */ +html { + height: 100%; } + +/* line 10, site.css.sass */ +body { + height: 100%; + color: #222222; + font-size: 14px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: normal; + background-image: url(../images/bg-image.jpg); + background-attachment: fixed; + background-color: #222222; + background-position: 50% 0%; + background-repeat: no-repeat no-repeat; + margin: 0px; + padding: 0px; + line-height: 18px; } + +/* line 27, site.css.sass */ +ul { + font-size: 12px; + padding-left: 0px; + margin-bottom: 25px; + margin-left: 30px; } + +/* line 34, site.css.sass */ +a { + text-decoration: none; + color: #0066b3; } + /* line 38, site.css.sass */ + a:hover { + text-decoration: underline; } + +/* line 42, site.css.sass */ +#yield_content { + width: 950px; + margin: 0 auto; + min-height: 100%; } + #yield_content #logoAndTitlePanel { + height: 52px; + } + #yield_content .sapLogo { + height: 52px; + float: left; + } + #yield_content .headNavigation { + float: right; + margin: 25px 0 0 0; + padding: 0; } + #yield_content .headNavigation a { + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: bold; + color: #fff; } + /* line 47, site.css.sass */ + #yield_content #pageHeadline { + color: white; + margin: 10px 0 10px 0; + font-size: 18px; } + /* line 53, site.css.sass */ + #yield_content #keyVisual { + height: 240px; + position: relative; + background: white; } + /* line 58, site.css.sass */ + #yield_content #keyVisual #infoBox { + width: 502px; + height: 100px; + background: #999999; + position: absolute; + right: 0; + bottom: 0; + padding: 10px; } + /* line 67, site.css.sass */ + #yield_content #keyVisual #infoBox p { + color: white; + font-size: 12px; } + #yield_content #keyVisual #infoBox a { + text-decoration: none; + float: right; + background: #fcb913; + color: white; + font-size: 18px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + padding: 6px 12px 6px 12px; } + + /* line 72, site.css.sass */ + #yield_content #mainContent { + padding: 0 10px 10px 10px; + background: white; } + /* line 79, site.css.sass */ + #yield_content #mainContent .innerContent .timeBar { + background: url(../images/timeBar-bg.png) 0 0 repeat-y; } + /* line 82, site.css.sass */ + #yield_content #mainContent .innerContent .timeBar .raceSection { + margin: 0 0 20px 0; + *display: inline-block; } + /* line 3, _helpers.sass */ + #yield_content #mainContent .innerContent .timeBar .raceSection:after { + content: " "; + display: block; + height: 0; + clear: both; + visibility: hidden; } + /* line 85, site.css.sass */ + #yield_content #mainContent .innerContent .timeBar .raceSection .date { + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-size: 12px; + margin-left: 35px; } + /* line 91, site.css.sass */ + #yield_content #mainContent .innerContent .timeBar .raceSection .raceTimepoint { + background: url(../images/raceTimepoint-bg.png) left top no-repeat; + width: 29px; + height: 54px; + float: left; + margin: -16px 0 0 -5px; } + /* line 97, site.css.sass */ + #yield_content #mainContent .innerContent .timeBar .raceSection .racePanel { + background: url(../images/racePanel-bg.png) left bottom repeat-x #fcb913; + width: 169px; + min-height: 36px; + margin: 2px 10px 0 -5px; + float: left; + padding: 6px 0 6px 13px; } + /* line 104, site.css.sass */ + #yield_content #mainContent .innerContent .timeBar .raceSection .racePanel .raceName { + font-size: 12px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: bold; + color: white; } + /* line 109, site.css.sass */ + #yield_content #mainContent .innerContent .timeBar .raceSection .racePanel .raceLocation { + font-size: 12px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + color: white; } + #yield_content #mainContent .innerContent .timeBar .raceSection a:hover { + text-decoration: none; } + /* line 114, site.css.sass */ + #yield_content #mainContent .innerContent .timeBar .raceDescription { + padding: 4px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-size: 12px; + font-color: #222222; } + /* line 121, site.css.sass */ + #yield_content #mainContent .articleHeadline { + font-size: 18px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: bold; + color: #222222; + border-bottom: 2px solid #222222; + clear: both; + padding: 0 0 5px; + margin: 0 0 15px; } + /* line 131, site.css.sass */ + #yield_content #mainContent .articleSubheadline { + font-size: 14px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: bold; + color: #222222; + margin: 0 0 6px 0; } + /* line 138, site.css.sass */ + #yield_content #mainContent h4 { + margin: 0 0 10px; + font-size: 12px; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: bold; } + /* line 144, site.css.sass */ + #yield_content #spaceBar { + height: 80px; } + +/* line 148, site.css.sass */ +#footer { + color: white; + width: 940px; + height: 20px; + margin: -40px auto 0 auto; + background: url(../images/bkg-footer1.png); + padding: 10px; + font-size: 11px; } + /* line 157, site.css.sass */ + #footer a { + color: white; + font-size: 11px; } + /* line 162, site.css.sass */ + #footer .regularNav { + margin: 0; } + /* line 164, site.css.sass */ + #footer .regularNav li { + display: inline; } diff --git a/java/com.sap.sailing.www/release_notes.css b/java/com.sap.sailing.www/release_notes.css index 130857a5cbc..139b198f944 100644 --- a/java/com.sap.sailing.www/release_notes.css +++ b/java/com.sap.sailing.www/release_notes.css @@ -4,7 +4,8 @@ body{ #yield_content #mainContent .releaseHeadline{ font-size: 24px; - font-family: OpenSansBold, Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; + font-weight: bold; padding: 0 0 5px; margin: 0 0 10px 0; border-bottom: 2px solid #222222; @@ -14,7 +15,7 @@ body{ border-bottom: none; } #yield_content #mainContent .articleSubheadline { - font-family: OpenSansRegular, Arial, Verdana, sans-serif; + font-family: 'Open Sans', Arial, Verdana, sans-serif; } #yield_content #mainContent .releaseNavigation{ margin: 0 0 20px 0; diff --git a/java/com.sap.sailing.www/sailing-fontface-1.0.cache.css b/java/com.sap.sailing.www/sailing-fontface-1.0.cache.css index aba42bafbc1..6eaba7f0c4d 100644 --- a/java/com.sap.sailing.www/sailing-fontface-1.0.cache.css +++ b/java/com.sap.sailing.www/sailing-fontface-1.0.cache.css @@ -29,3 +29,24 @@ font-weight: 300; font-style: normal; } +@font-face { + font-family: 'Open Sans Condensed'; + src: url("fonts/open-sans-condensed-light-webfont.eot"); + src: url("fonts/open-sans-condensed-light-webfont.eot?#iefix") format('embedded-opentype'), url("fonts/open-sans-condensed-light-webfont.woff") format('woff'), url("fonts/open-sans-condensed-light-webfont.ttf") format('truetype'), url("fonts/open-sans-condensed-light-webfont.svg#open_sanslight") format('svg'); + font-weight: 300; + font-style: normal; +} +@font-face { + font-family: 'Open Sans Condensed'; + src: url("fonts/open-sans-condensed-light-italic-webfont.eot"); + src: url("fonts/open-sans-condensed-light-italic-webfont.eot?#iefix") format('embedded-opentype'), url("fonts/open-sans-condensed-light-italic-webfont.woff") format('woff'), url("fonts/open-sans-condensed-light-italic-webfont.ttf") format('truetype'), url("fonts/open-sans-condensed-light-italic-webfont.svg#open_sanslight") format('svg'); + font-weight: 300; + font-style: italic; +} +@font-face { + font-family: 'Open Sans Condensed'; + src: url("fonts/open-sans-condensed-bold-webfont.eot"); + src: url("fonts/open-sans-condensed-bold-webfont.eot?#iefix") format('embedded-opentype'), url("fonts/open-sans-condensed-bold-webfont.woff") format('woff'), url("fonts/open-sans-condensed-bold-webfont.ttf") format('truetype'), url("fonts/open-sans-condensed-bold-webfont.svg#open_sanslight") format('svg'); + font-weight: 700; + font-style: normal; +} 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 bc2f2b10568..916d2e7d9a1 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 @@ -38,6 +38,7 @@ import com.sap.sailing.domain.common.impl.KnotSpeedImpl; import com.sap.sailing.domain.common.impl.MeterDistance; import com.sap.sailing.domain.common.tracking.GPSFixMoving; import com.sap.sailing.domain.leaderboard.Leaderboard; +import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo; import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.LineDetails; import com.sap.sailing.domain.tracking.Maneuver; @@ -607,7 +608,10 @@ public class LeaderboardData extends ExportAction { /** * Create xml elements related to a leg. */ - private Element createLegXML(TrackedLeg trackedLeg, Leaderboard leaderboard, int legCounter, Util.Pair> raceConfidenceAndErrorMessages, Util.Pair> legConfidenceAndErrorMessages) throws NoWindException, IOException, ServletException { + private Element createLegXML(TrackedLeg trackedLeg, Leaderboard leaderboard, int legCounter, + Util.Pair> raceConfidenceAndErrorMessages, + Util.Pair> legConfidenceAndErrorMessages) throws NoWindException, IOException, + ServletException { TimePoint timeSpent = MillisecondsTimePoint.now(); Leg leg = trackedLeg.getLeg(); Element legElement = new Element("leg"); @@ -633,8 +637,8 @@ public class LeaderboardData extends ExportAction { legElement.addContent(competitorElement); continue; } - TimePoint legFinishTime = competitorLeg.getFinishTime(); + final RankingInfo rankingMetricAtLegFinishTime = trackedLeg.getTrackedRace().getRankingMetric().getRankingInfo(legFinishTime); competitorLegDataElement.addContent(createTimedXML("leg_started_time_", competitorLeg.getStartTime())); Util.Pair maximumSpeed = competitorLeg.getMaximumSpeedOverGround(legFinishTime); addNamedElementWithValue(competitorLegDataElement, "maximum_speed_over_ground_in_knots", maximumSpeed != null ? maximumSpeed.getB().getKnots() : -1); @@ -650,9 +654,10 @@ public class LeaderboardData extends ExportAction { legConfidenceAndErrorMessages = updateConfidence("Competitor " + competitor.getName() + " seems to not have finished this leg before end of tracking time.", 0.1, legConfidenceAndErrorMessages); } addNamedElementWithValue(competitorLegDataElement, "time_spend_in_this_leg_as_millis", timeSpentInThisLeg != null ? timeSpentInThisLeg.asMillis() : new MillisecondsDurationImpl(0).asMillis()); - addNamedElementWithValue(competitorLegDataElement, "gap_to_leader_at_finish_in_seconds", competitorLeg.getGapToLeader(legFinishTime, WindPositionMode.LEG_MIDDLE).asSeconds()); - Distance windwardDistanceToOverallLeader = competitorLeg.getWindwardDistanceToOverallLeader(legFinishTime, WindPositionMode.LEG_MIDDLE); - addNamedElementWithValue(competitorLegDataElement, "windward_distance_to_overall_leader_that_has_finished_this_leg_in_meters", windwardDistanceToOverallLeader != null ? windwardDistanceToOverallLeader.getMeters() : 0); + addNamedElementWithValue(competitorLegDataElement, "gap_to_leader_at_finish_in_seconds", competitorLeg.getGapToLeader(legFinishTime, rankingMetricAtLegFinishTime, WindPositionMode.LEG_MIDDLE).asSeconds()); + Distance windwardDistanceToCompetitorFarthestAhead = competitorLeg.getWindwardDistanceToCompetitorFarthestAhead(legFinishTime, WindPositionMode.LEG_MIDDLE, rankingMetricAtLegFinishTime); + // TODO bug1018 decide whether the following field should be renamed to express that it's not the distance to the "leader" but the competitor farthest ahead; discussed with Simon on 2015-05-28; Simon says we leave it like this for now + addNamedElementWithValue(competitorLegDataElement, "windward_distance_to_overall_leader_that_has_finished_this_leg_in_meters", windwardDistanceToCompetitorFarthestAhead != null ? windwardDistanceToCompetitorFarthestAhead.getMeters() : 0); addNamedElementWithValue(competitorLegDataElement, "distance_traveled_in_meters", competitorLeg.getDistanceTraveled(legFinishTime).getMeters()); addNamedElementWithValue(competitorLegDataElement, "average_speed_over_ground_in_knots", competitorLeg.getAverageSpeedOverGround(legFinishTime).getKnots()); diff --git a/java/com.sap.sse.common/META-INF/MANIFEST.MF b/java/com.sap.sse.common/META-INF/MANIFEST.MF index cd2f6d8761e..1086559f24a 100644 --- a/java/com.sap.sse.common/META-INF/MANIFEST.MF +++ b/java/com.sap.sse.common/META-INF/MANIFEST.MF @@ -9,6 +9,7 @@ Export-Package: com.sap.sse.common, com.sap.sse.common.filter, com.sap.sse.common.impl, com.sap.sse.common.mail, + com.sap.sse.common.media, com.sap.sse.common.scalablevalue, com.sap.sse.common.search, com.sap.sse.common.settings, diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/media/AbstractMediaDTO.java b/java/com.sap.sse.common/src/com/sap/sse/common/media/AbstractMediaDTO.java new file mode 100644 index 00000000000..6e0ff8ba07b --- /dev/null +++ b/java/com.sap.sse.common/src/com/sap/sse/common/media/AbstractMediaDTO.java @@ -0,0 +1,118 @@ +package com.sap.sse.common.media; + +import java.io.Serializable; +import java.util.Date; +import java.util.HashSet; + +/** + * Common media data for media items + * + * @author pgtaboada + * + */ +public class AbstractMediaDTO implements Serializable, Comparable { + private static final long serialVersionUID = 1L; + + /** + * TODO: would "caption" be a better name? + */ + private String title; + + /** + * TODO: should we provide a subtitle? + */ + protected String subtitle; + + /** + * This information should be used for chronological ordering + * TODO: Must not be null. We need to find a way to + * migrate data! + */ + protected Date createdAtDate; + + /** + * TODO: initially I though of "author", but I am not sure if we need the author name. But we probably will need + * some place to store copyright information... + */ + protected String copyright; + + private MimeType mimeType; + + /** + * TODO: tags will give us some way to categorize medias. If we pass this information to the frontend, we need some + * nice idea to show display this data. for now it should be something restricted to the backend domain modell + */ + private HashSet tags = new HashSet(); + + /** + * URL pointing to the media data. + */ + private String sourceRef; + + /** + * Default constructor required for GWT serialization. + */ + protected AbstractMediaDTO() { + } + + /** + * Media item with minimal set of information + * + * @param sourceRef + * @param mimeType + */ + public AbstractMediaDTO(String sourceRef, MimeType mimeType, Date createdAtDate) { + this.mimeType = mimeType; + this.sourceRef = sourceRef; + this.createdAtDate = createdAtDate; + } + + public MimeType getMimeType() { + return mimeType; + } + + public String getSourceRef() { + return sourceRef; + } + + public String getTitle() { + return title; + } + + public void setTitle(String title) { + this.title = title; + } + + public HashSet getTags() { + return tags; + } + + public String getSubtitle() { + return subtitle; + } + + public void setSubtitle(String subtitle) { + this.subtitle = subtitle; + } + + public Date getCreatedAtDate() { + return createdAtDate; + } + + public void setCreatedAtDate(Date createdAtDate) { + this.createdAtDate = createdAtDate; + } + + public String getCopyright() { + return copyright; + } + + public void setCopyright(String copyright) { + this.copyright = copyright; + } + + @Override + public int compareTo(AbstractMediaDTO o) { + return createdAtDate.compareTo(o.createdAtDate); + } +} diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/media/ImageMetadataDTO.java b/java/com.sap.sse.common/src/com/sap/sse/common/media/ImageMetadataDTO.java new file mode 100644 index 00000000000..ee72871c032 --- /dev/null +++ b/java/com.sap.sse.common/src/com/sap/sse/common/media/ImageMetadataDTO.java @@ -0,0 +1,42 @@ +package com.sap.sse.common.media; + +import java.util.Date; + + +public class ImageMetadataDTO extends AbstractMediaDTO { + + private static final long serialVersionUID = 1L; + + private int widthInPx; + private int heightInPx; + + protected ImageMetadataDTO() { + } + + /** + * TODO: should we use imagesize, or should we store height and width? For now, this is a convenience method... + * + * @param imageURL + * @param size + */ + public ImageMetadataDTO(String imageURL, ImageSize size, Date createdAtDate) { + super(imageURL, MimeType.image, createdAtDate); + this.widthInPx = size.getWidth(); + this.heightInPx = size.getHeight(); + } + + public ImageMetadataDTO(String imageURL, int heightInPx, int widthInPx, Date createdAtDate) { + super(imageURL, MimeType.image, createdAtDate); + this.widthInPx = widthInPx; + this.heightInPx = heightInPx; + } + + public int getWidthInPx() { + return widthInPx; + } + + public int getHeightInPx() { + return heightInPx; + } + +} diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ImageSize.java b/java/com.sap.sse.common/src/com/sap/sse/common/media/ImageSize.java similarity index 75% rename from java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ImageSize.java rename to java/com.sap.sse.common/src/com/sap/sse/common/media/ImageSize.java index fb1182e2ec9..2ceb6d5b911 100755 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/ImageSize.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/media/ImageSize.java @@ -1,4 +1,4 @@ -package com.sap.sailing.domain.common; +package com.sap.sse.common.media; import java.io.Serializable; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaSubType.java b/java/com.sap.sse.common/src/com/sap/sse/common/media/MediaSubType.java similarity index 80% rename from java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaSubType.java rename to java/com.sap.sse.common/src/com/sap/sse/common/media/MediaSubType.java index 45a7b5eeb0a..14d44d96de3 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaSubType.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/media/MediaSubType.java @@ -1,4 +1,4 @@ -package com.sap.sailing.domain.common.media; +package com.sap.sse.common.media; public enum MediaSubType { ogg, mp4, mpeg, x_aiff, quicktime, aac, webm, youtube, vimeo, unknown; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaType.java b/java/com.sap.sse.common/src/com/sap/sse/common/media/MediaType.java similarity index 57% rename from java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaType.java rename to java/com.sap.sse.common/src/com/sap/sse/common/media/MediaType.java index 308f8d99e15..f4cd39fc14a 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MediaType.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/media/MediaType.java @@ -1,4 +1,4 @@ -package com.sap.sailing.domain.common.media; +package com.sap.sse.common.media; public enum MediaType { audio, video, image, unknown; diff --git a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MimeType.java b/java/com.sap.sse.common/src/com/sap/sse/common/media/MimeType.java similarity index 96% rename from java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MimeType.java rename to java/com.sap.sse.common/src/com/sap/sse/common/media/MimeType.java index 377b9f20b9e..ce1bc2ec070 100644 --- a/java/com.sap.sailing.domain.common/src/com/sap/sailing/domain/common/media/MimeType.java +++ b/java/com.sap.sse.common/src/com/sap/sse/common/media/MimeType.java @@ -1,4 +1,4 @@ -package com.sap.sailing.domain.common.media; +package com.sap.sse.common.media; public enum MimeType { diff --git a/java/com.sap.sse.common/src/com/sap/sse/common/media/VideoMetadataDTO.java b/java/com.sap.sse.common/src/com/sap/sse/common/media/VideoMetadataDTO.java new file mode 100644 index 00000000000..72207aefd00 --- /dev/null +++ b/java/com.sap.sse.common/src/com/sap/sse/common/media/VideoMetadataDTO.java @@ -0,0 +1,45 @@ +package com.sap.sse.common.media; + +import java.util.Date; + +public class VideoMetadataDTO extends AbstractMediaDTO { + + private static final long serialVersionUID = 1L; + + /** + * TODO: Do we really need video length? Should it be mandatory? + */ + private int lengthInSeconds; + + /** + * URL Reference to thumbnail image. This information works as override for youtube videos or as missing thumbnail + * information for other formats. It can be either a link to thumbnail or even be a data/url contaning the base64 + * encoded image. + */ + private String thumbnailRef; + + + protected VideoMetadataDTO() { + } + + + public VideoMetadataDTO(String url, MimeType mimeType, Date createdAtDate) { + super(url, mimeType, createdAtDate); + } + + public int getLengthInSeconds() { + return lengthInSeconds; + } + + public void setLengthInSeconds(int lengthInSeconds) { + this.lengthInSeconds = lengthInSeconds; + } + + public void setThumbnailRef(String thumbnailRef) { + this.thumbnailRef = thumbnailRef; + } + + public String getThumbnailRef() { + return thumbnailRef; + } +} diff --git a/java/com.sap.sse.gwt.theme/src/main/java/com/sap/sse/gwt/theme/client/resources/ThemeResources.java b/java/com.sap.sse.gwt.theme/src/main/java/com/sap/sse/gwt/theme/client/resources/ThemeResources.java index d6026a3ab87..de02ba3ead6 100644 --- a/java/com.sap.sse.gwt.theme/src/main/java/com/sap/sse/gwt/theme/client/resources/ThemeResources.java +++ b/java/com.sap.sse.gwt.theme/src/main/java/com/sap/sse/gwt/theme/client/resources/ThemeResources.java @@ -20,6 +20,7 @@ public interface ThemeResources extends ClientBundle { String navbar_buttonhidden(); String navbar_buttonactive(); String button(); + String buttontextonly(); String buttonsmall(); String buttonstrong(); String buttoninactive(); @@ -41,6 +42,23 @@ public interface ThemeResources extends ClientBundle { String lightbox_overlay(); String lightbox_content(); String lightbox_close(); + String spacer(); + String spacersmall(); + String spacermediumsmall(); + String spacermedium(); + String spacermediumbig(); + String spacerbig(); + String spacermargin(); + String spacermargintopsmall(); + String spacermargintopmediumsmall(); + String spacermargintopmedium(); + String spacermargintopmediumbig(); + String spacermargintopbig(); + String spacermarginbottomsmall(); + String spacermarginbottommediumsmall(); + String spacermarginbottommedium(); + String spacermarginbottommediumbig(); + String spacermarginbottombig(); String stickyheader(); String slideto(); String slidetonav(); diff --git a/java/com.sap.sse.gwt.theme/src/main/java/com/sap/sse/gwt/theme/client/resources/main.gss b/java/com.sap.sse.gwt.theme/src/main/java/com/sap/sse/gwt/theme/client/resources/main.gss index 076037ca0cb..e09d28752bc 100644 --- a/java/com.sap.sse.gwt.theme/src/main/java/com/sap/sse/gwt/theme/client/resources/main.gss +++ b/java/com.sap.sse.gwt.theme/src/main/java/com/sap/sse/gwt/theme/client/resources/main.gss @@ -129,6 +129,14 @@ figcaption { outline: none; text-decoration: none; } +.buttontextonly { + color: #333; + background: none; + -webkit-transition: none; + -moz-transition: none; + transition: none; + font-weight: normal; +} .buttonsmall { visibility: inherit; } @@ -338,8 +346,7 @@ figcaption { transform: translate(-50%, -50%); } .mainsection { - padding-top: 5.333333333333333em; - padding-bottom: 5.333333333333333em; + padding: 5.333333333333333em 0; } .mainsection_header { text-align: center; @@ -400,6 +407,61 @@ figcaption { cursor: pointer; z-index: 2; } +.spacer { + width: 100%; + border: 0; + margin: 0; + padding: 0; + display: block; +} +.spacersmall { + height: 1em; +} +.spacermediumsmall { + height: 1.333333333333333em; +} +.spacermedium { + height: 2em; +} +.spacermediumbig { + height: 4em; +} +.spacerbig { + height: 5.333333333333333em; +} +.spacermargin { + font-size: inherit; +} +.spacermargintopsmall { + margin-top: 1em; +} +.spacermargintopmediumsmall { + margin-top: 1.333333333333333em; +} +.spacermargintopmedium { + margin-top: 2em; +} +.spacermargintopmediumbig { + margin-top: 4em; +} +.spacermargintopbig { + margin-top: 5.333333333333333em; +} +.spacermarginbottomsmall { + margin-bottom: 1em; +} +.spacermarginbottommediumsmall { + margin-bottom: 1.333333333333333em; +} +.spacermarginbottommedium { + margin-bottom: 2em; +} +.spacermarginbottommediumbig { + margin-bottom: 4em; +} +.spacermarginbottombig { + margin-bottom: 5.333333333333333em; +} .stickyheader { background: inherit; } diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java index 342a878a890..77219290e4c 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/URLEncoder.java @@ -1,26 +1,26 @@ -package com.sap.sse.gwt.client; - -import com.google.gwt.http.client.URL; - -/** - * A very basic URL encoder that also works in a GWT environment where java.net.URLEncoder is not available. - * It is based on {@link URL#encode(String)} and adds encoding for the backslash (\) character. - * - * @author Axel Uhl (D043530) - * - */ -public class URLEncoder { - - private URLEncoder() { } - - private final static char[] charsToReplace = { '\'', '(', ')', '"', '[', ']', '{', '}', '<', '>', '|' }; - - public static String encode(String url) { - String nearlyEncoded = URL.encode(url); - for (char c : charsToReplace) { - nearlyEncoded = nearlyEncoded.replaceAll("\\" + Character.toString(c), "%" + Integer.toHexString(c)); - } - return nearlyEncoded; - } - -} +package com.sap.sse.gwt.client; + +import com.google.gwt.http.client.URL; + +/** + * A very basic URL encoder that also works in a GWT environment where java.net.URLEncoder is not available. + * It is based on {@link URL#encode(String)} and adds encoding for the backslash (\) character. + * + * @author Axel Uhl (D043530) + * + */ +public class URLEncoder { + + private URLEncoder() { } + + private final static char[] charsToReplace = { '\'', '(', ')', '"', '[', ']', '{', '}', '<', '>', '|', '+' }; + + public static String encode(String url) { + String nearlyEncoded = URL.encode(url); + for (char c : charsToReplace) { + nearlyEncoded = nearlyEncoded.replaceAll("\\" + Character.toString(c), "%" + Integer.toHexString(c)); + } + return nearlyEncoded; + } + +} diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java index b6b1b7cab02..09c87896b97 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/carousel/ImageCarousel.java @@ -1,5 +1,8 @@ package com.sap.sse.gwt.client.controls.carousel; +import java.util.LinkedList; +import java.util.List; + import com.google.gwt.core.client.GWT; import com.google.gwt.core.client.Scheduler; import com.google.gwt.dom.client.DivElement; @@ -11,6 +14,7 @@ import com.google.gwt.safehtml.shared.UriUtils; import com.google.gwt.uibinder.client.UiBinder; import com.google.gwt.user.client.Command; import com.google.gwt.user.client.ui.Widget; +import com.sap.sse.common.media.ImageMetadataDTO; /** * Image carousel that uses the open source "slick carousel". @@ -48,10 +52,11 @@ import com.google.gwt.user.client.ui.Widget; * * Created by pgtaboada on 10.11.14. */ -public class ImageCarousel extends Widget { +public class ImageCarousel extends Widget { private static SlickSliderUiBinder ourUiBinder = GWT.create(SlickSliderUiBinder.class); + private LinkedList currentImages = new LinkedList<>(); /** * slick slider property: dots */ @@ -81,9 +86,12 @@ public class ImageCarousel extends Widget { * The height of the images */ private int imagesHeight = 300; + private int currentSlideIndex = 0; private final String uniqueId; + private FullscreenViewer fsViewer; + /** * Widget constructor */ @@ -95,12 +103,38 @@ public class ImageCarousel extends Widget { init(); } + public void onClick() { + GWT.log("Current slide: " + currentSlideIndex); + if (fsViewer != null) { + fsViewer.show(currentImages.get(currentSlideIndex), currentImages); + } + } + /** * JSNI wrapper that does setup the slider * * @param uniqueId */ - native void setupSlider(ImageCarousel sliderReference) /*-{ + native void setupSlider(ImageCarousel sliderReference) /*-{ + + var slider = $wnd + .$('.' + + (sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::uniqueId)); + + slider + .on( + 'click', + '.slick-slide', + function() { + sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::onClick()(); + }); + + slider + .on( + 'afterChange', + function(event, index) { + sliderReference.@com.sap.sse.gwt.client.controls.carousel.ImageCarousel::currentSlideIndex = index.currentSlide; + }); $wnd .$( @@ -136,8 +170,12 @@ public class ImageCarousel extends Widget { * * @param url */ - public void addImage(String url, int height, int width) { - + public void addImage(TYPE image) { + currentImages.add(image); + String url = image.getSourceRef(); + int height = image.getHeightInPx(); + int width = image.getWidthInPx(); + DivElement imageHolder = Document.get().createDivElement(); ImageElement imageElement = Document.get().createImageElement(); imageElement.setAttribute("data-lazy", UriUtils.fromString(url).asString()); @@ -209,7 +247,7 @@ public class ImageCarousel extends Widget { /** * UiBinder interface */ - interface SlickSliderUiBinder extends UiBinder { + interface SlickSliderUiBinder extends UiBinder> { } /** @@ -242,7 +280,7 @@ public class ImageCarousel extends Widget { * Apply slick js to constructed dom tree. */ private void init() { - final ImageCarousel reference = this; + final ImageCarousel reference = this; Scheduler.get().scheduleDeferred(new Command() { @@ -265,4 +303,13 @@ public class ImageCarousel extends Widget { }); } + public void registerFullscreenViewer(FullscreenViewer fsViewer) { + this.fsViewer = fsViewer; + } + + public interface FullscreenViewer { + + public void show(TYPE selectedImage, List imageList); + } + } \ No newline at end of file diff --git a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/slider/SliderBar.css b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/slider/SliderBar.css index 324c623cb1f..6b8cf3144e0 100644 --- a/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/slider/SliderBar.css +++ b/java/com.sap.sse.gwt/src/com/sap/sse/gwt/client/controls/slider/SliderBar.css @@ -1,87 +1,90 @@ -.gwt-SliderBar-shell { - width: 100%; - height: 50px; -} - -.gwt-SliderBar-shell .gwt-SliderBar-line { - background: #333; - height: 12px; - width: 100%; - top: 18px; - overflow: hidden; -} - -.gwt-SliderBar-shell .gwt-SliderBar-line-disabled { - border: 1px solid gray; -} - -.gwt-SliderBar-shell .gwt-SliderBar-knob { - z-index: 1; - cursor: pointer; - top: 7px; -} - -.gwt-SliderBar-shell .gwt-SliderBar-tick { - top: 13px; - width: 2px; - height: 5px; - background: #333; - overflow: hidden; -} - -.gwt-SliderBar-shell:focus{ - outline: none; -} - -.gwt-SliderBar-shell .gwt-SliderBar-mark { - top: 13px; - width: 24px; - height: 24px; - background: #fcbb22; - overflow: hidden; - border-radius: 15px; -} - -.gwt-SliderBar-shell .gwt-SliderBar-mark-tooClose { - top: 12px; - width: 16px; - height: 16px; - background: #fcbb22; - overflow: hidden; - border-radius: 15px; -} - -.gwt-SliderBar-shell .gwt-SliderBar-tick-disabled { - background: gray; -} - -.gwt-SliderBar-shell .gwt-SliderBar-ticklabel { - font-size: 12px; - cursor: default; - font-family: 'OpenSansSemiBold',Arial,Verdana,sans-serif; - top: 0; -} - -.gwt-SliderBar-shell .gwt-SliderBar-markerlabel { - top: 20px; - font-size: 12px; - color: #333; - cursor: default; - font-family: 'OpenSansSemiBold',Arial,Verdana,sans-serif; -} - -.gwt-SliderBar-shell .gwt-SliderBar-markerlabel-tooClose { - top: 13px; - font-size: 8px; - color: #333; - cursor: default; - font-family: 'OpenSansSemiBold',Arial,Verdana,sans-serif; -} - -.gwt-SliderBar-shell-focused { - -} - -.gwt-SliderBar-shell .gwt-SliderBar-line-sliding { - cursor: pointer; -} +.gwt-SliderBar-shell { + width: 100%; + height: 50px; +} + +.gwt-SliderBar-shell .gwt-SliderBar-line { + background: #333; + height: 12px; + width: 100%; + top: 18px; + overflow: hidden; +} + +.gwt-SliderBar-shell .gwt-SliderBar-line-disabled { + border: 1px solid gray; +} + +.gwt-SliderBar-shell .gwt-SliderBar-knob { + z-index: 1; + cursor: pointer; + top: 7px; +} + +.gwt-SliderBar-shell .gwt-SliderBar-tick { + top: 13px; + width: 2px; + height: 5px; + background: #333; + overflow: hidden; +} + +.gwt-SliderBar-shell:focus{ + outline: none; +} + +.gwt-SliderBar-shell .gwt-SliderBar-mark { + top: 13px; + width: 24px; + height: 24px; + background: #fcbb22; + overflow: hidden; + border-radius: 15px; +} + +.gwt-SliderBar-shell .gwt-SliderBar-mark-tooClose { + top: 12px; + width: 16px; + height: 16px; + background: #fcbb22; + overflow: hidden; + border-radius: 15px; +} + +.gwt-SliderBar-shell .gwt-SliderBar-tick-disabled { + background: gray; +} + +.gwt-SliderBar-shell .gwt-SliderBar-ticklabel { + font-size: 12px; + cursor: default; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; + top: 0; +} + +.gwt-SliderBar-shell .gwt-SliderBar-markerlabel { + top: 20px; + font-size: 12px; + color: #333; + cursor: default; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; +} + +.gwt-SliderBar-shell .gwt-SliderBar-markerlabel-tooClose { + top: 13px; + font-size: 8px; + color: #333; + cursor: default; + font-family: 'Open Sans',Arial,Verdana,sans-serif; + font-weight: 600; +} + +.gwt-SliderBar-shell-focused { + +} + +.gwt-SliderBar-shell .gwt-SliderBar-line-sliding { + cursor: pointer; +} diff --git a/java/target/configuration/jetty/etc/realm.properties b/java/target/configuration/jetty/etc/realm.properties index de57f0d5d15..c5d89e18f9b 100644 --- a/java/target/configuration/jetty/etc/realm.properties +++ b/java/target/configuration/jetty/etc/realm.properties @@ -28,4 +28,4 @@ steffen: steffen,spectator,moderator,administrator jacob@sejlsport.dk: !hsf/sf67, eventmanager m.welte@stg-academy.org: mwelte, eventmanager e.anders@stg-academy.org: eanders, eventmanager -siegmund@ubilabs.net: psiegmund,spectator,moderator,administrator +siegmund@ubilabs.net: psiegmund,spectator,moderator,administrator