Merge branch 'master' into bug3881

Conflicts:
	java/com.sap.sailing.www/release_notes_admin.html
This commit is contained in:
Steffen Schaefer committed 2016-10-24 10:10:37 +02:00
commit 5428f541a2
162 files changed
+2674 -1241

No files matched your search

@@ -161,7 +161,7 @@ public class StartAnalysisCard extends Composite implements HasWidgets, StartAna
final RaceMapSettings raceMapSettings = new RaceMapSettings(raceMapZoomSettings, getHelpLineSettings(),
defaultRaceMapSettings.getTransparentHoverlines(), defaultRaceMapSettings.getHoverlineStrokeWeight(),
startAnalysisDTO.tailLenghtInMilliseconds, defaultRaceMapSettings.isWindUp(),
defaultRaceMapSettings.getBuoyZoneRadiusInMeters(), defaultRaceMapSettings.isShowOnlySelectedCompetitors(),
defaultRaceMapSettings.getBuoyZoneRadius(), defaultRaceMapSettings.isShowOnlySelectedCompetitors(),
defaultRaceMapSettings.isShowSelectedCompetitorsInfo(), defaultRaceMapSettings.isShowWindStreamletColors(),
defaultRaceMapSettings.isShowWindStreamletOverlay(), defaultRaceMapSettings.isShowSimulationOverlay(),
defaultRaceMapSettings.isShowMapControls(), defaultRaceMapSettings.getManeuverTypesToShow(),
@@ -2,6 +2,9 @@ package com.sap.sailing.datamining.shared;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.LegType;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.impl.CentralAngleDistance;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
@@ -16,6 +19,9 @@ public final class SailingDataMiningSerializationDummy implements SerializationD
private CentralAngleDistance centralAngleDistance;
private MeterDistance meterDistance;
private NauticalMileDistance nauticalMileDistance;
private ManeuverType maneuverType;
private Tack tack;
private NauticalSide nauticalSide;
private SailingDataMiningSerializationDummy() { }
@@ -0,0 +1,86 @@
package com.sap.sailing.datamining.impl.data;
import static org.hamcrest.CoreMatchers.is;
import static org.hamcrest.CoreMatchers.nullValue;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import com.sap.sse.datamining.data.Cluster;
import com.sap.sse.datamining.impl.data.ClusterWithLowerAndUpperBoundaries;
import com.sap.sse.datamining.impl.data.ClusterWithSingleBoundary;
import com.sap.sse.datamining.impl.data.ComparableClusterBoundary;
import com.sap.sse.datamining.impl.data.ComparisonStrategy;
// TODO Move to sse bundle, after 49er analysis
public class TestLinearDoubleClusterGroup {
private LinearDoubleClusterGroup hardPercentageClusterGroup;
private LinearDoubleClusterGroup softLinearClusterGroup;
@Before
public void initialize() {
hardPercentageClusterGroup = new LinearDoubleClusterGroup(0.0, 1.0, 0.1, true);
softLinearClusterGroup = new LinearDoubleClusterGroup(0, 100, 10, false);
}
@Test
public void testGetClusterForValueOutOfBounds() {
Cluster<Double> cluster = hardPercentageClusterGroup.getClusterFor(-0.01);
assertThat(cluster, nullValue());
cluster = hardPercentageClusterGroup.getClusterFor(1.01);
assertThat(cluster, nullValue());
cluster = softLinearClusterGroup.getClusterFor(-0.1);
Cluster<Double> expectedCluster = createCluster(softLinearClusterGroup.getStepSize(), ComparisonStrategy.LOWER_THAN);
assertThat(cluster, is(expectedCluster));
cluster = softLinearClusterGroup.getClusterFor(100.1);
expectedCluster = createCluster(softLinearClusterGroup.getUpperGroupBound() - softLinearClusterGroup.getStepSize(), ComparisonStrategy.GREATER_EQUALS_THAN);
assertThat(cluster, is(expectedCluster));
}
@Test
public void testGetClusterForBoundaryValues() {
Cluster<Double> cluster = hardPercentageClusterGroup.getClusterFor(0.0);
Cluster<Double> expectedCluster = createCluster(hardPercentageClusterGroup.getLowerGroupBound(), ComparisonStrategy.GREATER_EQUALS_THAN,
hardPercentageClusterGroup.getLowerGroupBound() + hardPercentageClusterGroup.getStepSize(), ComparisonStrategy.LOWER_THAN);
assertThat(cluster, is(expectedCluster));
cluster = hardPercentageClusterGroup.getClusterFor(1.0);
expectedCluster = createCluster(hardPercentageClusterGroup.getUpperGroupBound() - hardPercentageClusterGroup.getStepSize(), ComparisonStrategy.GREATER_EQUALS_THAN,
hardPercentageClusterGroup.getUpperGroupBound(), ComparisonStrategy.LOWER_EQUALS_THAN);
assertThat(cluster, is(expectedCluster));
cluster = softLinearClusterGroup.getClusterFor(0.0);
expectedCluster = createCluster(softLinearClusterGroup.getStepSize(), ComparisonStrategy.LOWER_THAN);
assertThat(cluster, is(expectedCluster));
cluster = softLinearClusterGroup.getClusterFor(100.0);
expectedCluster = createCluster(softLinearClusterGroup.getUpperGroupBound() - softLinearClusterGroup.getStepSize(), ComparisonStrategy.GREATER_EQUALS_THAN);
assertThat(cluster, is(expectedCluster));
}
@Test
public void testGetClusterFor() {
LinearDoubleClusterGroup clusterGroup = softLinearClusterGroup;
double value = (clusterGroup.getUpperGroupBound() + clusterGroup.getStepSize()) / 2;
Cluster<Double> cluster = clusterGroup.getClusterFor(value);
Cluster<Double> expectedCluster = createCluster(clusterGroup.getUpperGroupBound() / 2, ComparisonStrategy.GREATER_EQUALS_THAN,
(clusterGroup.getUpperGroupBound() / 2) + clusterGroup.getStepSize(), ComparisonStrategy.LOWER_THAN);
assertThat(cluster, is(expectedCluster));
}
private Cluster<Double> createCluster(double lowerBoundValue, ComparisonStrategy lowerComparisonStrategy, double upperBoundValue, ComparisonStrategy upperComparisonStrategy) {
ComparableClusterBoundary<Double> lowerBound = new ComparableClusterBoundary<>(lowerBoundValue, lowerComparisonStrategy);
ComparableClusterBoundary<Double> upperBound = new ComparableClusterBoundary<>(upperBoundValue, upperComparisonStrategy);
return new ClusterWithLowerAndUpperBoundaries<>(lowerBound, upperBound);
}
private Cluster<Double> createCluster(double boundValue, ComparisonStrategy comparisonStrategy) {
return new ClusterWithSingleBoundary<>(new ComparableClusterBoundary<>(boundValue, comparisonStrategy));
}
}
@@ -0,0 +1,30 @@
package com.sap.sailing.datamining.impl.data;
import static org.hamcrest.CoreMatchers.is;
import static org.junit.Assert.assertThat;
import org.junit.Before;
import org.junit.Test;
import com.sap.sse.datamining.data.Cluster;
import com.sap.sse.datamining.data.ClusterGroup;
public class TestPercentageClusterFormatter {
private ClusterGroup<Double> percentageClusterGroup;
private PercentageClusterFormatter formatter;
@Before
public void initialize() {
percentageClusterGroup = new LinearDoubleClusterGroup(0.0, 1.0, 0.1, true);
formatter = new PercentageClusterFormatter();
}
@Test
public void testFormat() {
Cluster<Double> cluster = percentageClusterGroup.getClusterFor(0.05);
String formattedCluster = formatter.format(cluster);
assertThat(formattedCluster, is("[0% - 10%["));
}
}
@@ -24,12 +24,14 @@ GPSFixSailingDomainRetrieverChain=GPS-Fixes
MarkPassingSailingDomainRetrieverChain=Mark Passings
RaceResultSailingDomainRetrieverChain=Race Results
RaceOfCompetitorSailingDomainRetrieverChain=Races of Competitors
ManeuverSailingDomainRetrieverChain=Maneuver
LeaderboardGroup=Leaderboard Group
Leaderboard=Leaderboard
Leg=Leg
MarkPassing=Mark Passing
LegOfCompetitor=Leg of competitor
GpsFix=GPS-Fix
Maneuver=Maneuver
WindStrengthInBeaufort=Wind Strength in Beaufort
Bft0=Calm
Bft1=Light air
@@ -45,11 +47,13 @@ Bft10=Storm
Bft11=Violent storm
Bft12=Hurricane
RelativeScore=Relative Score (0 best; 1 worst)
RelativeScoreInPercent=Relative Score in %
RelativeScoreInRaceInPercent=Relative Score in Race in %
AbsoluteRank=Absolute Rank
WindSpeedInBeaufort=Wind speed in Bft
DistanceAtStart=Distance to start at start
SpeedWhenStarting=Speed when starting
SpeedTenSecondsBeforeStart=Speed 10s before start
WindSpeedInBeaufort=Wind Speed in Bft
DistanceAtStart=Distance to Start at Start
SpeedWhenStarting=Speed at Start
SpeedTenSecondsBeforeStart=Speed 10s Before Start
RankAtFirstMark=Rank at First Mark
NumberOfManeuvers=Number of Maneuvers
NumberOfTacks=Number of Tacks
@@ -62,4 +66,19 @@ NumberOfWins=Number of Wins
RankGainsOrLossesBetweenFirstMarkAndFinish=Rank Gains or Losses (1st Mark -> Finish)
IsTracked=Is Tracked
NumberOfCompetitorFixes=Number of Competitor Fixes
NumberOfMarkFixes=Number of Mark Fixes
NumberOfMarkFixes=Number of Mark Fixes
ManeuverType=Maneuver Type
Tack=Tack
SpeedBefore=Speed Before Maneuver
SpeedAfter=Speed After Maneuver
DirectionChange=Direction Change in Degrees
ManeuverLoss=Maneuver Loss
Waypoint=Waypoint
PassingSide=Passing Side
TackAtStart=Tack at Start
SpeedTenSecondsAfterStart=Speed 10s After Start
RankThirtySecondsAfterStart=Rank 30s After Start
WindwardDistanceToAdvantageousEndOfLine=Windward Distance to Advantageous End of Line
RankAfterHalfOfTheFirstLeg=Rank after the half of the first leg
AdvantageousEndOfLine=Advantageous End of Line
TimeSpentInSeconds=Time Spent in Seconds
@@ -22,12 +22,14 @@ GPSFixSailingDomainRetrieverChain=GPS-Fixes
MarkPassingSailingDomainRetrieverChain=Tonnenrundungen
RaceResultSailingDomainRetrieverChain=Rennergebnisse
RaceOfCompetitorSailingDomainRetrieverChain=Rennen eines Teilnehmers
ManeuverSailingDomainRetrieverChain=Manöver
LeaderboardGroup=Ranglisten Gruppe
Leaderboard=Rangliste
Leg=Schenkel
MarkPassing=Tonnenrundung
LegOfCompetitor=Schenkel eines Teilnehmers
GpsFix=GPS-Fix
Maneuver=Manöver
Speed=Geschwindigkeit
SpeedTenSecondsBefore=Geschwindigkeit 10s vorher
WindStrengthInBeaufort=Windstärke in Beaufort
@@ -45,6 +47,8 @@ Bft10=schwerer Sturm
Bft11=orkanartiger Sturm
Bft12=Orkan
RelativeScore=Relative Wertung (0 am besten; 1 am schlechtesten)
RelativeScoreInPercent=Relative Wertung in %
RelativeScoreInRaceInPercent=Relative Wertung im Rennen in %
AbsoluteRank=Absolute Platzierung
WindSpeedInBeaufort=Windgeschwindigkeit in Bft.
DistanceAtStart=Distanz zur Startlinie zum Startzeitpunkt
@@ -62,4 +66,19 @@ NumberOfWins=Anzahl der Siege
RankGainsOrLossesBetweenFirstMarkAndFinish=Positions-Verluste oder -Gewinne (1ter Wegpunkt -> Ziel)
IsTracked=Ist getracked
NumberOfCompetitorFixes=Anzahl der Teilnehmer-Fixes
NumberOfMarkFixes=Anzahl der Tonnen-Fixes
NumberOfMarkFixes=Anzahl der Tonnen-Fixes
ManeuverType=Art des Manövers
Tack=Bug
SpeedBefore=Geschwindigkeit bevor dem Manöver
SpeedAfter=Geschwindigkeit nach dem Manöver
DirectionChange=Richtungsänderung in Grad
ManeuverLoss=Manöververlust
Waypoint=Wegpunkt
PassingSide=Passierende Seite
TackAtStart=Startbug
SpeedTenSecondsAfterStart=Geschwindigkeit 10s nach dem Startzeitpunkt
RankThirtySecondsAfterStart=Absolute Platzierung 30s nach dem Startzeitpunkt
WindwardDistanceToAdvantageousEndOfLine=Windwärtige Distanz zum Bevorteilten Ende der Startlinie
RankAfterHalfOfTheFirstLeg=Platzierung nach der Hälfte des ersten Schenkels
AdvantageousEndOfLine=Bevorteiltes Ende der Startlinie
TimeSpentInSeconds=Benötigte Zeit in Sekunden
@@ -10,6 +10,7 @@ import org.osgi.framework.BundleContext;
import com.sap.sailing.datamining.data.HasGPSFixContext;
import com.sap.sailing.datamining.data.HasLeaderboardContext;
import com.sap.sailing.datamining.data.HasLeaderboardGroupContext;
import com.sap.sailing.datamining.data.HasManeuverContext;
import com.sap.sailing.datamining.data.HasMarkPassingContext;
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
import com.sap.sailing.datamining.data.HasRaceResultOfCompetitorContext;
@@ -22,11 +23,9 @@ import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceMa
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceMedianAggregationProcessor;
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceMinAggregationProcessor;
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceSumAggregationProcessor;
import com.sap.sailing.domain.common.Speed;
import com.sap.sse.datamining.DataSourceProvider;
import com.sap.sse.datamining.components.AggregationProcessorDefinition;
import com.sap.sse.datamining.components.DataRetrieverChainDefinition;
import com.sap.sse.datamining.data.ClusterGroup;
import com.sap.sse.datamining.impl.AbstractDataMiningActivatorWithPredefinedQueries;
import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO;
import com.sap.sse.datamining.shared.impl.PredefinedQueryIdentifier;
@@ -82,6 +81,7 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries
internalClasses.add(HasTrackedLegContext.class);
internalClasses.add(HasTrackedLegOfCompetitorContext.class);
internalClasses.add(HasGPSFixContext.class);
internalClasses.add(HasManeuverContext.class);
internalClasses.add(HasMarkPassingContext.class);
internalClasses.add(HasRaceOfCompetitorContext.class);
internalClasses.add(HasLeaderboardGroupContext.class);
@@ -124,9 +124,9 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries
dataSourceProviders = new HashSet<>();
dataSourceProviders.add(new RacingEventServiceProvider(context));
}
public static ClusterGroup<Speed> getWindStrengthInBeaufortClusterGroup() {
return clusterGroups.getWindStrengthInBeaufortCluster();
public static SailingClusterGroups getClusterGroups() {
return clusterGroups;
}
public static Activator getDefault() {
@@ -3,6 +3,9 @@ package com.sap.sailing.datamining;
import java.util.ArrayList;
import java.util.Collection;
import com.sap.sailing.datamining.data.ClusterFormatter;
import com.sap.sailing.datamining.impl.data.LinearDoubleClusterGroup;
import com.sap.sailing.datamining.impl.data.PercentageClusterFormatter;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
import com.sap.sse.datamining.data.Cluster;
@@ -17,7 +20,10 @@ import com.sap.sse.datamining.impl.data.LocalizedCluster;
public class SailingClusterGroups {
private final ClusterGroup<Speed> windStrengthInBeaufortCluster;
private final ClusterGroup<Speed> windStrengthInBeaufortClusterGroup;
private final ClusterGroup<Double> percentageClusterGroup;
private final ClusterFormatter<Double> percentageClusterFormatter;
public SailingClusterGroups() {
Collection<Cluster<Speed>> clusters = new ArrayList<>();
@@ -98,11 +104,22 @@ public class SailingClusterGroups {
lowerBound = new ComparableClusterBoundary<Speed>(lowerBoundWindSpeed, ComparisonStrategy.GREATER_EQUALS_THAN);
clusters.add(new LocalizedCluster<Speed>("Bft12", new ClusterWithSingleBoundary<Speed>(lowerBound)));
windStrengthInBeaufortCluster = new FixClusterGroup<Speed>(clusters);
windStrengthInBeaufortClusterGroup = new FixClusterGroup<Speed>(clusters);
percentageClusterGroup = new LinearDoubleClusterGroup(0.0, 1.0, 0.1, true);
percentageClusterFormatter = new PercentageClusterFormatter();
}
public ClusterGroup<Speed> getWindStrengthInBeaufortCluster() {
return windStrengthInBeaufortCluster;
public ClusterGroup<Speed> getWindStrengthInBeaufortClusterGroup() {
return windStrengthInBeaufortClusterGroup;
}
public ClusterGroup<Double> getPercentageClusterGroup() {
return percentageClusterGroup;
}
public ClusterFormatter<Double> getPercentageClusterFormatter() {
return percentageClusterFormatter;
}
}
@@ -5,6 +5,7 @@ import java.util.Collection;
import com.sap.sailing.datamining.data.HasGPSFixContext;
import com.sap.sailing.datamining.data.HasLeaderboardContext;
import com.sap.sailing.datamining.data.HasManeuverContext;
import com.sap.sailing.datamining.data.HasMarkPassingContext;
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
import com.sap.sailing.datamining.data.HasRaceResultOfCompetitorContext;
@@ -15,6 +16,7 @@ import com.sap.sailing.datamining.impl.components.CompetitorOfRaceInLeaderboardR
import com.sap.sailing.datamining.impl.components.GPSFixRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.LeaderboardGroupRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.LeaderboardRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.ManeuverRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.MarkPassingRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.RaceOfCompetitorRetrievalProcessor;
import com.sap.sailing.datamining.impl.components.TrackedLegOfCompetitorRetrievalProcessor;
@@ -48,11 +50,6 @@ public class SailingDataRetrievalChainDefinitions {
trackedRaceRetrieverChainDefinition.endWith(LeaderboardRetrievalProcessor.class, TrackedRaceRetrievalProcessor.class,
HasTrackedRaceContext.class, "Race");
dataRetrieverChainDefinitions.add(trackedRaceRetrieverChainDefinition);
final DataRetrieverChainDefinition<RacingEventService, HasMarkPassingContext> markPassingRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
trackedRaceRetrieverChainDefinition, HasMarkPassingContext.class, "MarkPassingSailingDomainRetrieverChain");
markPassingRetrieverChainDefinition.endWith(TrackedRaceRetrievalProcessor.class, MarkPassingRetrievalProcessor.class, HasMarkPassingContext.class, "MarkPassing");
dataRetrieverChainDefinitions.add(markPassingRetrieverChainDefinition);
final DataRetrieverChainDefinition<RacingEventService, HasRaceOfCompetitorContext> raceOfCompetitorRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
trackedRaceRetrieverChainDefinition, HasRaceOfCompetitorContext.class, "RaceOfCompetitorSailingDomainRetrieverChain");
@@ -71,6 +68,17 @@ public class SailingDataRetrievalChainDefinitions {
gpsFixRetrieverChainDefinition.endWith(TrackedLegOfCompetitorRetrievalProcessor.class, GPSFixRetrievalProcessor.class,
HasGPSFixContext.class, "GpsFix");
dataRetrieverChainDefinitions.add(gpsFixRetrieverChainDefinition);
final DataRetrieverChainDefinition<RacingEventService, HasManeuverContext> maneuverRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
legOfCompetitorRetrieverChainDefinition, HasManeuverContext.class, "ManeuverSailingDomainRetrieverChain");
maneuverRetrieverChainDefinition.endWith(TrackedLegOfCompetitorRetrievalProcessor.class, ManeuverRetrievalProcessor.class,
HasManeuverContext.class, "Maneuver");
dataRetrieverChainDefinitions.add(maneuverRetrieverChainDefinition);
final DataRetrieverChainDefinition<RacingEventService, HasMarkPassingContext> markPassingRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
legOfCompetitorRetrieverChainDefinition, HasMarkPassingContext.class, "MarkPassingSailingDomainRetrieverChain");
markPassingRetrieverChainDefinition.endWith(TrackedLegOfCompetitorRetrievalProcessor.class, MarkPassingRetrievalProcessor.class, HasMarkPassingContext.class, "MarkPassing");
dataRetrieverChainDefinitions.add(markPassingRetrieverChainDefinition);
}
public Iterable<DataRetrieverChainDefinition<?, ?>> get() {
@@ -0,0 +1,12 @@
package com.sap.sailing.datamining.data;
import java.io.Serializable;
import com.sap.sse.datamining.data.Cluster;
// TODO Move to sse bundle, after 49er analysis
public interface ClusterFormatter<T extends Serializable> {
String format(Cluster<T> cluster);
}
@@ -0,0 +1,38 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sse.datamining.annotations.Connector;
import com.sap.sse.datamining.annotations.Dimension;
import com.sap.sse.datamining.annotations.Statistic;
public interface HasManeuverContext {
@Connector(scanForStatistics=false)
HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext();
Maneuver getManeuver();
//TODO Clean-Up:
// Move Dimensions and Statistics to Maneuver and connect to it
@Dimension(messageKey="ManeuverType", ordinal=12)
ManeuverType getManeuverType();
@Dimension(messageKey="Tack", ordinal=13)
Tack getTack();
@Connector(messageKey="SpeedBefore", ordinal=0)
SpeedWithBearing getSpeedBefore();
@Connector(messageKey="SpeedAfter", ordinal=1)
SpeedWithBearing getSpeedAfter();
@Statistic(messageKey="DirectionChange", resultDecimals=2, ordinal=2)
Double getDirectionChangeInDegrees();
@Statistic(messageKey="ManeuverLoss", resultDecimals=0, ordinal=3)
Distance getManeuverLoss();
}
@@ -1,32 +1,45 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
import com.sap.sse.datamining.annotations.Connector;
import com.sap.sse.datamining.annotations.Dimension;
import com.sap.sse.datamining.annotations.Statistic;
public interface HasMarkPassingContext {
@Connector
HasTrackedRaceContext getTrackedRaceContext();
@Connector(scanForStatistics=false)
HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext();
MarkPassingManeuver getManeuver();
//TODO Clean-Up:
// Move Dimensions and Statistics to Maneuver/MarkPassingManeuver and connect to it
// Find a way to implement HasManeuverContext and HasMarkPassingManeuver with a base class
@Dimension(messageKey="Tack", ordinal=12)
Tack getTack();
@Connector(messageKey="Waypoint", ordinal=13)
Waypoint getWaypoint();
@Dimension(messageKey="PassingSide", ordinal=13)
NauticalSide getPassingSide();
@Connector
MarkPassing getMarkPassing();
@Connector(messageKey="SpeedBefore", ordinal=0)
SpeedWithBearing getSpeedBefore();
@Connector(messageKey="SpeedAfter", ordinal=1)
SpeedWithBearing getSpeedAfter();
@Connector(messageKey="Competitor")
Competitor getCompetitor();
@Statistic(messageKey="DirectionChange", resultDecimals=2, ordinal=2)
Double getDirectionChangeInDegrees();
@Connector(messageKey="Speed", ordinal=1)
Speed getSpeed();
@Statistic(messageKey="RelativeScore", ordinal=3, resultDecimals=2)
Double getRelativeRank();
@Connector(messageKey="SpeedTenSecondsBefore", ordinal=2)
Speed getSpeedTenSecondsBefore();
@Statistic(messageKey="AbsoluteRank", ordinal=4, resultDecimals=2)
Double getAbsoluteRank();
@Dimension(messageKey="LegType", ordinal=6)
String getPreviousLegTypeSignifier() throws NoWindException;
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
public String getCompetitorSearchTag();
}
@@ -2,10 +2,13 @@ package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sse.datamining.annotations.Connector;
import com.sap.sse.datamining.annotations.Dimension;
import com.sap.sse.datamining.annotations.Statistic;
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
public interface HasRaceOfCompetitorContext {
@@ -18,34 +21,55 @@ public interface HasRaceOfCompetitorContext {
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
public String getCompetitorSearchTag();
@Dimension(messageKey="TackAtStart", ordinal=12)
public Tack getTackAtStart() throws NoWindException;
@Dimension(messageKey="DistanceToStarboardSideAtStart", ordinal=13)
public ClusterDTO getPercentageClusterForDistanceToStarboardSideAtStart();
@Dimension(messageKey="RelativeScoreInPercent", ordinal=14)
public ClusterDTO getPercentageClusterForRelativeScore();
@Statistic(messageKey="DistanceAtStart", resultDecimals=2, ordinal=0)
public Distance getDistanceToStartLineAtStart();
@Statistic(messageKey="DistanceToStarboardSideAtStart", resultDecimals=2, ordinal=1)
public Double getNormalizedDistanceToStarboardSideAtStart();
@Connector(messageKey="SpeedWhenStarting", ordinal=2)
@Statistic(messageKey="WindwardDistanceToAdvantageousEndOfLine", resultDecimals=2, ordinal=2)
public Distance getWindwardDistanceToAdvantageousLineEndAtStart();
@Connector(messageKey="SpeedWhenStarting", ordinal=3)
public Speed getSpeedWhenStarting();
@Connector(messageKey="SpeedTenSecondsBeforeStart", ordinal=3)
@Connector(messageKey="SpeedTenSecondsBeforeStart", ordinal=4)
public Speed getSpeedTenSecondsBeforeStart();
@Statistic(messageKey="RankAtFirstMark", resultDecimals=2, ordinal=4)
public Double getRankAtFirstMark();
@Connector(messageKey="SpeedTenSecondsAfterStart", ordinal=5)
public Speed getSpeedTenSecondsAfterStart();
@Statistic(messageKey="NumberOfManeuvers", resultDecimals=0, ordinal=5)
public int getNumberOfManeuvers();
@Statistic(messageKey="NumberOfTacks", resultDecimals=2, ordinal=6)
public int getNumberOfTacks();
@Statistic(messageKey="NumberOfJibes", resultDecimals=2, ordinal=7)
public int getNumberOfJibes();
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=8)
public int getNumberOfPenaltyCircles();
@Statistic(messageKey="RankThirtySecondsAfterStart", resultDecimals=2, ordinal=6)
public Double getRankThirtySecondsAfterStart();
@Statistic(messageKey="RankAfterHalfOfTheFirstLeg", resultDecimals=2, ordinal=7)
public Double getRankAfterHalfOfTheFirstLeg();
@Statistic(messageKey="RankAtFirstMark", resultDecimals=2, ordinal=8)
public Double getRankAtFirstMark();
@Statistic(messageKey="RankGainsOrLossesBetweenFirstMarkAndFinish", resultDecimals=2, ordinal=9)
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish();
@Statistic(messageKey="NumberOfManeuvers", resultDecimals=0, ordinal=10)
public int getNumberOfManeuvers();
@Statistic(messageKey="NumberOfTacks", resultDecimals=2, ordinal=11)
public int getNumberOfTacks();
@Statistic(messageKey="NumberOfJibes", resultDecimals=2, ordinal=12)
public int getNumberOfJibes();
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=13)
public int getNumberOfPenaltyCircles();
}
@@ -12,24 +12,24 @@ public interface HasRaceResultOfCompetitorContext {
@Connector(messageKey="Competitor")
public Competitor getCompetitor();
@Dimension(messageKey="Regatta")
String getRegattaName();
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
public String getCompetitorSearchTag();
@Dimension(messageKey="WindSpeedInBeaufort")
int getAverageWindSpeedInRoundedBeaufort();
/**
* 0 means the competitor won the race, 1 means the competitor ranked last
*/
@Statistic(messageKey="RelativeScore", ordinal=1, resultDecimals=2)
public double getRelativeRank();
public Double getRelativeRank();
@Statistic(messageKey="AbsoluteRank", ordinal=2, resultDecimals=2)
public double getAbsoluteRank();
@Dimension(messageKey="WindSpeedInBeaufort")
int getAverageWindSpeedInRoundedBeaufort();
@Dimension(messageKey="Regatta")
String getRegattaName();
public Double getAbsoluteRank();
@Statistic(messageKey="NumberOfPodiumFinish", ordinal=3)
public Boolean isPodiumFinish();
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sse.datamining.annotations.Connector;
import com.sap.sse.datamining.annotations.Dimension;
import com.sap.sse.datamining.annotations.Statistic;
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
public interface HasTrackedLegOfCompetitorContext {
@@ -16,6 +17,9 @@ public interface HasTrackedLegOfCompetitorContext {
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
public String getCompetitorSearchTag();
@Dimension(messageKey="RelativeScoreInRaceInPercent", ordinal=12)
public ClusterDTO getPercentageClusterForRelativeScoreInRace();
@Connector(messageKey="Competitor")
public Competitor getCompetitor();
@@ -31,5 +35,8 @@ public interface HasTrackedLegOfCompetitorContext {
@Statistic(messageKey="AbsoluteRank", resultDecimals=2, ordinal=3)
public Double getAbsoluteRank();
@Statistic(messageKey="TimeSpentInSeconds", resultDecimals=0, ordinal=4)
public Long getTimeTakenInSeconds();
}
@@ -1,10 +1,12 @@
package com.sap.sailing.datamining.data;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.CourseArea;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.datamining.annotations.Connector;
import com.sap.sse.datamining.annotations.Dimension;
@@ -35,7 +37,10 @@ public interface HasTrackedRaceContext {
@Dimension(messageKey="Year", ordinal=2)
public Integer getYear();
@Dimension(messageKey="IsTracked", ordinal=6)
@Dimension(messageKey="AdvantageousEndOfLine", ordinal=6)
public NauticalSide getAdvantageousEndOfLine();
@Dimension(messageKey="IsTracked", ordinal=7)
public Boolean isTracked();
@Statistic(messageKey="NumberOfCompetitorFixes", resultDecimals=0, ordinal=0)
@@ -44,4 +49,9 @@ public interface HasTrackedRaceContext {
@Statistic(messageKey="NumberOfMarkFixes", resultDecimals=0, ordinal=1)
public int getNumberOfMarkFixes();
// Convenience methods for race dependent calculation to avoid code duplication
public Double getRelativeScoreForCompetitor(Competitor competitor);
public Double getRankAtFinishForCompetitor(Competitor competitor);
}
@@ -0,0 +1,40 @@
package com.sap.sailing.datamining.impl.components;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.concurrent.ExecutorService;
import com.sap.sailing.datamining.data.HasManeuverContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.datamining.impl.data.ManeuverWithContext;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sse.common.TimePoint;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
public class ManeuverRetrievalProcessor extends AbstractRetrievalProcessor<HasTrackedLegOfCompetitorContext, HasManeuverContext> {
public ManeuverRetrievalProcessor(ExecutorService executor, Collection<Processor<HasManeuverContext, ?>> resultReceivers, int retrievalLevel) {
super(HasTrackedLegOfCompetitorContext.class, HasManeuverContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
protected Iterable<HasManeuverContext> retrieveData(HasTrackedLegOfCompetitorContext element) {
Collection<HasManeuverContext> maneuversWithContext = new ArrayList<>();
TimePoint finishTime = element.getTrackedLegOfCompetitor().getFinishTime();
if (finishTime != null) {
try {
List<Maneuver> maneuvers = element.getTrackedLegOfCompetitor().getManeuvers(finishTime, false);
for (Maneuver maneuver : maneuvers) {
maneuversWithContext.add(new ManeuverWithContext(element, maneuver));
}
} catch (NoWindException e) {
throw new IllegalStateException("No wind retrieving the maneuvers", e);
}
}
return maneuversWithContext;
}
}
@@ -1,46 +1,45 @@
package com.sap.sailing.datamining.impl.components;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashSet;
import java.util.NavigableSet;
import java.util.Set;
import java.util.List;
import java.util.concurrent.ExecutorService;
import com.sap.sailing.datamining.data.HasMarkPassingContext;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.datamining.impl.data.MarkPassingWithContext;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
import com.sap.sse.common.TimePoint;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
public class MarkPassingRetrievalProcessor extends AbstractRetrievalProcessor<HasTrackedRaceContext, HasMarkPassingContext> {
public class MarkPassingRetrievalProcessor extends AbstractRetrievalProcessor<HasTrackedLegOfCompetitorContext, HasMarkPassingContext> {
public MarkPassingRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasMarkPassingContext, ?>> resultReceivers, int retrievalLevel) {
super(HasTrackedRaceContext.class, HasMarkPassingContext.class, executor, resultReceivers, retrievalLevel);
super(HasTrackedLegOfCompetitorContext.class, HasMarkPassingContext.class, executor, resultReceivers, retrievalLevel);
}
@Override
protected Iterable<HasMarkPassingContext> retrieveData(HasTrackedRaceContext element) {
TrackedRace trackedRace = element.getTrackedRace();
Set<HasMarkPassingContext> result = new HashSet<>();
trackedRace.getRace().getCourse().lockForRead();
try {
for (Competitor competitor : trackedRace.getRace().getCompetitors()) {
NavigableSet<MarkPassing> markPassingsForCompetitor = trackedRace.getMarkPassings(competitor);
trackedRace.lockForRead(markPassingsForCompetitor);
try {
markPassingsForCompetitor.stream().forEach(mp->result.add(new MarkPassingWithContext(element, mp)));
} finally {
trackedRace.unlockAfterRead(markPassingsForCompetitor);
protected Iterable<HasMarkPassingContext> retrieveData(HasTrackedLegOfCompetitorContext element) {
Collection<HasMarkPassingContext> maneuversWithContext = new ArrayList<>();
TimePoint finishTime = element.getTrackedLegOfCompetitor().getFinishTime();
if (finishTime != null) {
try {
List<Maneuver> maneuvers = element.getTrackedLegOfCompetitor().getManeuvers(finishTime, false);
for (Maneuver maneuver : maneuvers) {
if (maneuver.getType() == ManeuverType.MARK_PASSING) {
maneuversWithContext.add(new MarkPassingWithContext(element, (MarkPassingManeuver) maneuver));
}
}
} catch (NoWindException e) {
throw new IllegalStateException("No wind retrieving the mark passings", e);
}
} finally {
trackedRace.getRace().getCourse().unlockAfterRead();
}
return result;
return maneuversWithContext;
}
}
@@ -0,0 +1,143 @@
package com.sap.sailing.datamining.impl.data;
import java.io.Serializable;
import java.lang.reflect.Field;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import com.sap.sailing.datamining.data.ClusterFormatter;
import com.sap.sse.datamining.data.Cluster;
import com.sap.sse.datamining.data.ClusterBoundary;
import com.sap.sse.datamining.impl.data.AbstractCluster;
import com.sap.sse.datamining.impl.data.AbstractClusterBoundary;
import com.sap.sse.datamining.impl.data.ClusterWithLowerAndUpperBoundaries;
import com.sap.sse.datamining.impl.data.ClusterWithSingleBoundary;
import com.sap.sse.datamining.impl.data.ComparisonStrategy;
import com.sap.sse.datamining.impl.data.LocalizedCluster;
//TODO Clean and move to sse bundle after 49er analysis
public abstract class AbstractClusterFormatter<T extends Serializable> implements ClusterFormatter<T> {
private static final char INFINITE = '\u221e';
private final ClusterBoundariesAccessor boundariesAccessor = new ClusterBoundariesAccessor();
private final BoundaryValueAccessor valueAccessor = new BoundaryValueAccessor();
@Override
public String format(Cluster<T> cluster) {
if (cluster instanceof LocalizedCluster) {
// TODO recursive call format(localizedCluster.getInnerCluster()) after 49er analysis
}
if (cluster instanceof ClusterWithSingleBoundary) {
return formatClusterWithSingleBoundary((ClusterWithSingleBoundary<T>) cluster);
}
if (cluster instanceof ClusterWithLowerAndUpperBoundaries) {
return formatClusterWithLowerAndUpperBoundaries((ClusterWithLowerAndUpperBoundaries<T>) cluster);
}
throw new IllegalArgumentException("Can't format clusters of type " + cluster.getClass().getName());
}
protected String formatClusterWithSingleBoundary(ClusterWithSingleBoundary<T> cluster) {
List<ClusterBoundary<T>> boundaries = boundariesAccessor.getBoundaries(cluster);
if (boundaries == null) {
return "<Unformattable>";
}
ClusterBoundary<T> boundary = boundaries.get(0);
StringBuilder builder = new StringBuilder();
if (boundary.getStrategy() == ComparisonStrategy.LOWER_EQUALS_THAN ||
boundary.getStrategy() == ComparisonStrategy.LOWER_THAN) {
builder.append("-" + INFINITE + " - ");
}
builder.append(formatBoundary(boundary));
if (boundary.getStrategy() == ComparisonStrategy.GREATER_EQUALS_THAN ||
boundary.getStrategy() == ComparisonStrategy.GREATER_THAN) {
builder.append(" - " + INFINITE);
}
return builder.toString();
}
protected String formatClusterWithLowerAndUpperBoundaries(ClusterWithLowerAndUpperBoundaries<T> cluster) {
List<ClusterBoundary<T>> boundaries = boundariesAccessor.getBoundaries(cluster);
if (boundaries == null) {
return "<Unformattable>";
}
return String.format("%s - %s", formatBoundary(boundaries.get(0)), formatBoundary(boundaries.get(1)));
}
protected String formatBoundary(ClusterBoundary<T> boundary) {
String value = formatValue(valueAccessor.getValue((AbstractClusterBoundary<T>) boundary));
if (value == null) {
return "<Unformattable>";
}
StringBuilder builder = new StringBuilder();
ComparisonStrategy strategy = boundary.getStrategy();
if (strategy == ComparisonStrategy.GREATER_EQUALS_THAN || strategy == ComparisonStrategy.GREATER_THAN) {
builder.append(strategy.getSignifier());
}
builder.append(value);
if (strategy == ComparisonStrategy.LOWER_EQUALS_THAN || strategy == ComparisonStrategy.LOWER_THAN) {
builder.append(strategy.getSignifier());
}
return builder.toString();
}
protected abstract String formatValue(T value);
//FIXME Please delete me
private class ClusterBoundariesAccessor {
private static final String boundariesFieldName = "boundaries";
public List<ClusterBoundary<T>> getBoundaries(AbstractCluster<T> cluster) {
List<ClusterBoundary<T>> boundaries = null;
Field boundariesField = null;
try {
boundariesField = AbstractCluster.class.getDeclaredField(boundariesFieldName);
boundariesField.setAccessible(true);
@SuppressWarnings("unchecked")
Collection<ClusterBoundary<T>> reflectedBoundaries = (Collection<ClusterBoundary<T>>) boundariesField.get(cluster);
boundaries = new ArrayList<>();
for (ClusterBoundary<T> boundary : reflectedBoundaries) {
boundaries.add(boundary);
}
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
} finally {
if (boundariesField != null) {
boundariesField.setAccessible(false);
}
}
return boundaries;
}
}
//FIXME Please delete me
private class BoundaryValueAccessor {
private static final String valueFieldName = "boundaryValue";
@SuppressWarnings("unchecked")
public T getValue(AbstractClusterBoundary<T> boundary) {
T value = null;
Field valueField = null;
try {
valueField = AbstractClusterBoundary.class.getDeclaredField(valueFieldName);
valueField.setAccessible(true);
value = (T) valueField.get(boundary);
} catch (NoSuchFieldException | SecurityException | IllegalArgumentException | IllegalAccessException e) {
} finally {
if (valueField != null) {
valueField.setAccessible(false);
}
}
return value;
}
}
}
@@ -36,7 +36,7 @@ public class GPSFixWithContext implements HasGPSFixContext {
@Override
public ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages) {
Wind wind = getWind();
Cluster<?> cluster = Activator.getWindStrengthInBeaufortClusterGroup().getClusterFor(wind);
Cluster<?> cluster = Activator.getClusterGroups().getWindStrengthInBeaufortClusterGroup().getClusterFor(wind);
return new ClusterDTO(cluster.asLocalizedString(locale, stringMessages));
}
@@ -0,0 +1,110 @@
package com.sap.sailing.datamining.impl.data;
import java.util.HashMap;
import java.util.Map;
import com.sap.sse.datamining.data.Cluster;
import com.sap.sse.datamining.data.ClusterGroup;
import com.sap.sse.datamining.impl.data.ClusterWithLowerAndUpperBoundaries;
import com.sap.sse.datamining.impl.data.ClusterWithSingleBoundary;
import com.sap.sse.datamining.impl.data.ComparableClusterBoundary;
import com.sap.sse.datamining.impl.data.ComparisonStrategy;
// TODO: Improve and move to com.sap.sse.datamining
public class LinearDoubleClusterGroup implements ClusterGroup<Double> {
private static final long serialVersionUID = 1559316450183865115L;
private final double lowerBound;
private final double upperBound;
private final double stepSize;
private final boolean hardBounds;
private final transient Map<Integer, Cluster<Double>> clusterCache;
private final int lastClusterIndex;
public LinearDoubleClusterGroup(double lowerBound, double upperBound, double stepSize, boolean hardBounds) {
this.lowerBound = lowerBound;
this.upperBound = upperBound;
this.stepSize = stepSize;
this.hardBounds = hardBounds;
clusterCache = new HashMap<>();
lastClusterIndex = calculateLastClusterIndex(this.upperBound, this.stepSize);
}
private static int calculateLastClusterIndex(double upperBound, double stepSize) {
double doubleIndex = upperBound / stepSize;
int index = (int) doubleIndex;
double fractionalPart = doubleIndex - index;
if (fractionalPart <= 0.0001) {
index--;
}
return index;
}
@Override
public Cluster<Double> getClusterFor(Double valueObject) {
double value = valueObject.doubleValue();
if (value < lowerBound) {
return hardBounds ? null : getCluster(0);
}
if (value > upperBound) {
return hardBounds ? null : getCluster(lastClusterIndex);
}
if (Math.abs(upperBound - value) < 0.0001) {
return getCluster(lastClusterIndex);
}
return getCluster((int) (value / stepSize));
}
private Cluster<Double> getCluster(int index) {
if (!clusterCache.containsKey(index)) {
clusterCache.put(index, createCluster(index));
}
return clusterCache.get(index);
}
private Cluster<Double> createCluster(int index) {
if (index == 0 && !hardBounds) {
return new ClusterWithSingleBoundary<>(createUpperBound(index, ComparisonStrategy.LOWER_THAN));
}
if (index == lastClusterIndex && !hardBounds) {
return new ClusterWithSingleBoundary<>(createLowerBound(index, ComparisonStrategy.GREATER_EQUALS_THAN));
}
ComparisonStrategy upperComparisonStrategy = index == lastClusterIndex ? ComparisonStrategy.LOWER_EQUALS_THAN : ComparisonStrategy.LOWER_THAN;
return new ClusterWithLowerAndUpperBoundaries<>(createLowerBound(index, ComparisonStrategy.GREATER_EQUALS_THAN),
createUpperBound(index, upperComparisonStrategy));
}
private ComparableClusterBoundary<Double> createLowerBound(int index, ComparisonStrategy comparisonStrategy) {
return new ComparableClusterBoundary<Double>(this.lowerBound + stepSize * index, comparisonStrategy);
}
private ComparableClusterBoundary<Double> createUpperBound(int index, ComparisonStrategy comparisonStrategy) {
return new ComparableClusterBoundary<Double>(this.lowerBound + stepSize * (index + 1.0), comparisonStrategy);
}
@Override
public Class<Double> getClusterElementsType() {
return Double.class;
}
public double getLowerGroupBound() {
return lowerBound;
}
public double getUpperGroupBound() {
return upperBound;
}
public double getStepSize() {
return stepSize;
}
public boolean isHardBounds() {
return hardBounds;
}
}
@@ -0,0 +1,61 @@
package com.sap.sailing.datamining.impl.data;
import com.sap.sailing.datamining.data.HasManeuverContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.tracking.Maneuver;
public class ManeuverWithContext implements HasManeuverContext {
private final HasTrackedLegOfCompetitorContext trackedLegOfCompetitor;
private final Maneuver maneuver;
public ManeuverWithContext(HasTrackedLegOfCompetitorContext trackedLegOfCompetitor, Maneuver maneuver) {
this.trackedLegOfCompetitor = trackedLegOfCompetitor;
this.maneuver = maneuver;
}
@Override
public HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext() {
return trackedLegOfCompetitor;
}
@Override
public Maneuver getManeuver() {
return maneuver;
}
@Override
public ManeuverType getManeuverType() {
return getManeuver().getType();
}
@Override
public Tack getTack() {
return getManeuver().getNewTack();
}
@Override
public SpeedWithBearing getSpeedBefore() {
return getManeuver().getSpeedWithBearingBefore();
}
@Override
public SpeedWithBearing getSpeedAfter() {
return getManeuver().getSpeedWithBearingAfter();
}
@Override
public Double getDirectionChangeInDegrees() {
return getManeuver().getDirectionChangeInDegrees();
}
@Override
public Distance getManeuverLoss() {
return getManeuver().getManeuverLoss();
}
}
@@ -1,79 +1,87 @@
package com.sap.sailing.datamining.impl.data;
import com.sap.sailing.datamining.data.HasMarkPassingContext;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.leaderboard.Leaderboard;
import com.sap.sailing.domain.tracking.MarkPassingManeuver;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Util;
public class MarkPassingWithContext implements HasMarkPassingContext {
private final HasTrackedRaceContext trackedRaceContext;
private final MarkPassing markPassing;
public MarkPassingWithContext(HasTrackedRaceContext trackedRaceContext, MarkPassing markPassing) {
super();
this.trackedRaceContext = trackedRaceContext;
this.markPassing = markPassing;
private final HasTrackedLegOfCompetitorContext trackedLegOfCompetitor;
private final MarkPassingManeuver maneuver;
private Double absoluteRank;
private boolean rankHasBeenInitialized;
public MarkPassingWithContext(HasTrackedLegOfCompetitorContext trackedLegOfCompetitor, MarkPassingManeuver maneuver) {
this.trackedLegOfCompetitor = trackedLegOfCompetitor;
this.maneuver = maneuver;
}
@Override
public HasTrackedRaceContext getTrackedRaceContext() {
return trackedRaceContext;
public HasTrackedLegOfCompetitorContext getTrackedLegOfCompetitorContext() {
return trackedLegOfCompetitor;
}
@Override
public MarkPassing getMarkPassing() {
return markPassing;
public MarkPassingManeuver getManeuver() {
return maneuver;
}
@Override
public Competitor getCompetitor() {
return markPassing.getCompetitor();
}
@Override
public String getCompetitorSearchTag() {
return getCompetitor().getSearchTag();
}
@Override
public Speed getSpeed() {
return getTrackedRace().getTrack(getCompetitor()).getEstimatedSpeed(getMarkPassing().getTimePoint());
}
@Override
public Speed getSpeedTenSecondsBefore() {
return getTrackedRace().getTrack(getCompetitor()).getEstimatedSpeed(getMarkPassing().getTimePoint().minus(10000 /* ms */));
}
private TrackedRace getTrackedRace() {
return trackedRaceContext.getTrackedRace();
public Tack getTack() {
return getManeuver().getNewTack();
}
@Override
public String getPreviousLegTypeSignifier() throws NoWindException {
final String result;
TrackedLeg previousLeg = getPreviousLeg();
if (previousLeg == null) {
result = "Start";
} else {
result = previousLeg.getLegType(getMarkPassing().getTimePoint()).toString();
}
return result;
public Waypoint getWaypoint() {
return getManeuver().getWaypointPassed();
}
@Override
public NauticalSide getPassingSide() {
return getManeuver().getSide();
}
private TrackedLeg getPreviousLeg() {
final TrackedLeg result;
Waypoint waypoint = markPassing.getWaypoint();
if (waypoint == getTrackedRace().getRace().getCourse().getFirstWaypoint()) {
result = null;
} else {
result = getTrackedRace().getTrackedLegFinishingAt(waypoint);
}
return result;
@Override
public SpeedWithBearing getSpeedBefore() {
return getManeuver().getSpeedWithBearingBefore();
}
@Override
public SpeedWithBearing getSpeedAfter() {
return getManeuver().getSpeedWithBearingAfter();
}
@Override
public Double getDirectionChangeInDegrees() {
return getManeuver().getDirectionChangeInDegrees();
}
@Override
public Double getRelativeRank() {
Leaderboard leaderboard = getTrackedLegOfCompetitorContext().getTrackedLegContext().getTrackedRaceContext().getLeaderboardContext().getLeaderboard();
double competitorCount = Util.size(leaderboard.getCompetitors());
return getAbsoluteRank() == null ? null : getAbsoluteRank() / competitorCount;
}
@Override
public Double getAbsoluteRank() {
if (!rankHasBeenInitialized) {
TrackedRace trackedRace = getTrackedLegOfCompetitorContext().getTrackedLegContext().getTrackedRaceContext().getTrackedRace();
Competitor competitor = getTrackedLegOfCompetitorContext().getCompetitor();
int rank = trackedRace.getRank(competitor, getManeuver().getTimePoint());
absoluteRank = rank == 0 ? null : Double.valueOf(rank);
rankHasBeenInitialized = true;
}
return absoluteRank;
}
}
@@ -0,0 +1,10 @@
package com.sap.sailing.datamining.impl.data;
public class PercentageClusterFormatter extends AbstractClusterFormatter<Double> {
@Override
protected String formatValue(Double value) {
return (int) (value * 100) + "%";
}
}
@@ -2,18 +2,32 @@ package com.sap.sailing.datamining.impl.data;
import java.util.concurrent.TimeUnit;
import com.sap.sailing.datamining.Activator;
import com.sap.sailing.datamining.SailingClusterGroups;
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.ManeuverType;
import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.Speed;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.Maneuver;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.WindPositionMode;
import com.sap.sse.common.TimePoint;
import com.sap.sse.datamining.data.Cluster;
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
@@ -44,11 +58,83 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
return getCompetitor().getSearchTag();
}
@Override
public Tack getTackAtStart() throws NoWindException {
TimePoint startOfRace = getTrackedRace().getStartOfRace();
return startOfRace == null ? null : getTrackedRace().getTack(getCompetitor(), startOfRace);
}
@Override
public ClusterDTO getPercentageClusterForDistanceToStarboardSideAtStart() {
Double normalizedDistance = getNormalizedDistanceToStarboardSideAtStart();
if (normalizedDistance == null) {
return null;
}
SailingClusterGroups clusterGroups = Activator.getClusterGroups();
Cluster<Double> cluster = clusterGroups.getPercentageClusterGroup().getClusterFor(normalizedDistance);
return new ClusterDTO(clusterGroups.getPercentageClusterFormatter().format(cluster));
}
@Override
public Distance getDistanceToStartLineAtStart() {
return getTrackedRace().getDistanceToStartLine(getCompetitor(), 0);
}
@Override
public Double getNormalizedDistanceToStarboardSideAtStart() {
TrackedRace trackedRace = getTrackedRace();
TrackedLegOfCompetitor firstTrackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, trackedRace.getRace().getCourse().getFirstLeg());
TimePoint competitorStartTime = firstTrackedLegOfCompetitor.getStartTime();
if (competitorStartTime == null) {
return null;
}
Double distance = trackedRace.getDistanceFromStarboardSideOfStartLine(getCompetitor(), competitorStartTime).getMeters();
Double length = trackedRace.getStartLine(competitorStartTime).getLength().getMeters();
return distance / length;
}
@Override
public Distance getWindwardDistanceToAdvantageousLineEndAtStart() {
TrackedRace trackedRace = getTrackedRace();
TimePoint startOfRace = trackedRace.getStartOfRace();
LineDetails startLine = trackedRace.getStartLine(startOfRace);
Mark advantageousMark = null;
switch (startLine.getAdvantageousSideWhileApproachingLine()) {
case PORT:
advantageousMark = startLine.getPortMarkWhileApproachingLine();
break;
case STARBOARD:
advantageousMark = startLine.getStarboardMarkWhileApproachingLine();
break;
}
if (advantageousMark == null) {
return null;
}
GPSFixTrack<Mark, GPSFix> advantageousMarkTrack = trackedRace.getOrCreateTrack(advantageousMark);
Position advantageousMarkPosition = advantageousMarkTrack.getEstimatedPosition(startOfRace, false);
GPSFixTrack<Competitor, GPSFixMoving> competitorTrack = trackedRace.getTrack(getCompetitor());
Position competitorPosition = competitorTrack.getEstimatedPosition(startOfRace, false);
TrackedLeg trackedLeg = trackedRace.getTrackedLeg(trackedRace.getRace().getCourse().getFirstLeg());
Distance distance = trackedLeg.getWindwardDistance(competitorPosition, advantageousMarkPosition, startOfRace, WindPositionMode.LEG_MIDDLE);
return distance;
}
@Override
public ClusterDTO getPercentageClusterForRelativeScore() {
Double relativeScore = getTrackedRaceContext().getRelativeScoreForCompetitor(getCompetitor());
if (relativeScore == null) {
return null;
}
SailingClusterGroups clusterGroups = Activator.getClusterGroups();
Cluster<Double> cluster = clusterGroups.getPercentageClusterGroup().getClusterFor(relativeScore);
return new ClusterDTO(clusterGroups.getPercentageClusterFormatter().format(cluster));
}
@Override
public Speed getSpeedWhenStarting() {
return getTrackedRace().getSpeedWhenCrossingStartLine(getCompetitor());
@@ -59,14 +145,57 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
return getTrackedRace().getSpeed(getCompetitor(), TimeUnit.SECONDS.toMillis(10));
}
@Override
public Speed getSpeedTenSecondsAfterStart() {
TimePoint startOfRace = getTrackedRace().getStartOfRace();
if (startOfRace == null) {
return null;
}
return getTrackedRace().getTrack(getCompetitor()).getEstimatedSpeed(startOfRace.plus(TimeUnit.SECONDS.toMillis(10)));
}
@Override
public Double getRankThirtySecondsAfterStart() {
TimePoint startOfRace = getTrackedRace().getStartOfRace();
if (startOfRace == null) {
return null;
}
int rank = getTrackedRace().getRank(getCompetitor(), startOfRace.plus(TimeUnit.SECONDS.toMillis(30)));
return rank == 0 ? null : Double.valueOf(rank);
}
@Override
public Double getRankAfterHalfOfTheFirstLeg() {
Course course = getTrackedRace().getRace().getCourse();
TrackedLegOfCompetitor trackedLeg = getTrackedRace().getTrackedLeg(getCompetitor(), course.getFirstLeg());
TimePoint startTime = trackedLeg.getStartTime();
TimePoint finishTime = trackedLeg.getFinishTime();
if (startTime == null || finishTime == null) {
return null;
}
long halfOffset = (finishTime.asMillis() - startTime.asMillis()) / 2;
int rank = getTrackedRace().getRank(getCompetitor(), startTime.plus(halfOffset));
return rank == 0 ? null : Double.valueOf(rank);
}
@Override
public Double getRankAtFirstMark() {
Course course = getTrackedRace().getRace().getCourse();
Waypoint firstMark = course.getFirstLeg().getTo();
Competitor competitor = getCompetitor();
return Double.valueOf(getTrackedRace().getRank(competitor, getTrackedRace().getMarkPassing(competitor, firstMark).getTimePoint()));
int rank = getTrackedRace().getRank(competitor, getTrackedRace().getMarkPassing(competitor, firstMark).getTimePoint());
return rank == 0 ? null : Double.valueOf(rank);
}
@Override
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish() {
Double rankAtFirstMark = getRankAtFirstMark();
Double rankAtFinish = getTrackedRaceContext().getRankAtFinishForCompetitor(getCompetitor());
return rankAtFirstMark != null && rankAtFinish != null ? rankAtFirstMark - rankAtFinish : null;
}
@Override
public int getNumberOfManeuvers() {
return getNumberOfTacks() + getNumberOfJibes();
@@ -97,26 +226,5 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
}
return number;
}
@Override
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish() {
Double rankAtFirstMark = getRankAtFirstMark();
Double rankAtFinish = getRankAtFinish();
return rankAtFirstMark - rankAtFinish;
}
private Double getRankAtFinish() {
return Double.valueOf(getTrackedRace().getRank(getCompetitor(), getTrackedRace().getEndOfTracking()));
}
@Override
public Double getNormalizedDistanceToStarboardSideAtStart() {
TrackedRace trackedRace = getTrackedRace();
TrackedLegOfCompetitor firstTrackedLegOfCompetitor = trackedRace.getTrackedLeg(competitor, trackedRace.getRace().getCourse().getFirstLeg());
TimePoint competitorStartTime = firstTrackedLegOfCompetitor.getStartTime();
Double distance = trackedRace.getDistanceFromStarboardSideOfStartLine(getCompetitor(), competitorStartTime).getMeters();
Double length = trackedRace.getStartLine(competitorStartTime).getLength().getMeters();
return distance / length;
}
}
@@ -62,29 +62,15 @@ public class RaceResultOfCompetitorWithContext implements HasRaceResultOfCompeti
}
@Override
public String getCompetitorSearchTag() {
return getCompetitor().getSearchTag();
public String getRegattaName() {
Leaderboard leaderboard = getLeaderboard();;
final String result = leaderboard.getName();
return result;
}
@Override
public double getRelativeRank() {
Leaderboard leaderboard = getLeaderboard();
final TimePoint now = MillisecondsTimePoint.now();
double competitorCount = Util.size(leaderboard.getCompetitors());
double points = leaderboard.getTotalPoints(competitor, raceColumn, now);
double relativeLowPoints = leaderboard.getScoringScheme().isHigherBetter() ?
competitorCount - points : points;
final double result = relativeLowPoints / competitorCount;
return result;
}
@Override
public double getAbsoluteRank() {
try {
return getLeaderboard().getTotalRankOfCompetitor(competitor, MillisecondsTimePoint.now());
} catch (NoWindException e) {
throw new IllegalStateException("No wind calculating the absoulte rank", e);
}
public String getCompetitorSearchTag() {
return getCompetitor().getSearchTag();
}
@Override
@@ -171,10 +157,23 @@ public class RaceResultOfCompetitorWithContext implements HasRaceResultOfCompeti
}
@Override
public String getRegattaName() {
Leaderboard leaderboard = getLeaderboard();;
final String result = leaderboard.getName();
return result;
public Double getRelativeRank() {
Leaderboard leaderboard = getLeaderboard();
final TimePoint now = MillisecondsTimePoint.now();
double competitorCount = Util.size(leaderboard.getCompetitors());
Double points = leaderboard.getTotalPoints(competitor, raceColumn, now);
if (points != null) {
double relativeLowPoints = leaderboard.getScoringScheme().isHigherBetter() ? competitorCount - points
: points;
final double result = relativeLowPoints / competitorCount;
return result;
}
return null;
}
@Override
public Double getAbsoluteRank() {
return getLeaderboard().getTotalPoints(competitor, raceColumn, MillisecondsTimePoint.now());
}
@Override
@@ -1,5 +1,7 @@
package com.sap.sailing.datamining.impl.data;
import com.sap.sailing.datamining.Activator;
import com.sap.sailing.datamining.SailingClusterGroups;
import com.sap.sailing.datamining.data.HasTrackedLegContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.domain.base.Competitor;
@@ -9,6 +11,8 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.datamining.data.Cluster;
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompetitorContext {
@@ -18,7 +22,9 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
private final Competitor competitor;
private Double rankAtStart;
private boolean isRankAtStartInitialized;
private Double rankAtFinish;
private boolean isRankAtFinishInitialized;
public TrackedLegOfCompetitorWithContext(HasTrackedLegContext trackedLegContext, TrackedLegOfCompetitor trackedLegOfCompetitor) {
this.trackedLegContext = trackedLegContext;
@@ -46,6 +52,18 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
return getCompetitor().getSearchTag();
}
@Override
public ClusterDTO getPercentageClusterForRelativeScoreInRace() {
Double relativeScore = getTrackedLegContext().getTrackedRaceContext().getRelativeScoreForCompetitor(getCompetitor());
if (relativeScore == null) {
return null;
}
SailingClusterGroups clusterGroups = Activator.getClusterGroups();
Cluster<Double> cluster = clusterGroups.getPercentageClusterGroup().getClusterFor(relativeScore);
return new ClusterDTO(clusterGroups.getPercentageClusterFormatter().format(cluster));
}
@Override
public Distance getDistanceTraveled() {
TimePoint timePoint = getTrackedLegContext().getTrackedRaceContext().getTrackedRace().getEndOfTracking();
@@ -54,13 +72,17 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
@Override
public Double getRankGainsOrLosses() {
return getRankAtStart() - getRankAtFinish();
Double rankAtStart = getRankAtStart();
Double rankAtFinish = getRankAtFinish();
return rankAtStart != null && rankAtFinish != null ? rankAtStart - rankAtFinish : null;
}
private Double getRankAtStart() {
if (rankAtStart == null) {
if (!isRankAtStartInitialized) {
TrackedRace trackedRace = getTrackedLegContext().getTrackedRaceContext().getTrackedRace();
rankAtStart = Double.valueOf(trackedRace.getRank(getCompetitor(), getTrackedLegOfCompetitor().getStartTime()));
int rank = trackedRace.getRank(getCompetitor(), getTrackedLegOfCompetitor().getStartTime());
rankAtStart = rank == 0 ? null : Double.valueOf(rank);
isRankAtStartInitialized = true;
}
return rankAtStart;
}
@@ -69,7 +91,8 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
public Double getRelativeRank() {
Leaderboard leaderboard = getTrackedLegContext().getTrackedRaceContext().getLeaderboardContext().getLeaderboard();
double competitorCount = Util.size(leaderboard.getCompetitors());
return getRankAtFinish() / competitorCount;
Double rankAtFinish = getRankAtFinish();
return rankAtFinish == null ? null : rankAtFinish / competitorCount;
}
@Override
@@ -78,11 +101,24 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
}
private Double getRankAtFinish() {
if (rankAtStart == null) {
if (!isRankAtFinishInitialized) {
TrackedRace trackedRace = getTrackedLegContext().getTrackedRaceContext().getTrackedRace();
rankAtFinish = Double.valueOf(trackedRace.getRank(getCompetitor(), getTrackedLegOfCompetitor().getFinishTime()));
int rank = trackedRace.getRank(getCompetitor(), getTrackedLegOfCompetitor().getFinishTime());
rankAtFinish = rank == 0 ? null : Double.valueOf(rank);
isRankAtFinishInitialized = true;
}
return rankAtFinish;
}
@Override
public Long getTimeTakenInSeconds() {
TimePoint startTime = getTrackedLegOfCompetitor().getStartTime();
TimePoint finishTime = getTrackedLegOfCompetitor().getFinishTime();
if (startTime == null || finishTime == null) {
return null;
}
return (finishTime.asMillis() - startTime.asMillis()) / 1000;
}
}
@@ -11,9 +11,11 @@ import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.LineDetails;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -90,6 +92,12 @@ public class TrackedRaceWithContext implements HasTrackedRaceContext {
calendar.setTime(time.asDate());
return calendar.get(Calendar.YEAR);
}
@Override
public NauticalSide getAdvantageousEndOfLine() {
LineDetails startLine = getTrackedRace().getStartLine(getTrackedRace().getStartOfRace());
return startLine.getAdvantageousSideWhileApproachingLine();
}
@Override
public Boolean isTracked() {
@@ -125,5 +133,20 @@ public class TrackedRaceWithContext implements HasTrackedRaceContext {
}
return number;
}
// Convenience methods for race dependent calculation to avoid code duplication
public Double getRelativeScoreForCompetitor(Competitor competitor) {
Double rankAtFinish = getRankAtFinishForCompetitor(competitor);
if (rankAtFinish == null) {
return null;
}
return rankAtFinish / Util.size(getTrackedRace().getRace().getCompetitors());
}
@Override
public Double getRankAtFinishForCompetitor(Competitor competitor) {
int rank = getTrackedRace().getRank(competitor, getTrackedRace().getEndOfTracking());
return rank == 0 ? null : Double.valueOf(rank);
}
}
@@ -113,20 +113,20 @@ public enum BoatClassMasterdata {
private final String displayName;
private final String[] alternativeNames;
private final double hullLengthInMeter;
private final double hullBeamInMeter;
private final double hullLengthInMeters;
private final double hullBeamInMeters;
private final BoatHullType hullType;
private final boolean typicallyStartsUpwind;
private final boolean hasAdditionalDownwindSail;
private static Map<String, BoatClassMasterdata> fromUnifiedDisplayAndAlternativeNamesToBoatClassMasterdata;
private BoatClassMasterdata(String displayName, boolean typicallyStartsUpwind, double hullLengthInMeter,
double hullBeamInMeter, BoatHullType hullType, boolean hasAdditionalDownwindSail, String... alternativeNames) {
private BoatClassMasterdata(String displayName, boolean typicallyStartsUpwind, double hullLengthInMeters,
double hullBeamInMeters, BoatHullType hullType, boolean hasAdditionalDownwindSail, String... alternativeNames) {
this.displayName = displayName;
this.typicallyStartsUpwind = typicallyStartsUpwind;
this.hullLengthInMeter = hullLengthInMeter;
this.hullBeamInMeter = hullBeamInMeter;
this.hullLengthInMeters = hullLengthInMeters;
this.hullBeamInMeters = hullBeamInMeters;
this.hullType = hullType;
this.hasAdditionalDownwindSail = hasAdditionalDownwindSail;
this.alternativeNames = alternativeNames;
@@ -147,8 +147,8 @@ public enum BoatClassMasterdata {
double hullBeamInMeter, BoatHullType hullType, boolean hasAdditionalDownwindSail) {
this.displayName = displayName;
this.typicallyStartsUpwind = typicallyStartsUpwind;
this.hullLengthInMeter = hullLengthInMeter;
this.hullBeamInMeter = hullBeamInMeter;
this.hullLengthInMeters = hullLengthInMeter;
this.hullBeamInMeters = hullBeamInMeter;
this.hullType = hullType;
this.hasAdditionalDownwindSail = hasAdditionalDownwindSail;
this.alternativeNames = null;
@@ -192,7 +192,7 @@ public enum BoatClassMasterdata {
}
public Distance getHullLength() {
return new MeterDistance(hullLengthInMeter);
return new MeterDistance(hullLengthInMeters);
}
public String getDisplayName() {
@@ -204,7 +204,7 @@ public enum BoatClassMasterdata {
}
public Distance getHullBeam() {
return new MeterDistance(hullBeamInMeter);
return new MeterDistance(hullBeamInMeters);
}
public BoatHullType getHullType() {
@@ -13,7 +13,7 @@ import com.sap.sse.common.Duration;
*
*/
public interface Distance extends Comparable<Distance>, Serializable {
static final Distance NULL = new Distance() {
static class NullDistance implements Distance {
private static final long serialVersionUID = -3167560884686340893L;
@Override
@@ -96,7 +96,9 @@ public interface Distance extends Comparable<Distance>, Serializable {
public Distance abs() {
return this;
}
};
}
static final NullDistance NULL = new NullDistance();
double getGeographicalMiles();
@@ -17,6 +17,11 @@ public class RegattaName implements RegattaIdentifier {
return regattaName;
}
@Override
public String toString() {
return getRegattaName();
}
@Override
public Object getRegatta(RegattaFetcher regattaFetcher) {
return regattaFetcher.getRegatta(this);
@@ -2,6 +2,8 @@ package com.sap.sailing.domain.common.dto;
import java.io.Serializable;
import com.sap.sailing.domain.common.Distance;
public class BoatClassDTO extends NamedDTO implements Serializable, Comparable<BoatClassDTO> {
private static final long serialVersionUID = 1981789833769906676L;
@@ -11,24 +13,24 @@ public class BoatClassDTO extends NamedDTO implements Serializable, Comparable<B
*/
public static final String DEFAULT_NAME = "Default";
private double hullLengthInMeters;
private Distance hullLength;
private String displayName;
BoatClassDTO() {}
public BoatClassDTO(String name, String displayName, double hullLengthInMeters) {
public BoatClassDTO(String name, String displayName, Distance hullLength) {
super(name);
this.hullLengthInMeters = hullLengthInMeters;
this.hullLength = hullLength;
}
public BoatClassDTO(String name, double hullLengthInMeters) {
public BoatClassDTO(String name, Distance hullLength) {
super(name);
this.hullLengthInMeters = hullLengthInMeters;
this.hullLength = hullLength;
this.displayName = null;
}
public double getHullLengthInMeters() {
return hullLengthInMeters;
public Distance getHullLength() {
return hullLength;
}
public String getDisplayName() {
@@ -3,10 +3,13 @@ package com.sap.sailing.domain.common.dto;
import java.io.Serializable;
import java.util.List;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.MaxPointsReason;
import com.sap.sailing.domain.common.RaceIdentifier;
import com.sap.sailing.domain.common.Tack;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.impl.MillisecondsDurationImpl;
/**
* Holds a single competitor's scoring details for a single race. It may optionally contain
@@ -78,6 +81,8 @@ public class LeaderboardEntryDTO implements Serializable {
*/
public Duration averageSamplingInterval;
public Double averageRideHeightInMeters;
/**
* The time gap to the competitor leading the race; for one-design races this is the time the competitor is expected
* to need to reach the leader's (windward) position (if in the same leg) or the difference between the time at
@@ -132,6 +137,44 @@ public class LeaderboardEntryDTO implements Serializable {
public boolean hasScoreCorrection() {
return totalPointsCorrected || (reasonForMaxPoints != null && reasonForMaxPoints != MaxPointsReason.NONE);
}
public Duration getTimeSailedInMillis() {
final Duration result;
if (legDetails != null) {
long timeInMilliseconds = 0;
for (LegEntryDTO legDetail : legDetails) {
if (legDetail != null) {
if (legDetail.distanceTraveledInMeters != null && legDetail.timeInMilliseconds != null) {
timeInMilliseconds += legDetail.timeInMilliseconds;
} else {
timeInMilliseconds = 0;
break;
}
}
}
result = new MillisecondsDurationImpl(timeInMilliseconds);
} else {
result = null;
}
return result;
}
public Distance getDistanceTraveledInMeters() {
Distance result = null;
if (legDetails != null) {
for (LegEntryDTO legDetail : legDetails) {
if (legDetail != null) {
if (legDetail.distanceTraveledInMeters != null) {
if (result == null) {
result = Distance.NULL;
}
result = result.add(new MeterDistance(legDetail.distanceTraveledInMeters));
}
}
}
}
return result;
}
@Override
public int hashCode() {
@@ -174,6 +217,7 @@ public class LeaderboardEntryDTO implements Serializable {
result = prime * result
+ ((speedOverGroundAtStartOfRaceInKnots == null) ? 0 : speedOverGroundAtStartOfRaceInKnots.hashCode());
result = prime * result + ((startTack == null) ? 0 : startTack.hashCode());
result = prime * result + ((averageRideHeightInMeters == null) ? 0 : averageRideHeightInMeters.hashCode());
result = prime * result + ((averageSamplingInterval == null) ? 0 : averageSamplingInterval.hashCode());
result = prime * result + ((timeSinceLastPositionFixInSeconds == null) ? 0 : timeSinceLastPositionFixInSeconds.hashCode());
result = prime * result + ((netPoints == null) ? 0 : netPoints.hashCode());
@@ -282,6 +326,11 @@ public class LeaderboardEntryDTO implements Serializable {
return false;
if (startTack != other.startTack)
return false;
if (averageRideHeightInMeters == null) {
if (other.averageRideHeightInMeters != null)
return false;
} else if (!averageRideHeightInMeters.equals(other.averageRideHeightInMeters))
return false;
if (averageSamplingInterval == null) {
if (other.averageSamplingInterval != null)
return false;
@@ -4,6 +4,9 @@ import java.io.Serializable;
import java.util.Date;
import java.util.Map;
import com.sap.sailing.domain.common.Distance;
import com.sap.sse.common.Duration;
/**
* Holds data about one competitor and all races represented by the owning {@link LeaderboardDTO leaderboard}.
*
@@ -117,6 +120,28 @@ public class LeaderboardRowDTO implements Serializable {
return true;
}
public Distance getDistanceTraveledInMeters(String raceColumnName) {
final Distance result;
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
if (fieldsForRace != null) {
result = fieldsForRace.getDistanceTraveledInMeters();
} else {
result = null;
}
return result;
}
public Duration getTimeSailed(String raceColumnName) {
final Duration result;
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
if (fieldsForRace != null) {
result = fieldsForRace.getTimeSailedInMillis();
} else {
result = null;
}
return result;
}
@Override
public String toString() {
return "LeaderboardRowDTO for competitor "+competitor;
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.common.tracking;
import com.sap.sailing.domain.common.Distance;
/**
* Specific {@link SensorFix} for bravo devices (http://www.bravosystems.es/).
*
@@ -12,5 +14,5 @@ public interface BravoFix extends SensorFix {
*
* @return the ride height
*/
double getRideHeight();
Distance getRideHeight();
}
@@ -1,5 +1,7 @@
package com.sap.sailing.domain.common.tracking.impl;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
@@ -32,8 +34,8 @@ public class BravoFixImpl implements BravoFix {
}
@Override
public double getRideHeight() {
return fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightColumn);
public Distance getRideHeight() {
return new MeterDistance(fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightColumn));
}
}
@@ -1144,11 +1144,12 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
logger.log(Level.WARNING, "Error loading racing procedure configration for regatta.", e);
}
}
final Double buoyZoneRadiusInHullLengths = (Double) dbRegatta.get(FieldNames.REGATTA_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS.name());
final Boolean useStartTimeInference = (Boolean) dbRegatta.get(FieldNames.REGATTA_USE_START_TIME_INFERENCE.name());
final Boolean controlTrackingFromStartAndFinishTimes = (Boolean) dbRegatta.get(FieldNames.REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES.name());
final RankingMetricConstructor rankingMetricConstructor = loadRankingMetricConstructor(dbRegatta);
result = new RegattaImpl(getRaceLogStore(), getRegattaLogStore(), name, boatClass, startDate, endDate, series, /* persistent */true,
loadScoringScheme(dbRegatta), id, courseArea, useStartTimeInference == null ? true
loadScoringScheme(dbRegatta), id, courseArea, buoyZoneRadiusInHullLengths == null ? Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS : buoyZoneRadiusInHullLengths, useStartTimeInference == null ? true
: useStartTimeInference, controlTrackingFromStartAndFinishTimes == null ? false : controlTrackingFromStartAndFinishTimes,
rankingMetricConstructor);
result.setRegattaConfiguration(configuration);
@@ -34,6 +34,7 @@ public enum FieldNames {
REGATTA_END_DATE,
REGATTA_SERIES,
REGATTA_REGATTA_CONFIGURATION,
REGATTA_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS,
REGATTA_USE_START_TIME_INFERENCE,
REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES,
SERIES_NAME,
@@ -646,6 +646,7 @@ public class MongoObjectFactoryImpl implements MongoObjectFactory {
DBObject configurationObject = (DBObject) JSON.parse(json.toString());
dbRegatta.put(FieldNames.REGATTA_REGATTA_CONFIGURATION.name(), configurationObject);
}
dbRegatta.put(FieldNames.REGATTA_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS.name(), regatta.getBuoyZoneRadiusInHullLengths());
dbRegatta.put(FieldNames.REGATTA_USE_START_TIME_INFERENCE.name(), regatta.useStartTimeInference());
dbRegatta.put(FieldNames.REGATTA_CONTROL_TRACKING_FROM_START_AND_FINISH_TIMES.name(), regatta.isControlTrackingFromStartAndFinishTimes());
dbRegatta.put(FieldNames.REGATTA_RANKING_METRIC.name(), storeRankingMetric(regatta));
@@ -86,7 +86,7 @@ public class CreateAndTrackWithRaceLogTest {
service);
regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser",
/* startDate */null, /* endDate */null, UUID.randomUUID(), Collections.<Series> singletonList(series), false,
new HighPoint(), UUID.randomUUID(), /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
new HighPoint(), UUID.randomUUID(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false, OneDesignRankingMetric::new);
series.addRaceColumn(columnName, /* trackedRegattaRegistry */null);
leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {});
adapter = RaceLogTrackingAdapterFactory.INSTANCE.getAdapter(DomainFactory.INSTANCE);
@@ -49,9 +49,11 @@ import com.sap.sailing.domain.base.impl.CourseImpl;
import com.sap.sailing.domain.base.impl.RaceDefinitionImpl;
import com.sap.sailing.domain.base.impl.RegattaImpl;
import com.sap.sailing.domain.base.impl.WaypointImpl;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
@@ -95,9 +97,9 @@ public class SensorFixStoreAndLoadTest {
private static final long FIX_TIMESTAMP2 = 120;
private static final long FIX_TIMESTAMP3 = 210;
private static final long AFTER_LAST_FIX = FIX_TIMESTAMP3 + 1;
private static final double FIX_RIDE_HEIGHT = 1337.0;
private static final double FIX_RIDE_HEIGHT2 = 1338.0;
private static final double FIX_RIDE_HEIGHT3 = 1336.0;
private static final Distance FIX_RIDE_HEIGHT = new MeterDistance(1337.0);
private static final Distance FIX_RIDE_HEIGHT2 = new MeterDistance(1338.0);
private static final Distance FIX_RIDE_HEIGHT3 = new MeterDistance(1336.0);
private static final double FIX_TEST_VALUE = 12.0;
protected final MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory();
protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a");
@@ -384,20 +386,14 @@ public class SensorFixStoreAndLoadTest {
public void testBravoFixIsCorrectlyWrapped() throws InterruptedException {
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
addBravoFixes();
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
trackedRace.attachRaceLog(raceLog);
trackedRace.attachRegattaLog(regattaLog);
trackedRace.waitForLoadingToFinish();
BravoFixTrack<Competitor> bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
assertEquals(FIX_RIDE_HEIGHT,
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP)).getRideHeight());
fixLoaderAndTracker.stop(true);
}
@@ -482,9 +478,9 @@ public class SensorFixStoreAndLoadTest {
}
private void addBravoFixes() {
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP, FIX_RIDE_HEIGHT));
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2));
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3));
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP, FIX_RIDE_HEIGHT.getMeters()));
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2.getMeters()));
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3.getMeters()));
}
private FixLoaderAndTracker createFixLoaderAndTracker() {
@@ -250,7 +250,7 @@ public class TransientCompetitorStoreImpl implements CompetitorStore, Serializab
c.getFlagImage() == null ? null : c.getFlagImage().toString(),
new BoatDTO(c.getBoat().getName(), c.getBoat().getSailID(), c.getBoat().getColor()),
new BoatClassDTO(c.getBoat().getBoatClass()
.getName(), c.getBoat().getBoatClass().getDisplayName(), c.getBoat().getBoatClass().getHullLength().getMeters()),
.getName(), c.getBoat().getBoatClass().getDisplayName(), c.getBoat().getBoatClass().getHullLength()),
c.getTimeOnTimeFactor(), c.getTimeOnDistanceAllowancePerNauticalMile(), c.getSearchTag());
weakCompetitorDTOCache.put(c, competitorDTO);
}
@@ -35,6 +35,7 @@ import com.sap.sailing.domain.common.dto.LeaderboardRowDTO;
import com.sap.sailing.domain.common.dto.LegEntryDTO;
import com.sap.sailing.domain.common.dto.RaceColumnDTO;
import com.sap.sailing.domain.common.dto.RaceDTO;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.test.StoredTrackBasedTest;
import com.sap.sse.common.Cloner;
import com.sap.sse.common.Color;
@@ -234,7 +235,7 @@ public class LeaderboardDTODiffingTest {
public void testCompetitorListChange() {
newVersion.competitors = new ArrayList<CompetitorDTO>(newVersion.competitors); // clone competitor list so it's not identical to that of previous version
CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987",
/* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", 5.05), /* timeOnTimeFactor */ null,
/* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", new MeterDistance(5.05)), /* timeOnTimeFactor */ null,
/* timeOnDistanceAllowancePerNauticalMile */ null, null);
newVersion.competitors.add(13, somebodyNew); // insert a competitor; this should mess up all others' indexes; check if this works
CompetitorDTO wolfgang = getPreviousCompetitorByName("HUNGER +JESS");
@@ -255,7 +256,7 @@ public class LeaderboardDTODiffingTest {
newVersion.setSuppressedCompetitors(newSuppressedCompetitors);
newVersion.competitors = new ArrayList<CompetitorDTO>(newVersion.competitors); // clone competitor list so it's not identical to that of previous version
CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987",
/* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", 5.05), /* timeOnTimeFactor */ null,
/* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", new MeterDistance(5.05)), /* timeOnTimeFactor */ null,
/* timeOnDistanceAllowancePerNauticalMile */ null, null);
newVersion.setSuppressed(newVersion.competitors.get(13), true); // suppress an existing competitor; compaction should reduce this to a single number only
newVersion.setSuppressed(somebodyNew, true); // check that mixed mode with existing and new competitors works as well
@@ -278,7 +279,7 @@ public class LeaderboardDTODiffingTest {
public void testDisplayNameChange() {
newVersion.competitors = new ArrayList<CompetitorDTO>(newVersion.competitors); // clone competitor list so it's not identical to that of previous version
CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987",
/* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", 5.05), /* timeOnTimeFactor */ null,
/* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", new MeterDistance(5.05)), /* timeOnTimeFactor */ null,
/* timeOnDistanceAllowancePerNauticalMile */ null, null);
newVersion.competitors.add(somebodyNew);
newVersion.competitorDisplayNames = new HashMap<CompetitorDTO, String>(newVersion.competitorDisplayNames);
@@ -308,7 +309,7 @@ public class LeaderboardDTODiffingTest {
newVersion.setCompetitorsFromBestToWorst(r9, newOrdering);
newVersion.competitors = new ArrayList<CompetitorDTO>(newVersion.competitors); // clone competitor list so it's not identical to that of previous version
CompetitorDTO somebodyNew = new CompetitorDTOImpl("Someone New", Color.RED, "someone@nobody.de", "DE", "GER", "Germany", "912p09871203987",
/* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", 5.05), /* timeOnTimeFactor */ null,
/* imageURL */ null, /* flagImageURL */ null, new BoatDTO("LSC", "GER 1234"), new BoatClassDTO("505", new MeterDistance(5.05)), /* timeOnTimeFactor */ null,
/* timeOnDistanceAllowancePerNauticalMile */ null, null);
newVersion.competitors.add(somebodyNew);
newOrdering.add(somebodyNew);
@@ -14,6 +14,7 @@ import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.impl.NauticalMileDistance;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.CompactGPSFixMovingImpl;
@@ -67,6 +68,12 @@ public class PositionTest {
assertEquals(20004, Math.abs(southPole.getDistance(northPole).getKilometers()), 0.0001);
}
@Test
public void radTest() {
assertEquals(180, new MeterDistance(20005000).getCentralAngleDeg(), 0.01);
assertEquals(Math.PI, new MeterDistance(20005000).getCentralAngleRad(), 0.01);
}
@Test
public void bearingTest() {
// Note that the bearing is a bearing on a great circle!
@@ -447,6 +447,15 @@ public class MockedTrackedRace implements DynamicTrackedRace {
}
@Override
public Double getBuoyZoneRadiusInHullLengths(){
return 1.0;
}
@Override
public void setBuoyZoneRadiusInHullLengths(Double buoyZoneRadiusInHullLengths){
}
@Override
public RegattaLikeIdentifier getRegattaLikeIdentifier() {
return null;
@@ -532,15 +541,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
@Override
public boolean isControlTrackingFromStartAndFinishTimes() {
// TODO Auto-generated method stub
return false;
}
@Override
public void setControlTrackingFromStartAndFinishTimes(
boolean controlTrackingFromStartAndFinishTimes) {
// TODO Auto-generated method stub
}
};
}
@@ -1121,4 +1127,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
public Iterable<RaceLog> getAttachedRaceLogs() {
return Collections.emptySet();
}
@Override
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) {
return null;
}
}
@@ -681,4 +681,9 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
public Iterable<RaceLog> getAttachedRaceLogs() {
return Collections.emptySet();
}
@Override
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) {
return null;
}
}
@@ -82,8 +82,8 @@ public class BravoFixTrackSerializationTest {
assertRideHeight(track, testFixData.timePoint, testFixData.rideHeight);
}
private void assertRideHeight(BravoFixTrack<Competitor> track, TimePoint timePoint, double expected) {
assertEquals(expected, track.getRideHeight(timePoint), 0.0);
private void assertRideHeight(BravoFixTrack<Competitor> track, TimePoint timePoint, double expectedRideHeightInMeters) {
assertEquals(expectedRideHeightInMeters, track.getRideHeight(timePoint).getMeters(), 0.0);
}
@SuppressWarnings("unchecked")
@@ -24,6 +24,9 @@ import com.sap.sse.common.TimePoint;
*
*/
public interface Regatta extends NamedWithID, IsRegattaLike, HasRaceColumnsAndRegattaLike {
public static final double DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS = 2;
ScoringScheme getScoringScheme();
/**
@@ -155,6 +158,14 @@ public interface Regatta extends NamedWithID, IsRegattaLike, HasRaceColumnsAndRe
RegattaAndRaceIdentifier getRaceIdentifier(RaceDefinition race);
/**
* Define the value which would be multipled by hull length from {@link BoatClass}.
* Next the calculated value {@link RegattaUtil} would be used to fill out radius of buoy on race map setting.
*/
public Double getBuoyZoneRadiusInHullLengths();
public void setBuoyZoneRadiusInHullLengths(Double buoyZoneRadiusInHullLengths);
/**
* When there is no race committee app in place and no operator is managing the race start times for this regatta,
* start times can optionally be inferred from the start mark passings by the {@link TrackedRace}s in this regatta.
@@ -115,6 +115,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
private transient RaceLogStore raceLogStore;
private final IsRegattaLike regattaLikeHelper;
private final RankingMetricConstructor rankingMetricConstructor;
private Double buoyZoneRadiusInHullLengths;
private CourseArea defaultCourseArea;
private RegattaConfiguration configuration;
@@ -162,7 +163,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
Iterable<? extends Series> series, boolean persistent, ScoringScheme scoringScheme, Serializable id,
CourseArea courseArea, RankingMetricConstructor rankingMetricConstructor) {
this(EmptyRaceLogStore.INSTANCE, EmptyRegattaLogStore.INSTANCE, name, boatClass, startDate, endDate, series,
persistent, scoringScheme, id, courseArea, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false,
persistent, scoringScheme, id, courseArea, 0.0, /* useStartTimeInference */true, /* controlTrackingFromStartAndFinishTimes */ false,
rankingMetricConstructor);
}
@@ -204,7 +205,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
/* isMedal */false, /* isFleetsCanRunInParallel */ true, Collections
.singletonList(new FleetImpl(LeaderboardNameConstants.DEFAULT_FLEET_NAME)),
/* race column names */new ArrayList<String>(), trackedRegattaRegistry)), /* persistent */false,
scoringScheme, id, courseArea, /* useStartTimeInference */true, controlTrackingFromStartAndFinishTimes,
scoringScheme, id, courseArea, /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */true, controlTrackingFromStartAndFinishTimes,
rankingMetricConstructor);
}
@@ -215,7 +216,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
*/
public <S extends Series> RegattaImpl(RaceLogStore raceLogStore, RegattaLogStore regattaLogStore, String name,
BoatClass boatClass, TimePoint startDate, TimePoint endDate, Iterable<S> series, boolean persistent,
ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, boolean useStartTimeInference,
ScoringScheme scoringScheme, Serializable id, CourseArea courseArea, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, RankingMetricConstructor rankingMetricConstructor) {
super(name);
this.rankingMetricConstructor = rankingMetricConstructor;
@@ -241,6 +242,7 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
this.scoringScheme = scoringScheme;
this.defaultCourseArea = courseArea;
this.configuration = null;
this.buoyZoneRadiusInHullLengths = buoyZoneRadiusInHullLengths;
this.regattaLikeHelper = new BaseRegattaLikeImpl(new RegattaAsRegattaLikeIdentifier(this), regattaLogStore) {
private static final long serialVersionUID = 8546222568682770206L;
@@ -572,6 +574,16 @@ public class RegattaImpl extends NamedImpl implements Regatta, RaceColumnListene
this.endDate = endDate;
}
@Override
public Double getBuoyZoneRadiusInHullLengths() {
return buoyZoneRadiusInHullLengths;
}
@Override
public void setBuoyZoneRadiusInHullLengths(Double buoyZoneRadiusInHullLengths) {
this.buoyZoneRadiusInHullLengths = buoyZoneRadiusInHullLengths;
}
@Override
public CourseArea getDefaultCourseArea() {
return defaultCourseArea;
@@ -1437,6 +1437,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
} else {
entryDTO.timeSinceLastPositionFixInSeconds = null;
}
final Distance averageRideHeight = trackedRace.getAverageRideHeight(competitor, timePoint);
entryDTO.averageRideHeightInMeters = averageRideHeight == null ? null : averageRideHeight.getMeters();
}
if (addLegDetails && trackedRace != null) {
try {
@@ -1721,11 +1723,13 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
Double speedOverGroundInKnots;
if (trackedLeg.hasFinishedLeg(timePoint)) {
speedOverGroundInKnots = averageSpeedOverGround == null ? null : averageSpeedOverGround.getKnots();
result.currentRideHeightInMeters = trackedLeg.getAverageRideHeight(timePoint);
final Distance averageRideHeight = trackedLeg.getAverageRideHeight(timePoint);
result.currentRideHeightInMeters = averageRideHeight == null ? null : averageRideHeight.getMeters();
} else {
final SpeedWithBearing speedOverGround = trackedLeg.getSpeedOverGround(timePoint);
speedOverGroundInKnots = speedOverGround == null ? null : speedOverGround.getKnots();
result.currentRideHeightInMeters = trackedLeg.getRideHeight(timePoint);
final Distance rideHeight = trackedLeg.getRideHeight(timePoint);
result.currentRideHeightInMeters = rideHeight == null ? null : rideHeight.getMeters();
}
result.currentSpeedOverGroundInKnots = speedOverGroundInKnots == null ? null : speedOverGroundInKnots;
Distance distanceTraveled = trackedLeg.getDistanceTraveled(timePoint);
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking;
import java.io.Serializable;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.WithID;
@@ -22,7 +23,7 @@ public interface BravoFixTrack<ItemType extends WithID & Serializable> extends S
* @return the ride height for the given {@link TimePoint} or <code>null</code> if the {@link TimePoint} is not in the range
* where we have fixes for.
*/
Double getRideHeight(TimePoint timePoint);
Distance getRideHeight(TimePoint timePoint);
/**
* Calculates the average ride height for the given time range.
@@ -34,5 +35,5 @@ public interface BravoFixTrack<ItemType extends WithID & Serializable> extends S
* @return the average ride height for the given time range or <code>null</code> if there are no fixes in the given
* time range.
*/
Double getAverageRideHeight(TimePoint from, TimePoint to);
Distance getAverageRideHeight(TimePoint from, TimePoint to);
}
@@ -97,7 +97,7 @@ public interface TrackedLegOfCompetitor extends Serializable {
* <code>timePoint</code>. If the competitor already completed the leg at <code>timePoint</code> and the respective
* mark passing event was already received, the average ride height for the entire leg (and no further) is computed.
*/
Double getAverageRideHeight(TimePoint timePoint);
Distance getAverageRideHeight(TimePoint timePoint);
/**
* @return <code>null</code> if the competitor hasn't started this leg yet, otherwise the fix where the maximum speed was
@@ -237,7 +237,7 @@ public interface TrackedLegOfCompetitor extends Serializable {
* <code>timePoint</code>, returns the current ride height for this time point. If the competitor has already
* finished the leg, the ride height at the time the competitor finished the leg is returned.
*/
Double getRideHeight(TimePoint at);
Distance getRideHeight(TimePoint at);
/**
* Computes the distance along the wind track to the wind-projected position of the race's overall leader. If leader
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.common.tracking.SensorFix;
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.impl.NonCachingMarkPositionAtTimePointCache;
@@ -685,6 +686,8 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
boolean waitForLatestAnalysis) throws NoWindException;
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint);
WindStore getWindStore();
Competitor getOverallLeader(TimePoint timePoint);
@@ -1,10 +1,8 @@
package com.sap.sailing.domain.tracking.impl;
import java.io.Serializable;
import java.util.OptionalDouble;
import java.util.Spliterator;
import java.util.stream.StreamSupport;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
import com.sap.sailing.domain.common.tracking.BravoFix;
import com.sap.sailing.domain.tracking.BravoFixTrack;
@@ -34,7 +32,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
}
@Override
public Double getRideHeight(TimePoint timePoint) {
public Distance getRideHeight(TimePoint timePoint) {
BravoFix fixAfter = getFirstFixAtOrAfter(timePoint);
if (fixAfter != null && fixAfter.getTimePoint().compareTo(timePoint) == 0) {
// exact match of timepoint -> no interpolation necessary
@@ -54,17 +52,24 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
}
@Override
public Double getAverageRideHeight(TimePoint from, TimePoint to) {
public Distance getAverageRideHeight(TimePoint from, TimePoint to) {
final Distance result;
lockForRead();
try {
Spliterator<BravoFix> fixes = getFixes(from, true, to, true).spliterator();
OptionalDouble average = StreamSupport.stream(fixes, false).mapToDouble(BravoFix::getRideHeight).average();
if (average.isPresent()) {
return average.getAsDouble();
Distance sum = Distance.NULL;
int count = 0;
for (final BravoFix fix : getFixes(from, true, to, true)) {
sum = sum.add(fix.getRideHeight());
count++;
}
if (count > 0) {
result = sum.scale(1./count);
} else {
result = null;
}
} finally {
unlockAfterRead();
}
return null;
return result;
}
}
@@ -174,8 +174,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
}
if (timePointToUse != null) {
Distance d = getDistanceTraveled(timePointToUse);
long millis = timePointToUse.asMillis() - legStart.getTimePoint().asMillis();
result = d.inTime(millis);
result = d.inTime(legStart.getTimePoint().until(timePointToUse));
} else {
result = null;
}
@@ -184,7 +183,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
}
@Override
public Double getAverageRideHeight(TimePoint timePoint) {
public Distance getAverageRideHeight(TimePoint timePoint) {
MarkPassing legStart = getMarkPassingForLegStart();
if (legStart != null) {
BravoFixTrack<Competitor> track = getTrackedRace()
@@ -677,15 +676,17 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
}
@Override
public Double getRideHeight(TimePoint at) {
public Distance getRideHeight(TimePoint at) {
final Distance result;
if (hasStartedLeg(at)) {
TimePoint timePoint =hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
BravoFixTrack<Competitor> track = getTrackedRace()
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
return track == null ? null : track.getRideHeight(timePoint);
result = track == null ? null : track.getRideHeight(timePoint);
} else {
return null;
result = null;
}
return result;
}
@Override
@@ -1538,6 +1538,22 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
return result;
}
@Override
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) {
final Distance result;
BravoFixTrack<Competitor> track = getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
final Leg firstLeg;
final TrackedLegOfCompetitor firstTrackedLeg;
if (track != null && (firstLeg = getRace().getCourse().getFirstLeg()) != null && (firstTrackedLeg = getTrackedLeg(competitor, firstLeg)).hasStartedLeg(timePoint)) {
final TrackedLegOfCompetitor lastTrackedLeg = getTrackedLegFinishingAt(getRace().getCourse().getLastWaypoint()).getTrackedLeg(competitor);
TimePoint endTimePoint = lastTrackedLeg.hasFinishedLeg(timePoint) ? lastTrackedLeg.getFinishTime() : timePoint;
result = track.getAverageRideHeight(firstTrackedLeg.getStartTime(), endTimePoint);
} else {
result = null;
}
return result;
}
@Override
public TrackedLegOfCompetitor getCurrentLeg(Competitor competitor, TimePoint timePoint) {
// If the mark passing that starts a leg happened exactly at timePoint, the MarkPassingByTimeComparator won't consider
@@ -0,0 +1,24 @@
package com.sap.sailing.util;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.impl.MeterDistance;
public class RegattaUtil {
public static final Distance DEFAULT_BUOY_ZONE_RADIUS = new MeterDistance(15);
public static Distance getCalculatedRegattaBuoyZoneRadius(Regatta regatta, BoatClass boatClass) {
final double buoyZoneRadiusInHullLengths;
if (regatta != null && regatta.getBuoyZoneRadiusInHullLengths() != null) {
buoyZoneRadiusInHullLengths = regatta.getBuoyZoneRadiusInHullLengths();
} else {
buoyZoneRadiusInHullLengths = Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS;
}
final Distance boatHullLength = boatClass == null ? null : boatClass.getHullLength();
final Distance buyZoneRadius = boatHullLength == null ? DEFAULT_BUOY_ZONE_RADIUS
: boatHullLength.scale(buoyZoneRadiusInHullLengths);
return buyZoneRadius;
}
}
@@ -860,4 +860,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
public Iterable<RaceLog> getAttachedRaceLogs() {
return Collections.emptySet();
}
@Override
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) {
return null;
}
}
@@ -41,7 +41,7 @@ public class PingMarkViaRegattaLogTest {
Collections.singletonList(columnName), service);
Regatta regatta = service.createRegatta(RegattaImpl.getDefaultName("regatta", "Laser"), "Laser", /*startDate*/ null, /*endDate*/ null,
UUID.randomUUID(), Collections.<Series>singletonList(series),
false, new HighPoint(), UUID.randomUUID(), /* useStartTimeInference */ true,
false, new HighPoint(), UUID.randomUUID(), /*buoyZoneRadiusInHullLengths*/2.0, /* useStartTimeInference */ true,
/* controlTrackingFromStartAndFinishTimes */ false, RankingMetricsFactory.getRankingMetricConstructor(RankingMetrics.ONE_DESIGN));
RegattaLeaderboard leaderboard = service.addRegattaLeaderboard(regatta.getRegattaIdentifier(), "RegattaLeaderboard", new int[] {});
+259 -255
View File
@@ -1,256 +1,260 @@
/** Add css rules here for your application. */
/*
body #calendar:after
content: " ";
display: block;
height: 0;
clear: both;
visibility: hidden;
*/
.clearafter {
clear: both;
font-size: 1px;
height: 0px;
line-height: 0px;
margin: -1px 0 0;
width: 100%;
}
.cover {
left: -1000px;
overflow: hidden;
position: absolute;
top: -1000px;
}
body {
background: url(images/spectatorview-bg.png) no-repeat left top #ebebeb;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
line-height: 1;
font-size: 14px;
font-weight: 400;
padding: 85px 0 0 0;
margin: 0;
color: #3b3b3b;
}
a {
color: #2968c0;
text-decoration: underline;
}
table td,select,button {
line-height: 1;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
font-size: 14px;
}
.gwt-DialogBox {
z-index: 20;
}
.gwt-ToggleButton {
display: inline-block;
}
.gwt-PopupPanelGlass {
z-index: 19;
}
.contentOuterPanel {
background: #dddcdc;
margin: 0 12px 0 12px;
padding: 12px;
}
/** Example rules used by the template application (remove for your app) */
h1 {
font-size: 2em;
font-weight: bold;
color: #777777;
margin: 40px 0px 70px;
text-align: center;
}
table {
border-collapse: separate;
}
.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;
}
.LogoAndTitlePanel:after {
content: " ";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.RaceBoardHeaderPanel {
margin: 0 12px 18px 12px;
}
.sapLogo {
float: left;
margin: 13px 0 0 12px;
}
.sailingAnalyticsLabelPanel {
margin: 10px 0 0 0;
}
.sailingAnalyticsLabel {
float: left;
color: #fff;
font-size: 14px;
font-weight: bold;
margin: 15px 0 0 0;
}
.leaderboardLabel {
border-bottom: 2px solid #007dc0;
font-weight: bold;
padding: 0 0 6px 0;
}
.refreshAndSettings {
background: #f2f2f2;
border-bottom: 2px solid #fff;
border-top: 2px solid #fff;
height: 30px;
}
.refreshAndSettings table td {
padding: 0 10px 0 0;
}
.refreshAndSettings table table td {
padding: 0 0 0 0;
}
.race-name {
margin: 2px 10px 0 0;
line-height: 1.0;
float: left;
}
input.magnifier {
display: block;
margin: 3px auto 0 auto !important;
}
.footerShadowPanel {
position: fixed;
height: 50px;
bottom: 0;
background: url(images/footer_shadow.png) left bottom repeat-x;
width: 100%;
z-index: 2000;
}
.mainPanel {
width: 100%;
margin-left: 13px;
}
.errorLabel {
color: #FF0000;
}
.chart-importantMessage {
font-family: 'Open Sans',Arial,Verdana,sans-serif;
font-weight: 600;
font-size: 20px;
height: 70px;
margin: 0 auto;
padding: 49px 0 0 80px;
width: 400px;
color: #7c7c7c;
background: url(images/important-message.png) left center no-repeat;
}
.chart-busyMessage {
font-family: 'Open Sans',Arial,Verdana,sans-serif;
font-weight: 600;
font-size: 20px;
height: 70px;
margin: 0 auto;
padding: 49px 0 0 40px;
width: 400px;
color: #7c7c7c;
background: url(images/busy_indicator_circle.gif) left center no-repeat;
}
.dataMiningPanel {
margin-left: 10px;
margin-right: 10px;
}
.dataMiningPanel-filledElement {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center 0 transparent;
border-bottom-left-radius: 3px;
border-top-left-radius: 3px;
color: #FFFFFF;
cursor: pointer;
font-family: 'Open Sans',Verdana,Arial,sans-serif;
font-weight: 600;
font-size: 14px;
margin: 0 0 0 0;
padding: 2px 7px 2px 3px;
height: 18px;
display: inline-block;
text-decoration: none;
-moz-border-radius: 5px 0 0 5px;
-webkit-border-radius: 5px 0 0 5px;
-o-border-radius: 5px 0 0 5px;
-ms-border-radius: 5px 0 0 5px;
border-radius: 5px 0 0 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);
-o-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
-ms-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);
}
.settingsAnchor {
position: relative;
top: 9px;
}
.groupingProviderElement {
margin: 2px;
}
.statisticProviderElement {
margin: 2px;
}
.definitionProviderControls {
margin: 2px;
}
.definitionProviderControlsElements {
margin: 2px;
}
.queryDefinitionViewerContent {
background-color: white;
padding: 5px;
/** Add css rules here for your application. */
/*
body #calendar:after
content: " ";
display: block;
height: 0;
clear: both;
visibility: hidden;
*/
html {
font-size: 15px;
}
.clearafter {
clear: both;
font-size: 1px;
height: 0px;
line-height: 0px;
margin: -1px 0 0;
width: 100%;
}
.cover {
left: -1000px;
overflow: hidden;
position: absolute;
top: -1000px;
}
body {
background: url(images/spectatorview-bg.png) no-repeat left top #ebebeb;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
line-height: 1;
font-size: 14px;
font-weight: 400;
padding: 85px 0 0 0;
margin: 0;
color: #3b3b3b;
}
a {
color: #2968c0;
text-decoration: underline;
}
table td,select,button {
line-height: 1;
font-family: 'Open Sans', Arial, Verdana, sans-serif;
font-size: 14px;
}
.gwt-DialogBox {
z-index: 20;
}
.gwt-ToggleButton {
display: inline-block;
}
.gwt-PopupPanelGlass {
z-index: 19;
}
.contentOuterPanel {
background: #dddcdc;
margin: 0 12px 0 12px;
padding: 12px;
}
/** Example rules used by the template application (remove for your app) */
h1 {
font-size: 2em;
font-weight: bold;
color: #777777;
margin: 40px 0px 70px;
text-align: center;
}
table {
border-collapse: separate;
}
.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;
}
.LogoAndTitlePanel:after {
content: " ";
display: block;
height: 0;
clear: both;
visibility: hidden;
}
.RaceBoardHeaderPanel {
margin: 0 12px 18px 12px;
}
.sapLogo {
float: left;
margin: 13px 0 0 12px;
}
.sailingAnalyticsLabelPanel {
margin: 10px 0 0 0;
}
.sailingAnalyticsLabel {
float: left;
color: #fff;
font-size: 14px;
font-weight: bold;
margin: 15px 0 0 0;
}
.leaderboardLabel {
border-bottom: 2px solid #007dc0;
font-weight: bold;
padding: 0 0 6px 0;
}
.refreshAndSettings {
background: #f2f2f2;
border-bottom: 2px solid #fff;
border-top: 2px solid #fff;
height: 30px;
}
.refreshAndSettings table td {
padding: 0 10px 0 0;
}
.refreshAndSettings table table td {
padding: 0 0 0 0;
}
.race-name {
margin: 2px 10px 0 0;
line-height: 1.0;
float: left;
}
input.magnifier {
display: block;
margin: 3px auto 0 auto !important;
}
.footerShadowPanel {
position: fixed;
height: 50px;
bottom: 0;
background: url(images/footer_shadow.png) left bottom repeat-x;
width: 100%;
z-index: 2000;
}
.mainPanel {
width: 100%;
margin-left: 13px;
}
.errorLabel {
color: #FF0000;
}
.chart-importantMessage {
font-family: 'Open Sans',Arial,Verdana,sans-serif;
font-weight: 600;
font-size: 20px;
height: 70px;
margin: 0 auto;
padding: 49px 0 0 80px;
width: 400px;
color: #7c7c7c;
background: url(images/important-message.png) left center no-repeat;
}
.chart-busyMessage {
font-family: 'Open Sans',Arial,Verdana,sans-serif;
font-weight: 600;
font-size: 20px;
height: 70px;
margin: 0 auto;
padding: 49px 0 0 40px;
width: 400px;
color: #7c7c7c;
background: url(images/busy_indicator_circle.gif) left center no-repeat;
}
.dataMiningPanel {
margin-left: 10px;
margin-right: 10px;
}
.dataMiningPanel-filledElement {
background: url("images/btn-cta-orangeleft.png") repeat-x scroll center 0 transparent;
border-bottom-left-radius: 3px;
border-top-left-radius: 3px;
color: #FFFFFF;
cursor: pointer;
font-family: 'Open Sans',Verdana,Arial,sans-serif;
font-weight: 600;
font-size: 14px;
margin: 0 0 0 0;
padding: 2px 7px 2px 3px;
height: 18px;
display: inline-block;
text-decoration: none;
-moz-border-radius: 5px 0 0 5px;
-webkit-border-radius: 5px 0 0 5px;
-o-border-radius: 5px 0 0 5px;
-ms-border-radius: 5px 0 0 5px;
border-radius: 5px 0 0 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);
-o-box-shadow: 0 1px 3px rgba(0, 0, 0, 0.3);
-ms-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);
}
.settingsAnchor {
position: relative;
top: 9px;
}
.groupingProviderElement {
margin: 2px;
}
.statisticProviderElement {
margin: 2px;
}
.definitionProviderControls {
margin: 2px;
}
.definitionProviderControlsElements {
margin: 2px;
}
.queryDefinitionViewerContent {
background-color: white;
padding: 5px;
}
@@ -0,0 +1,4 @@
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 20 20"><path fill=
"#FFF" d="M10,2a8,8,0,1,1-8,8,8,8,0,0,1,8-8m0-2A10,10,0,1,0,20,10,10,10,0,0,0,
10,0h0ZM9.71,10l0.08,0L9.71,10V10m-2-3.43v7l3.07-1.74L13.86,10,10.78,8.26,7.71,
6.52h0Z"/></svg>

After

Width:  |  Height:  |  Size: 252 B

@@ -7,8 +7,16 @@ import com.google.gwt.event.shared.EventBus;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.rpc.AsyncCallback;
import com.google.gwt.user.client.ui.AcceptsOneWidget;
import com.sap.sailing.gwt.autoplay.client.shared.leaderboard.LeaderboardWithHeaderPerspectiveLifecycle;
import com.sap.sailing.gwt.autoplay.client.shared.leaderboard.LeaderboardWithHeaderPerspectiveSettings;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.raceboard.RaceBoardPerspectiveLifecycle;
import com.sap.sailing.gwt.ui.raceboard.RaceBoardPerspectiveSettings;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
import com.sap.sse.gwt.client.mvp.ErrorView;
import com.sap.sse.gwt.client.shared.perspective.PerspectiveLifecycleWithAllSettings;
import com.sap.sse.gwt.client.useragent.UserAgentDetails;
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
@@ -26,6 +34,17 @@ public class PlayerActivity extends AbstractActivity {
this.autoPlayController = null;
}
private StrippedLeaderboardDTO getSelectedLeaderboard(EventDTO event,String leaderBoardName) {
for (LeaderboardGroupDTO leaderboardGroup : event.getLeaderboardGroups()) {
for (StrippedLeaderboardDTO leaderboard : leaderboardGroup.getLeaderboards()) {
if (leaderboard.name.equals(leaderBoardName)) {
return leaderboard;
}
}
}
return null;
}
@Override
public void start(final AcceptsOneWidget panel, EventBus eventBus) {
final boolean showRaceDetails = GwtHttpRequestUtils.getBooleanParameter(PARAM_SHOW_RACE_DETAILS, false);
@@ -36,6 +55,16 @@ public class PlayerActivity extends AbstractActivity {
clientFactory.getSailingService().getEventById(eventUUID, true, new AsyncCallback<EventDTO>() {
@Override
public void onSuccess(final EventDTO event) {
//This place fixes a specific null pointer bug3950, once the settings are redone, this should be removed, and instead be done via playerplace (see comment there)
StrippedLeaderboardDTO leaderBoardDTO = getSelectedLeaderboard(event,playerPlace.getConfiguration().getLeaderboardName());
LeaderboardWithHeaderPerspectiveLifecycle leaderboardPerspectiveLifecycle = new LeaderboardWithHeaderPerspectiveLifecycle(leaderBoardDTO, StringMessages.INSTANCE);
PerspectiveLifecycleWithAllSettings<LeaderboardWithHeaderPerspectiveLifecycle, LeaderboardWithHeaderPerspectiveSettings> leaderboardPerspectiveLifecyclesAndSettings = new PerspectiveLifecycleWithAllSettings<>(leaderboardPerspectiveLifecycle, leaderboardPerspectiveLifecycle.createDefaultSettings());
RaceBoardPerspectiveLifecycle raceboardPerspectiveLifecycle = new RaceBoardPerspectiveLifecycle(leaderBoardDTO, StringMessages.INSTANCE);
PerspectiveLifecycleWithAllSettings<RaceBoardPerspectiveLifecycle, RaceBoardPerspectiveSettings> raceboardPerspectiveLifecyclesAndSettings = new PerspectiveLifecycleWithAllSettings<>(raceboardPerspectiveLifecycle, raceboardPerspectiveLifecycle.createDefaultSettings());
UserAgentDetails userAgent = new UserAgentDetails(Window.Navigator.getUserAgent());
PlayerView view = clientFactory.createPlayerView();
@@ -44,8 +73,8 @@ public class PlayerActivity extends AbstractActivity {
autoPlayController = new AutoPlayController(clientFactory.getSailingService(), clientFactory
.getMediaService(), clientFactory.getUserService(), clientFactory.getErrorReporter(),
playerPlace.getConfiguration(), userAgent, delayToLiveMillis, showRaceDetails, view,
playerPlace.getLeaderboardPerspectiveLifecycleWithAllSettings(),
playerPlace.getRaceboardPerspectiveLifecycleWithAllSettings());
leaderboardPerspectiveLifecyclesAndSettings,
raceboardPerspectiveLifecyclesAndSettings);
autoPlayController.updatePlayMode(AutoPlayModes.Leaderboard);
}
@@ -23,6 +23,8 @@ public class PlayerPlace extends AbstractBasePlace {
Integer timeToSwitchBeforeRaceStartInSeconds = Integer.valueOf(getParameter(AutoPlayerConfiguration.PARAM_TIME_TO_SWITCH_BEFORE_RACE_START));
this.playerConfiguration = new AutoPlayerConfiguration(eventUuidAsString, leaderboardName, fullscreen, timeToSwitchBeforeRaceStartInSeconds);
//these are currently regenerated in the PlayerActivity, based on the historydata. Once the settings are properly stored, this should be replaced.
this.leaderboardPerspectiveLifecycleWithAllSettings = null;
this.raceboardPerspectiveLifecycleWithAllSettings = null;
}
@@ -4,6 +4,7 @@ import java.util.ArrayList;
import java.util.Collections;
import java.util.Date;
import com.sap.sailing.domain.common.Distance;
import com.sap.sse.gwt.dispatch.shared.commands.DTO;
public class RegattaMetadataDTO extends RegattaReferenceDTO implements HasRegattaMetadata {
@@ -18,6 +19,7 @@ public class RegattaMetadataDTO extends RegattaReferenceDTO implements HasRegatt
private RegattaState state;
private boolean flexibleLeaderboard;
private RaceDataInfo raceDataInfo;
private Distance buoyZoneRadius;
public RegattaMetadataDTO() {
}
@@ -122,7 +124,15 @@ public class RegattaMetadataDTO extends RegattaReferenceDTO implements HasRegatt
public void setRaceDataInfo(RaceDataInfo raceDataInfo) {
this.raceDataInfo = raceDataInfo;
}
public Distance getBuoyZoneRadius() {
return buoyZoneRadius;
}
public void setBuoyZoneRadius(Distance buoyZoneRadius) {
this.buoyZoneRadius = buoyZoneRadius;
}
/**
* Holder class for flags, which inform about GPS, wind, video or audio data availability.
*/
@@ -34,7 +34,7 @@ public class GetFavoritesAction implements SailingAction<FavoritesResult> {
if (preferences != null) {
for (BoatClassNotificationPreference pref : preferences.getBoatClasses()) {
String name = pref.getBoatClass().getName(), displayName = pref.getBoatClass().getDisplayName();
selected.add(new BoatClassDTO(name, displayName, pref.getBoatClass().getHullLength().getMeters()));
selected.add(new BoatClassDTO(name, displayName, pref.getBoatClass().getHullLength()));
notifyAboutUpcomingRaces |= pref.isNotifyAboutUpcomingRaces();
notifyAboutResults |= pref.isNotifyAboutResults();
}
@@ -1,14 +1,14 @@
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:s="urn:import:com.sap.sailing.gwt.home.client.shared"
xmlns:dp="urn:import:com.sap.sailing.gwt.home.desktop.partials">
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<ui:with field="local_res" type="com.sap.sailing.gwt.home.desktop.partials.racelist.RaceListResources" />
<g:HTMLPanel addStyleNames="{local_res.css.raceslistlive}">
<h3 ui:field="titleUi"></h3>
<div style="display: none;" ui:field="infoUi"></div>
<div style="display: none; font-weight: bold; font-style: italic;" ui:field="noContentPlaceholderUi"></div>
<dp:racelist.AbstractRaceList ui:field="raceListUi"/>
</g:HTMLPanel>
<!DOCTYPE ui:UiBinder SYSTEM "http://dl.google.com/gwt/DTD/xhtml.ent">
<ui:UiBinder xmlns:ui="urn:ui:com.google.gwt.uibinder" xmlns:g="urn:import:com.google.gwt.user.client.ui"
xmlns:s="urn:import:com.sap.sailing.gwt.home.client.shared"
xmlns:dp="urn:import:com.sap.sailing.gwt.home.desktop.partials">
<ui:with field="i18n" type="com.sap.sailing.gwt.ui.client.StringMessages" />
<ui:with field="res" type="com.sap.sailing.gwt.common.client.SharedResources" />
<ui:with field="local_res" type="com.sap.sailing.gwt.home.desktop.partials.racelist.RaceListResources" />
<g:HTMLPanel addStyleNames="{local_res.css.raceslistlive}">
<h3 ui:field="titleUi"></h3>
<div style="display: none;" ui:field="infoUi"></div>
<div style="display: none; font-weight: bold; font-style: italic;" ui:field="noContentPlaceholderUi"></div>
<dp:racelist.AbstractRaceList ui:field="raceListUi"/>
</g:HTMLPanel>
</ui:UiBinder>
@@ -63,6 +63,7 @@
.raceviewerlaunchpad:hover {
cursor: pointer;
background: #0081b8;
text-decoration: none;
}
.raceviewerlaunchpadlive {
background: #e94a1b;
@@ -79,11 +80,29 @@
.raceviewerlaunchpadlive .raceviewerlaunchpad_content_item_icon svg {
fill: #e94a1b;
}
.raceviewerlaunchpadplanned {
background: #999999;
}
.raceviewerlaunchpadplanned:hover {
background: #8a8a8a;
}
.raceviewerlaunchpadplanned .raceviewerlaunchpad_content_item {
color: #999999;
}
.raceviewerlaunchpadplanned .raceviewerlaunchpad_content_item:hover {
background: #999999;
}
.raceviewerlaunchpadplanned .raceviewerlaunchpad_content_item_icon svg {
fill: #999999;
}
.raceviewerlaunchpad_icon {
margin-left: 0.666666666666667em;
width: 1.666666666666667em;
height: 1.333333333333333em;
}
.raceviewerlaunchpad_icon img {
height: 100%;
}
.raceviewerlaunchpad_icon svg {
width: 100%;
height: 100%;
@@ -1,85 +1,110 @@
package com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
import com.sap.sailing.gwt.home.communication.race.RaceMetadataDTO;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
import com.sap.sailing.gwt.home.desktop.places.event.EventView;
import com.sap.sailing.gwt.home.desktop.places.event.EventView.Presenter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sse.common.Util;
public class RaceviewerLaunchPadCell<T extends RaceMetadataDTO<?>> extends AbstractCell<T> {
interface CellTemplates extends SafeHtmlTemplates {
@Template("<div class=\"{0}\">{1}</div>")
SafeHtml raceNotTracked(String styleNames, String text);
@Template("<div class=\"{0}\"><div>{2}</div><div class=\"{1}\"><img src=\"images/home/launch-loupe.svg\"/></div>")
SafeHtml raceviewerLaunchPad(String styleNames, String iconStyleNames, String text);
}
private static final CellTemplates TEMPLATE = GWT.create(CellTemplates.class);
private static final StringMessages I18N = StringMessages.INSTANCE;
private final RaceviewerLaunchPadResources local_res = RaceviewerLaunchPadResources.INSTANCE;
private final String notTrackedStyleNames = local_res.css().raceviewerlaunchpad_not_tracked();
private final String analyzeStyleNames = local_res.css().raceviewerlaunchpad();
private final String liveStyleNames = Util.join(" ", analyzeStyleNames, local_res.css().raceviewerlaunchpadlive());
private final String iconStyleNames = local_res.css().raceviewerlaunchpad_icon();
private final EventView.Presenter presenter;
private final PopupPanel panel = new PopupPanel(true, false);
public RaceviewerLaunchPadCell(Presenter presenter) {
super(BrowserEvents.CLICK);
local_res.css().ensureInjected();
this.presenter = presenter;
}
@Override
public void onBrowserEvent(Context context, final Element parent, T data, NativeEvent event,
ValueUpdater<T> valueUpdater) {
if (data.hasValidTrackingData() && BrowserEvents.CLICK.equals(event.getType())
&& parent.getFirstChildElement().isOrHasChild(Element.as(event.getEventTarget()))) {
panel.setWidget(new RaceviewerLaunchPad(data, panel) {
@Override
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String mode) {
return presenter.getRaceViewerURL(data, mode);
}
});
panel.setPopupPositionAndShow(new PositionCallback() {
@Override
public void setPosition(int offsetWidth, int offsetHeight) {
int alignBottom = parent.getAbsoluteTop() + parent.getOffsetHeight() - offsetHeight;
int top = (alignBottom - Window.getScrollTop() < 0 ? parent.getAbsoluteTop() - 1 : alignBottom + 1);
panel.setPopupPosition(parent.getAbsoluteRight() + 1 - offsetWidth, top);
panel.getElement().scrollIntoView();
}
});
return;
}
super.onBrowserEvent(context, parent, data, event, valueUpdater);
}
@Override
public void render(Context context, T data, SafeHtmlBuilder sb) {
if (data.hasValidTrackingData()) {
String styleNames = data.isFinished() ? analyzeStyleNames : liveStyleNames;
sb.append(TEMPLATE.raceviewerLaunchPad(styleNames, iconStyleNames, I18N.raceDetailsToShow()));
} else {
package com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad;
import com.google.gwt.cell.client.AbstractCell;
import com.google.gwt.cell.client.ValueUpdater;
import com.google.gwt.core.client.GWT;
import com.google.gwt.dom.client.BrowserEvents;
import com.google.gwt.dom.client.Element;
import com.google.gwt.dom.client.NativeEvent;
import com.google.gwt.safehtml.client.SafeHtmlTemplates;
import com.google.gwt.safehtml.shared.SafeHtml;
import com.google.gwt.safehtml.shared.SafeHtmlBuilder;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.PopupPanel.PositionCallback;
import com.sap.sailing.gwt.home.communication.race.RaceMetadataDTO;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
import com.sap.sailing.gwt.home.desktop.places.event.EventView;
import com.sap.sailing.gwt.home.desktop.places.event.EventView.Presenter;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.raceboard.RaceBoardModes;
import com.sap.sse.common.Util;
public class RaceviewerLaunchPadCell<T extends RaceMetadataDTO<?>> extends AbstractCell<T> {
interface CellTemplates extends SafeHtmlTemplates {
@Template("<div class=\"{0}\">{1}</div>")
SafeHtml raceNotTracked(String styleNames, String text);
@Template("<div class=\"{0}\"><div>{2}</div><div class=\"{1}\"><img src=\"images/home/launch-loupe.svg\"/></div>")
SafeHtml raceviewerLaunchPad(String styleNames, String iconStyleNames, String text);
@Template("<a href=\"{4}\" target=\"_blank\" class=\"{0}\"><div>{2}</div><div class=\"{1}\"><img src=\"images/home/{3}.svg\"/></div></a> ")
SafeHtml standaloneButton(String styleNames, String iconStyleNames, String text, String icon, String link);
}
private static final CellTemplates TEMPLATE = GWT.create(CellTemplates.class);
private static final StringMessages I18N = StringMessages.INSTANCE;
private final RaceviewerLaunchPadResources local_res = RaceviewerLaunchPadResources.INSTANCE;
private final String notTrackedStyleNames = local_res.css().raceviewerlaunchpad_not_tracked();
private final String analyzeStyleNames = local_res.css().raceviewerlaunchpad();
private final String plannedStyleNames = Util.join(" ", analyzeStyleNames, local_res.css().raceviewerlaunchpadplanned());
private final String liveStyleNames = Util.join(" ", analyzeStyleNames, local_res.css().raceviewerlaunchpadlive());
private final String iconStyleNames = local_res.css().raceviewerlaunchpad_icon();
private final EventView.Presenter presenter;
private final PopupPanel panel = new PopupPanel(true, false);
public RaceviewerLaunchPadCell(Presenter presenter) {
super(BrowserEvents.CLICK);
local_res.css().ensureInjected();
this.presenter = presenter;
}
@Override
public void onBrowserEvent(Context context, final Element parent, T data, NativeEvent event,
ValueUpdater<T> valueUpdater) {
if (!renderAsDirectLinkButton(data)) {
if (data.hasValidTrackingData() && BrowserEvents.CLICK.equals(event.getType())
&& parent.getFirstChildElement().isOrHasChild(Element.as(event.getEventTarget()))) {
panel.setWidget(createLaunchPad(parent.getFirstChildElement(), data));
panel.setPopupPositionAndShow(new PositionCallback() {
@Override
public void setPosition(int offsetWidth, int offsetHeight) {
int alignBottom = parent.getAbsoluteTop() + parent.getOffsetHeight() - offsetHeight;
int top = (alignBottom - Window.getScrollTop() < 0 ? parent.getAbsoluteTop() - 1 : alignBottom + 1);
panel.setPopupPosition(parent.getAbsoluteRight() + 1 - offsetWidth, top);
panel.getElement().scrollIntoView();
}
});
}
return;
}
super.onBrowserEvent(context, parent, data, event, valueUpdater);
}
@Override
public void render(Context context, T data, SafeHtmlBuilder sb) {
if (data.hasValidTrackingData()) {
if (renderAsDirectLinkButton(data)) {
sb.append(TEMPLATE.standaloneButton(plannedStyleNames, iconStyleNames, I18N.watchLive(),
"launch-play", presenter.getRaceViewerURL(data, RaceBoardModes.PLAYER.name())));
} else {
String styleNames = data.isFinished() ? analyzeStyleNames : liveStyleNames;
sb.append(TEMPLATE.raceviewerLaunchPad(styleNames, iconStyleNames, I18N.raceDetailsToShow()));
}
} else {
sb.append(TEMPLATE.raceNotTracked(notTrackedStyleNames, I18N.eventRegattaRaceNotTracked()));
}
}
}
}
}
/**
* Creates a {@link RaceviewerLaunchPad} for the given data object.
*/
private RaceviewerLaunchPad createLaunchPad(Element button, T data) {
RaceviewerLaunchPad launchPad = new RaceviewerLaunchPad(data, panel) {
@Override
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String raceBoardMode) {
return presenter.getRaceViewerURL(data, raceBoardMode);
}
};
return launchPad;
}
/** Determine whether a direct link button should be/is rendered instead of a menu popup. */
private boolean renderAsDirectLinkButton(T data) {
return !data.isFinished() && !data.isRunning();
}
}
@@ -18,6 +18,7 @@ public interface RaceviewerLaunchPadResources extends ClientBundle {
String raceviewerlaunchpad_date();
String raceviewerlaunchpad_cta();
String raceviewerlaunchpadlive();
String raceviewerlaunchpadplanned();
String raceviewerlaunchpad_content_item();
String raceviewerlaunchpad_content_item_icon();
String raceviewerlaunchpad_icon();
@@ -174,6 +174,9 @@
.fleet_races_raceplanned:hover {
background-color: #8a8a8a;
}
.fleet_races_raceplanned .fleet_races_race_name:after {
background: url("images/home/launch-play.svg") no-repeat center center;
}
.fleet_races_raceuntracked .fleet_races_race_name:after {
display: none;
}
@@ -55,7 +55,7 @@ public class TabletAndDesktopRegattaEventView extends Composite implements Event
if(currentPresenter.getEventDTO().getType() == EventType.SERIES_EVENT) {
final PlaceNavigation<SeriesDefaultPlace> currentEventSeriesNavigation = currentPresenter.getCurrentEventSeriesNavigation();
Anchor seriesAnchor = new Anchor(currentPresenter.getEventDTO().getSeriesName());
Anchor seriesAnchor = new Anchor(i18n.overallLeaderboardSelection());
seriesAnchor.setHref(currentEventSeriesNavigation.getTargetUrl());
seriesAnchor.addClickHandler(new ClickHandler() {
@Override
@@ -69,6 +69,7 @@
.regattacompetition_phase_fleetfullwidth .regattacompetition_phase_fleet_race_arrow {
display: block;
width: 1.6666666667em;
height: 1.4444444444em;
}
.regattacompetition_phase_fleet_title {
font-size: 1.066666666666667em;
@@ -117,9 +118,6 @@
background-color: #999;
font-style: italic;
}
.regattacompetition_phase_fleet_raceplanned .regattacompetition_phase_fleet_race_title:after {
display: none;
}
.regattacompetition_phase_fleet_racelive.regattacompetition_phase_fleet_raceuntracked {
color: #e94a1b;
background-color: #fff;
@@ -150,6 +148,7 @@
}
.regattacompetition_phase_fleet_race_arrow {
display: none;
background: url("images/home/launch-loupe.svg") no-repeat center center;
position: absolute;
top: 50%;
right: 1em;
@@ -158,6 +157,12 @@
-ms-transform: translateY(-50%);
transform: translateY(-50%);
}
.regattacompetition_phase_fleet_raceplanned .regattacompetition_phase_fleet_race_title:after {
background: url("images/home/launch-play.svg") no-repeat center center;
}
.regattacompetition_phase_fleet_raceplanned .regattacompetition_phase_fleet_race_arrow {
background: url("images/home/launch-play.svg") no-repeat center center;
}
.regattacompetition_phase_fleet_raceuntracked .regattacompetition_phase_fleet_race_arrow {
display: none;
}
@@ -10,6 +10,6 @@
<div class="{local_res.css.regattacompetition_phase_fleet_race_state}" ui:field="raceStateUi"></div>
<div class="{local_res.css.regattacompetition_phase_fleet_race_date}" ui:field="raceDateUi"></div>
</div>
<img src="images/home/launch-loupe.svg" class="{local_res.css.regattacompetition_phase_fleet_race_arrow}"></img>
<div class="{local_res.css.regattacompetition_phase_fleet_race_arrow}"></div>
</div>
</ui:UiBinder>
@@ -274,12 +274,9 @@ public abstract class AbstractEventActivity<PLACE extends AbstractEventPlace> ex
}
linkParams.put("raceName", trackedRaceName);
linkParams.put("regattaName", regattaName);
// TODO this must only be forwarded if there is a logged-on user
// linkParams.put(RaceBoardViewConfiguration.PARAM_CAN_REPLAY_DURING_LIVE_RACES, "true");
return linkParams;
}
public String getRegattaId() {
String regattaId = place.getRegattaId();
if(regattaId != null) {
@@ -9,11 +9,13 @@ import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Event;
import com.sap.sailing.domain.base.EventBase;
import com.sap.sailing.domain.base.Fleet;
import com.sap.sailing.domain.base.RaceColumn;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
import com.sap.sailing.domain.common.dto.LeaderboardDTO;
import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO;
@@ -33,6 +35,7 @@ import com.sap.sailing.gwt.home.communication.regatta.RegattaWithProgressDTO;
import com.sap.sailing.gwt.home.server.EventActionUtil.RaceCallback;
import com.sap.sailing.gwt.server.HomeServiceUtil;
import com.sap.sailing.server.RacingEventService;
import com.sap.sailing.util.RegattaUtil;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -248,8 +251,15 @@ public class LeaderboardContext {
RegattaRaceDataInfoCalculator regattaRaceDataInfoCalculator = new RegattaRaceDataInfoCalculator();
forRaces(regattaRaceDataInfoCalculator);
regattaDTO.setRaceDataInfo(regattaRaceDataInfoCalculator.getRaceDataInfo());
regattaDTO.setBuoyZoneRadius(getRegattaBuoyZoneRadius());
}
private Distance getRegattaBuoyZoneRadius() {
Regatta regatta = service.getRegattaByName(getLeaderboardName());
BoatClass boatClass = HomeServiceUtil.getBoatClass(leaderboard);
return RegattaUtil.getCalculatedRegattaBuoyZoneRadius(regatta, boatClass);
}
private static boolean hasMultipleLeaderboardGroups(EventBase event) {
return Util.size(event.getLeaderboardGroups()) > 1;
}
@@ -40,7 +40,7 @@ public abstract class AbstractSuggestedMultiSelectionBoatClassDataProvider exten
List<BoatClassMasterdata> boatClasses = Arrays.asList(BoatClassMasterdata.values());
List<BoatClassDTO> suggestionItems = new ArrayList<>();
for (BoatClassMasterdata bcm : filter.applyFilter(queryTokens, boatClasses)) {
suggestionItems.add(new BoatClassDTO(bcm.getDisplayName(), bcm.getHullLength().getMeters()));
suggestionItems.add(new BoatClassDTO(bcm.getDisplayName(), bcm.getHullLength()));
}
callback.setSuggestionItems(suggestionItems);
}
@@ -1,111 +1,122 @@
package com.sap.sailing.gwt.home.shared.partials.regattacompetition;
import static com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil.shortTimeFormatter;
import static com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil.weekdayMonthAbbrDayDateFormatter;
import static com.sap.sse.common.impl.MillisecondsTimePoint.now;
import java.util.Date;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.text.client.DateTimeFormatRenderer;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.common.client.DateUtil;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceTrackingState;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceViewState;
import com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad.RaceviewerLaunchPad;
import com.sap.sailing.gwt.home.shared.partials.regattacompetition.RegattaCompetitionView.RegattaCompetitionRaceView;
import com.sap.sailing.gwt.ui.client.StringMessages;
public abstract class AbstractRegattaCompetitionFleetRace extends Widget implements RegattaCompetitionRaceView {
private static final StringMessages I18N = StringMessages.INSTANCE;
protected final Element mainElement;
private final SimpleRaceMetadataDTO race;
private final RegattaCompetitionPresenter presenter;
private final PopupPanel panel = new PopupPanel(true, false);
protected AbstractRegattaCompetitionFleetRace(final SimpleRaceMetadataDTO race,
RegattaCompetitionPresenter presenter) {
this.race = race;
this.presenter = presenter;
this.mainElement = getMainUiElement();
setupRaceState(race.getTrackingState(), race.getViewState());
getRaceNameUiElement().setInnerText(race.getRaceName());
setupRaceStart(race.getStart());
setElement(mainElement);
if (race.hasValidTrackingData()) {
sinkEvents(Event.ONCLICK);
}
}
@Override
public void onBrowserEvent(Event event) {
if (race.hasValidTrackingData() && event.getTypeInt() == Event.ONCLICK) {
this.getElement().scrollIntoView();
panel.setWidget(new RaceviewerLaunchPad(race, panel) {
@Override
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String mode) {
return presenter.getRaceViewerURL(data, mode);
}
});
panel.setVisible(false);
panel.show();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
Widget button = AbstractRegattaCompetitionFleetRace.this, panelContent = panel.getWidget();
int alignRight = button.getAbsoluteLeft() + button.getOffsetWidth() - panelContent.getOffsetWidth();
int left = (alignRight - Window.getScrollLeft() < 0 ? button.getAbsoluteLeft() - 1 : alignRight + 1);
int alignBottom = button.getAbsoluteTop() + button.getOffsetHeight() - panelContent.getOffsetHeight();
int top = (alignBottom - Window.getScrollTop() < 0 ? button.getAbsoluteTop() - 1 : alignBottom + 1);
panel.setPopupPosition(left, top);
panel.setVisible(true);
}
});
}
}
private void setupRaceState(RaceTrackingState trackingState, RaceViewState viewState) {
boolean isUntrackedRace = trackingState != RaceTrackingState.TRACKED_VALID_DATA;
if (viewState == RaceViewState.RUNNING) {
mainElement.addClassName(getRaceLiveStyleName());
getRaceStateUiElement().setInnerText(isUntrackedRace ? I18N.live() : I18N.actionWatch());
} else if (viewState == RaceViewState.FINISHED) {
getRaceStateUiElement().setInnerText(isUntrackedRace ? I18N.raceIsFinished() : I18N.actionAnalyze());
} else {
mainElement.addClassName(getRacePlannedStyleName());
if (viewState == RaceViewState.SCHEDULED) getRaceStateUiElement().setInnerText(I18N.raceIsPlanned());
else getRaceStateUiElement().setInnerText(viewState.getLabel());
}
setStyleName(mainElement, getRaceUntrackedStyleName(), isUntrackedRace);
}
private void setupRaceStart(Date startDate) {
if (startDate != null) {
boolean showTime = DateUtil.isSameDayOfMonth(now().asDate(), startDate);
DateTimeFormatRenderer renderer = showTime ? shortTimeFormatter : weekdayMonthAbbrDayDateFormatter;
getRaceDateUiElement().setInnerText(renderer.render(startDate));
}
}
protected abstract Element getMainUiElement();
protected abstract Element getRaceNameUiElement();
protected abstract Element getRaceStateUiElement();
protected abstract Element getRaceDateUiElement();
protected abstract String getRaceLiveStyleName();
protected abstract String getRacePlannedStyleName();
protected abstract String getRaceUntrackedStyleName();
}
package com.sap.sailing.gwt.home.shared.partials.regattacompetition;
import static com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil.shortTimeFormatter;
import static com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil.weekdayMonthAbbrDayDateFormatter;
import static com.sap.sse.common.impl.MillisecondsTimePoint.now;
import java.util.Date;
import com.google.gwt.core.client.Scheduler;
import com.google.gwt.core.client.Scheduler.ScheduledCommand;
import com.google.gwt.dom.client.Element;
import com.google.gwt.text.client.DateTimeFormatRenderer;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.Window;
import com.google.gwt.user.client.ui.PopupPanel;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.common.client.DateUtil;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceTrackingState;
import com.sap.sailing.gwt.home.communication.race.SimpleRaceMetadataDTO.RaceViewState;
import com.sap.sailing.gwt.home.desktop.partials.raceviewerlaunchpad.RaceviewerLaunchPad;
import com.sap.sailing.gwt.home.shared.partials.regattacompetition.RegattaCompetitionView.RegattaCompetitionRaceView;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.raceboard.RaceBoardModes;
public abstract class AbstractRegattaCompetitionFleetRace extends Widget implements RegattaCompetitionRaceView {
private static final StringMessages I18N = StringMessages.INSTANCE;
protected final Element mainElement;
private final SimpleRaceMetadataDTO race;
private final RegattaCompetitionPresenter presenter;
private final PopupPanel panel = new PopupPanel(true, false);
protected AbstractRegattaCompetitionFleetRace(final SimpleRaceMetadataDTO race,
RegattaCompetitionPresenter presenter) {
this.race = race;
this.presenter = presenter;
this.mainElement = getMainUiElement();
setupRaceState(race.getTrackingState(), race.getViewState());
getRaceNameUiElement().setInnerText(race.getRaceName());
setupRaceStart(race.getStart());
setElement(mainElement);
if (race.hasValidTrackingData()) {
sinkEvents(Event.ONCLICK);
}
}
@Override
public void onBrowserEvent(Event event) {
// Only handle click events if valid tracking data is available
if (!race.hasValidTrackingData() || event.getTypeInt() != Event.ONCLICK) {
return;
}
// If rendered as direct link button, open link in new tab directly instead of showing the menu popup
if (!race.isFinished() && !race.isRunning()) {
Window.open(presenter.getRaceViewerURL(race, RaceBoardModes.PLAYER.name()), "_blank", "");
return;
}
// Show menu popup relative to button by ensuring it stays with the viewport
this.getElement().scrollIntoView();
panel.setWidget(new RaceviewerLaunchPad(race, panel) {
@Override
protected String getRaceViewerURL(SimpleRaceMetadataDTO data, String mode) {
return presenter.getRaceViewerURL(data, mode);
}
});
panel.setVisible(false);
panel.show();
Scheduler.get().scheduleDeferred(new ScheduledCommand() {
@Override
public void execute() {
Widget button = AbstractRegattaCompetitionFleetRace.this, panelContent = panel.getWidget();
int alignRight = button.getAbsoluteLeft() + button.getOffsetWidth() - panelContent.getOffsetWidth();
int left = (alignRight - Window.getScrollLeft() < 0 ? button.getAbsoluteLeft() - 1 : alignRight + 1);
int alignBottom = button.getAbsoluteTop() + button.getOffsetHeight() - panelContent.getOffsetHeight();
int top = (alignBottom - Window.getScrollTop() < 0 ? button.getAbsoluteTop() - 1 : alignBottom + 1);
panel.setPopupPosition(left, top);
panel.setVisible(true);
}
});
}
private void setupRaceState(RaceTrackingState trackingState, RaceViewState viewState) {
boolean isUntrackedRace = trackingState != RaceTrackingState.TRACKED_VALID_DATA;
if (viewState == RaceViewState.RUNNING) {
mainElement.addClassName(getRaceLiveStyleName());
getRaceStateUiElement().setInnerText(isUntrackedRace ? I18N.live() : I18N.actionWatch());
} else if (viewState == RaceViewState.FINISHED) {
getRaceStateUiElement().setInnerText(isUntrackedRace ? I18N.raceIsFinished() : I18N.actionAnalyze());
} else {
mainElement.addClassName(getRacePlannedStyleName());
if (viewState == RaceViewState.SCHEDULED) getRaceStateUiElement().setInnerText(I18N.raceIsPlanned());
else getRaceStateUiElement().setInnerText(viewState.getLabel());
}
setStyleName(mainElement, getRaceUntrackedStyleName(), isUntrackedRace);
}
private void setupRaceStart(Date startDate) {
if (startDate != null) {
boolean showTime = DateUtil.isSameDayOfMonth(now().asDate(), startDate);
DateTimeFormatRenderer renderer = showTime ? shortTimeFormatter : weekdayMonthAbbrDayDateFormatter;
getRaceDateUiElement().setInnerText(renderer.render(startDate));
}
}
protected abstract Element getMainUiElement();
protected abstract Element getRaceNameUiElement();
protected abstract Element getRaceStateUiElement();
protected abstract Element getRaceDateUiElement();
protected abstract String getRaceLiveStyleName();
protected abstract String getRacePlannedStyleName();
protected abstract String getRaceUntrackedStyleName();
}
@@ -937,6 +937,7 @@ public class RegattaRaceStatesComponent extends SimplePanel implements Component
}
return null;
}
private String createRegattaLink(RegattaOverviewEntryDTO entryDTO) {
String leaderboardLink = EntryPointLinkFactory.createLeaderboardTabLink(eventId.toString(), entryDTO.regattaName);
return leaderboardLink;
@@ -228,7 +228,7 @@ public final class HomeServiceUtil {
return boatClass == null ? null : boatClass.getName();
}
private static BoatClass getBoatClass(Leaderboard leaderboard) {
public static BoatClass getBoatClass(Leaderboard leaderboard) {
if(leaderboard instanceof RegattaLeaderboard) {
RegattaLeaderboard regattaLeaderboard = (RegattaLeaderboard) leaderboard;
BoatClass boatClassFromRegatta = regattaLeaderboard.getRegatta().getBoatClass();
@@ -709,7 +709,7 @@ public abstract class AbstractLeaderboardConfigPanel extends FormPanel implement
* Looks up the regatta for the selected leaderboard by name in {@link #allRegattas}
*/
protected RegattaDTO getSelectedRegatta() {
final String regattaName = getSelectedLeaderboard().regattaName;
final String regattaName = getSelectedLeaderboard() == null ? "" : getSelectedLeaderboard().regattaName;
return getRegattaByName(regattaName);
}
@@ -9,6 +9,7 @@ import com.google.gwt.dom.client.SelectElement;
import com.google.gwt.event.dom.client.ChangeEvent;
import com.google.gwt.event.dom.client.ChangeHandler;
import com.google.gwt.user.client.ui.CheckBox;
import com.google.gwt.user.client.ui.DoubleBox;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.ListBox;
@@ -42,7 +43,7 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
protected StringMessages stringMessages;
private final RegattaDTO regatta;
protected final BetterDateTimeBox startDateBox;
protected final BetterDateTimeBox endDateBox;
protected final ListBox scoringSchemeListBox;
@@ -50,6 +51,7 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
protected final ListBox sailingEventsListBox;
protected final CheckBox useStartTimeInferenceCheckBox;
protected final CheckBox controlTrackingFromStartAndFinishTimesCheckBox;
protected final DoubleBox buoyZoneRadiusInHullLengthsDoubleBox;
protected final ListEditorComposite<SeriesDTO> seriesEditor;
private final ListBox rankingMetricListBox;
@@ -93,7 +95,10 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
controlTrackingFromStartAndFinishTimesCheckBox = createCheckbox(stringMessages.controlTrackingFromStartAndFinishTimes());
controlTrackingFromStartAndFinishTimesCheckBox.ensureDebugId("ControlTrackingFromStartAndFinishTimesCheckBox");
controlTrackingFromStartAndFinishTimesCheckBox.setValue(regatta.controlTrackingFromStartAndFinishTimes);
buoyZoneRadiusInHullLengthsDoubleBox = createDoubleBox(regatta.buoyZoneRadiusInHullLengths, 10);
buoyZoneRadiusInHullLengthsDoubleBox.ensureDebugId("BuoyZoneRadiusInHullLengthsDoubleBox");
courseAreaListBox = createListBox(false);
courseAreaListBox.ensureDebugId("CourseAreaListBox");
courseAreaListBox.setEnabled(false);
@@ -142,7 +147,7 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(8, 2);
Grid formGrid = new Grid(9, 2);
panel.add(formGrid);
formGrid.setWidget(0, 0, new Label(stringMessages.timeZone() + ":"));
@@ -161,6 +166,8 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
formGrid.setWidget(6, 1, useStartTimeInferenceCheckBox);
formGrid.setWidget(7, 0, new Label(stringMessages.controlTrackingFromStartAndFinishTimes() + ":"));
formGrid.setWidget(7, 1, controlTrackingFromStartAndFinishTimesCheckBox);
formGrid.setWidget(8, 0, new Label(stringMessages.buoyZoneRadiusInHullLengths() + ":"));
formGrid.setWidget(8, 1, buoyZoneRadiusInHullLengthsDoubleBox);
setupAdditionalWidgetsOnPanel(panel, formGrid);
return panel;
}
@@ -285,6 +292,7 @@ public abstract class AbstractRegattaWithSeriesAndFleetsDialog<T> extends DataEn
result.scoringScheme = getSelectedScoringSchemeType();
result.useStartTimeInference = useStartTimeInferenceCheckBox.getValue();
result.controlTrackingFromStartAndFinishTimes = controlTrackingFromStartAndFinishTimesCheckBox.getValue();
result.buoyZoneRadiusInHullLengths = buoyZoneRadiusInHullLengthsDoubleBox.getValue();
setCourseAreaInRegatta(result);
result.series = getSeriesEditor().getValue();
return result;
@@ -13,6 +13,7 @@ import com.google.gwt.user.client.ui.ListBox;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.dto.BoatClassDTO;
import com.sap.sailing.domain.common.dto.BoatDTO;
import com.sap.sailing.domain.common.dto.CompetitorDTO;
@@ -187,7 +188,7 @@ public class CompetitorEditDialog extends DataEntryDialog<CompetitorDTO> {
color = new InvalidColor(iae);
}
}
BoatClassDTO boatClass = new BoatClassDTO(boatClassName.getValue(), 0);
BoatClassDTO boatClass = new BoatClassDTO(boatClassName.getValue(), Distance.NULL);
BoatDTO boat = new BoatDTO(name.getText(), sailId.getText());
CompetitorDTO result = new CompetitorDTOImpl(name.getText(), color, email.getText(),
/* twoLetterIsoCountryCode */ null,
@@ -9,6 +9,7 @@ import com.google.gwt.user.client.ui.HorizontalPanel;
import com.google.gwt.user.client.ui.Widget;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.gwt.ui.adminconsole.AbstractLeaderboardConfigPanel.RaceColumnDTOAndFleetDTOWithNameBasedEquality;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -27,7 +28,7 @@ public class CopyCourseAndCompetitorsDialog extends DataEntryDialog<CourseAndCom
public CopyCourseAndCompetitorsDialog(SailingServiceAsync sailingService, ErrorReporter errorReporter, final StringMessages stringMessages,
Collection<RaceColumnDTOAndFleetDTOWithNameBasedEquality> races,
String leaderboardName, DialogCallback<CourseAndCompetitorCopyOperation> dialogCallback) {
String leaderboardName, Distance buoyZoneRadius, DialogCallback<CourseAndCompetitorCopyOperation> dialogCallback) {
super(stringMessages.selectRaces(), stringMessages.selectRaces(), stringMessages.ok(), stringMessages.cancel(),
new Validator<CourseAndCompetitorCopyOperation>() {
@Override
@@ -66,7 +66,7 @@ public class CreateRegattaCallback implements DialogCallback<RegattaDTO>{
sailingService.createRegatta(newRegatta.getName(), newRegatta.boatClass==null?null:newRegatta.boatClass.getName(),
newRegatta.startDate, newRegatta.endDate,
new RegattaCreationParametersDTO(seriesStructure), true,
newRegatta.scoringScheme, newRegatta.defaultCourseAreaUuid, newRegatta.useStartTimeInference,
newRegatta.scoringScheme, newRegatta.defaultCourseAreaUuid, newRegatta.buoyZoneRadiusInHullLengths, newRegatta.useStartTimeInference,
newRegatta.controlTrackingFromStartAndFinishTimes,
newRegatta.rankingMetricType, new AsyncCallback<RegattaDTO>() {
@Override
@@ -41,7 +41,7 @@ public class DeviceConfigurationQRIdentifierDialog extends DialogBox {
Window.alert(stringMessages.notCapableOfGeneratingACodeForIdentifier());
} else if (!identifierBox.getValue().isEmpty() && !serverBox.getValue().isEmpty()) {
String apkUrl = getServerUrlWithoutFinalSlash() + rcAppApkPath;
return DeviceConfigurationQRCodeUtils.composeQRContent(URL.encodeQueryString(identifierBox.getValue()), apkUrl, accessToken);
return DeviceConfigurationQRCodeUtils.composeQRContent(URL.encodeQueryString(identifierBox.getValue()), apkUrl, URL.encodeQueryString(accessToken));
}
return null;
}
@@ -37,14 +37,11 @@ public class EventCreateDialog extends EventDialog {
endDateBox = createDateTimeBox(now);
endDateBox.setFormat("dd/mm/yyyy hh:ii");
endDateBox.ensureDebugId("EndDateTimeBox");
officialWebsiteURLEntryField = createTextBox(null);
officialWebsiteURLEntryField.setVisibleLength(50);
baseURLEntryField = createTextBox(null);
baseURLEntryField.setVisibleLength(50);
sailorsInfoWebsiteURLEntryFields = createTextBoxesForLocalesAndDefault(Collections.<String, String> emptyMap());
isPublicCheckBox = createCheckbox("");
isPublicCheckBox.setValue(false);
isPublicCheckBox.ensureDebugId("IsPublicCheckBox");
baseURLEntryField = createTextBox(null);
baseURLEntryField.setVisibleLength(50);
imagesListComposite.fillImages(Collections.<ImageDTO>emptyList());
videosListComposite.fillVideos(Collections.<VideoDTO>emptyList());
// add default course area
@@ -5,7 +5,6 @@ import java.util.Collection;
import java.util.Collections;
import java.util.Date;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
@@ -32,7 +31,6 @@ import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
import com.sap.sailing.gwt.ui.shared.VenueDTO;
import com.sap.sse.gwt.client.GWTLocaleUtil;
import com.sap.sse.gwt.client.IconResources;
import com.sap.sse.gwt.client.controls.listedit.GenericStringListInlineEditorComposite;
import com.sap.sse.gwt.client.controls.listedit.StringConstantsListEditorComposite;
@@ -48,14 +46,13 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
protected BetterDateTimeBox endDateBox;
protected CheckBox isPublicCheckBox;
protected UUID id;
protected TextBox officialWebsiteURLEntryField;
protected TextBox baseURLEntryField;
protected Map<String, TextBox> sailorsInfoWebsiteURLEntryFields = new HashMap<>();
protected CourseAreaListInlineEditorComposite courseAreaNameList;
protected StringConstantsListEditorComposite leaderboardGroupList;
protected Map<String, LeaderboardGroupDTO> availableLeaderboardGroupsByName;
protected ImagesListComposite imagesListComposite;
protected VideosListComposite videosListComposite;
protected ExternalLinksComposite externalLinksComposite;
protected static class EventParameterValidator implements Validator<EventDTO> {
@@ -162,6 +159,7 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
imagesListComposite = new ImagesListComposite(sailingService, stringMessages);
videosListComposite = new VideosListComposite(stringMessages);
externalLinksComposite = new ExternalLinksComposite(stringMessages);
}
@Override
@@ -176,13 +174,9 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
}
result.setName(nameEntryField.getText());
result.setDescription(descriptionEntryField.getText());
result.setOfficialWebsiteURL(officialWebsiteURLEntryField.getText().trim().isEmpty() ? null : officialWebsiteURLEntryField.getText().trim());
result.setOfficialWebsiteURL(externalLinksComposite.getOfficialWebsiteURLValue());
result.setBaseURL(baseURLEntryField.getText().trim().isEmpty() ? null : baseURLEntryField.getText().trim());
for (Map.Entry<String, TextBox> sailorsInfoWebsiteUrlEntry : sailorsInfoWebsiteURLEntryFields.entrySet()) {
TextBox sailorsInfoWebsiteURLEntryField = sailorsInfoWebsiteUrlEntry.getValue();
String sailorsInfoWebsiteURL = sailorsInfoWebsiteURLEntryField.getText().trim();
result.setSailorsInfoWebsiteURL(sailorsInfoWebsiteUrlEntry.getKey(), sailorsInfoWebsiteURL.isEmpty() ? null : sailorsInfoWebsiteURLEntryField.getText().trim());
}
result.setSailorsInfoWebsiteURLs(externalLinksComposite.getSailorsInfoWebsiteURLs());
result.startDate = startDateBox.getValue();
result.endDate = endDateBox.getValue();
result.isPublic = isPublicCheckBox.getValue();
@@ -206,7 +200,7 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
if (additionalWidget != null) {
panel.add(additionalWidget);
}
Grid formGrid = new Grid(10 + GWTLocaleUtil.getLanguageCountWithDefault(), 2);
Grid formGrid = new Grid(8, 2);
int rowIndex = 0;
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.name() + ":"));
formGrid.setWidget(rowIndex++, 1, nameEntryField);
@@ -222,24 +216,18 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
formGrid.setWidget(rowIndex++, 1, endDateBox);
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.isPublic() + ":"));
formGrid.setWidget(rowIndex++, 1, isPublicCheckBox);
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.eventOfficialWebsiteURL() + ":"));
formGrid.setWidget(rowIndex++, 1, officialWebsiteURLEntryField);
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.eventBaseURL() + ":"));
formGrid.setWidget(rowIndex++, 1, baseURLEntryField);
for (Map.Entry<String, TextBox> sailorsInfoWebsiteUrlEntry : sailorsInfoWebsiteURLEntryFields.entrySet()) {
String suffix = " ["+ (sailorsInfoWebsiteUrlEntry.getKey() == null ? stringMessages.defaultLocale() + "*" : sailorsInfoWebsiteUrlEntry.getKey()) + "]";
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.eventSailorsInfoWebsiteURL() + suffix + ":"));
formGrid.setWidget(rowIndex, 1, sailorsInfoWebsiteUrlEntry.getValue());
rowIndex++;
}
formGrid.setText(rowIndex, 1, "[*] " + stringMessages.defaultSailorsInfoLinkInfoText());
TabLayoutPanel tabPanel = new TabLayoutPanel(30, Unit.PX);
tabPanel.ensureDebugId("EventDialogTabs");
tabPanel.setHeight("525px");
tabPanel.setHeight("400px");
panel.add(tabPanel);
final ScrollPanel eventTab = new ScrollPanel(formGrid);
eventTab.ensureDebugId("EventTab");
tabPanel.add(eventTab, stringMessages.event());
final ScrollPanel externalLinksCompositeTab = new ScrollPanel(externalLinksComposite);
externalLinksCompositeTab.ensureDebugId("ExternalLinksCompositeTab");
tabPanel.add(externalLinksCompositeTab, stringMessages.externalLinks());
final ScrollPanel leaderboardGroupTab = new ScrollPanel(leaderboardGroupList);
leaderboardGroupTab.ensureDebugId("LeaderboardGroupsTab");
tabPanel.add(leaderboardGroupTab, stringMessages.leaderboardGroups());
@@ -259,14 +247,4 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
protected FocusWidget getInitialFocusWidget() {
return nameEntryField;
}
protected Map<String, TextBox> createTextBoxesForLocalesAndDefault(Map<String, String> initialValues) {
Map<String, TextBox> result = new LinkedHashMap<>();
for(String localeName : GWTLocaleUtil.getAvailableLocalesAndDefault()) {
TextBox sailorsInfoWebsiteURLEntryField = createTextBox(initialValues.get(localeName));
sailorsInfoWebsiteURLEntryField.setVisibleLength(50);
result.put(localeName, sailorsInfoWebsiteURLEntryField);
}
return result;
}
}
@@ -27,12 +27,9 @@ public class EventEditDialog extends EventDialog {
endDateBox.setFormat("dd/mm/yyyy hh:ii");
isPublicCheckBox = createCheckbox("");
isPublicCheckBox.setValue(event.isPublic);
id = event.id;
officialWebsiteURLEntryField = createTextBox(event.getOfficialWebsiteURL());
officialWebsiteURLEntryField.setVisibleLength(50);
baseURLEntryField = createTextBox(event.getBaseURL());
baseURLEntryField.setVisibleLength(50);
sailorsInfoWebsiteURLEntryFields = createTextBoxesForLocalesAndDefault(event.getSailorsInfoWebsiteURLs());
id = event.id;
courseAreaNameList.setValue(new ArrayList<>(event.venue.getCourseAreas()));
List<String> leaderboardGroupNames = new ArrayList<>();
for(LeaderboardGroupDTO leaderboardGroupDTO: event.getLeaderboardGroups()) {
@@ -41,5 +38,6 @@ public class EventEditDialog extends EventDialog {
leaderboardGroupList.setValue(leaderboardGroupNames);
imagesListComposite.fillImages(event.getImages());
videosListComposite.fillVideos(event.getVideos());
externalLinksComposite.fillExternalLinks(event);
}
}
@@ -0,0 +1,88 @@
package com.sap.sailing.gwt.ui.adminconsole;
import java.util.Collections;
import java.util.HashMap;
import java.util.LinkedHashMap;
import java.util.Map;
import com.google.gwt.dom.client.Style.Unit;
import com.google.gwt.i18n.client.LocaleInfo;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.shared.EventDTO;
import com.sap.sse.gwt.client.GWTLocaleUtil;
public class ExternalLinksComposite extends Composite {
private Map<String, TextBox> sailorsInfoWebsiteURLEntryFields = new HashMap<>();
private TextBox officialWebsiteURLEntryField;
public ExternalLinksComposite(StringMessages stringMessages) {
VerticalPanel panel = new VerticalPanel();
panel.setWidth("100%");
officialWebsiteURLEntryField = new TextBox();
officialWebsiteURLEntryField.setVisibleLength(50);
sailorsInfoWebsiteURLEntryFields = createTextBoxesForLocalesAndDefault(Collections.<String, String> emptyMap());
int rowIndex = 0;
Grid formGrid = new Grid(GWTLocaleUtil.getLanguageCountWithDefault() + 3, 2);
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.eventOfficialWebsiteURL() + ":"));
formGrid.setWidget(rowIndex++, 1, officialWebsiteURLEntryField);
formGrid.setWidget(rowIndex++, 0, new Label(stringMessages.eventSailorsInfoWebsiteURL()));
for (Map.Entry<String, TextBox> sailorsInfoWebsiteUrlEntry : sailorsInfoWebsiteURLEntryFields.entrySet()) {
String locale = sailorsInfoWebsiteUrlEntry.getKey();
String suffix = locale == null ? "*" : "[" + locale + "]";
String text = locale == null ? stringMessages.urlForAllLanguages()
: stringMessages.urlForLanguage(LocaleInfo.getLocaleNativeDisplayName(locale));
Label urlLabel = new Label(text + " " + suffix + ":");
urlLabel.getElement().getStyle().setMarginLeft(10, Unit.PX);
formGrid.setWidget(rowIndex, 0, urlLabel);
formGrid.setWidget(rowIndex, 1, sailorsInfoWebsiteUrlEntry.getValue());
rowIndex++;
}
panel.add(formGrid);
panel.add(new Label("[*] " + stringMessages.defaultSailorsInfoLinkInfoText()));
initWidget(panel);
}
public String getOfficialWebsiteURLValue() {
String value = officialWebsiteURLEntryField.getText().trim();
return value.isEmpty() ? null : value;
}
public Map<String, String> getSailorsInfoWebsiteURLs() {
Map<String, String> sailorsInfoWebsiteURLsMap = new HashMap<>();
for (Map.Entry<String, TextBox> sailorsInfoWebsiteUrlEntry : sailorsInfoWebsiteURLEntryFields.entrySet()) {
TextBox sailorsInfoWebsiteURLEntryField = sailorsInfoWebsiteUrlEntry.getValue();
String sailorsInfoWebsiteURL = sailorsInfoWebsiteURLEntryField.getText().trim();
sailorsInfoWebsiteURLsMap.put(sailorsInfoWebsiteUrlEntry.getKey(),
sailorsInfoWebsiteURL.isEmpty() ? null : sailorsInfoWebsiteURL);
}
return sailorsInfoWebsiteURLsMap;
}
public void fillExternalLinks(EventDTO event) {
Map<String, String> initialValues = event.getSailorsInfoWebsiteURLs();
for (String localeName : GWTLocaleUtil.getAvailableLocalesAndDefault()) {
sailorsInfoWebsiteURLEntryFields.get(localeName).setValue(initialValues.get(localeName));
}
officialWebsiteURLEntryField.setValue(event.getOfficialWebsiteURL());
}
private Map<String, TextBox> createTextBoxesForLocalesAndDefault(Map<String, String> initialValues) {
Map<String, TextBox> result = new LinkedHashMap<>();
for (String localeName : GWTLocaleUtil.getAvailableLocalesAndDefault()) {
TextBox sailorsInfoWebsiteURLEntryField = new TextBox();
sailorsInfoWebsiteURLEntryField.setValue(initialValues.get(localeName));
sailorsInfoWebsiteURLEntryField.setVisibleLength(50);
result.put(localeName, sailorsInfoWebsiteURLEntryField);
}
return result;
}
}
@@ -49,7 +49,7 @@ public class RegattaDetailsComposite extends Composite {
private final ErrorReporter errorReporter;
private final StringMessages stringMessages;
private final RegattaRefresher regattaRefresher;
private final Label regattaId;
private final Label regattaName;
private final Label startDate;
@@ -61,7 +61,8 @@ public class RegattaDetailsComposite extends Composite {
private final Label useStartTimeInference;
private final Label controlTrackingFromStartAndFinishTimes;
private final Label configuration;
private final Label buoyZoneRadiusInHullLengths;
private final SelectionModel<SeriesDTO> seriesSelectionModel;
private final CellTable<SeriesDTO> seriesTable;
private ListDataProvider<SeriesDTO> seriesListDataProvider;
@@ -79,7 +80,7 @@ public class RegattaDetailsComposite extends Composite {
VerticalPanel vPanel = new VerticalPanel();
mainPanel.add(vPanel);
int rows = 11;
int rows = 12;
Grid grid = new Grid(rows, 2);
vPanel.add(grid);
@@ -92,10 +93,11 @@ public class RegattaDetailsComposite extends Composite {
defaultCourseArea = createLabelAndValueWidget(grid, currentRow++, stringMessages.courseArea(), "CourseAreaLabel");
useStartTimeInference = createLabelAndValueWidget(grid, currentRow++, stringMessages.useStartTimeInference(), "UseStartTimeInferenceLabel");
controlTrackingFromStartAndFinishTimes = createLabelAndValueWidget(grid, currentRow++, stringMessages.controlTrackingFromStartAndFinishTimes(), "UseStartTimeInferenceLabel");
buoyZoneRadiusInHullLengths = createLabelAndValueWidget(grid, currentRow++, stringMessages.buoyZoneRadiusInHullLengths(), "BuoyZoneRadiusInHullLengthsLabel");
configuration = createLabelAndValueWidget(grid, currentRow++, stringMessages.racingProcedureConfiguration(), "RacingProcedureLabel");
scoringSystem = createLabelAndValueWidget(grid, currentRow++, stringMessages.scoringSystem(), "ScoringSystemLabel");
rankingMetric = createLabelAndValueWidget(grid, currentRow++, stringMessages.rankingMetric(), "RankingMetricLabel");
seriesTable = createRegattaSeriesTable();
seriesTable.ensureDebugId("SeriesCellTable");
seriesSelectionModel = new SingleSelectionModel<SeriesDTO>();
@@ -398,6 +400,8 @@ public class RegattaDetailsComposite extends Composite {
defaultCourseArea.setText(regatta.defaultCourseAreaUuid == null ? "" : regatta.defaultCourseAreaName);
useStartTimeInference.setText(regatta.useStartTimeInference ? stringMessages.yes() : stringMessages.no());
controlTrackingFromStartAndFinishTimes.setText(regatta.controlTrackingFromStartAndFinishTimes ? stringMessages.yes() : stringMessages.no());
buoyZoneRadiusInHullLengths.setText(String.valueOf(regatta.buoyZoneRadiusInHullLengths));
if (regatta.configuration != null) {
configuration.setText(stringMessages.configured());
} else {
@@ -290,7 +290,7 @@ public class RegattaListComposite extends Composite implements RegattasDisplayer
final RegattaIdentifier regattaName = new RegattaName(editedRegatta.getName());
sailingService.updateRegatta(regattaName, editedRegatta.startDate, editedRegatta.endDate, editedRegatta.defaultCourseAreaUuid,
editedRegatta.configuration, editedRegatta.useStartTimeInference, editedRegatta.controlTrackingFromStartAndFinishTimes,
editedRegatta.configuration, editedRegatta.buoyZoneRadiusInHullLengths, editedRegatta.useStartTimeInference, editedRegatta.controlTrackingFromStartAndFinishTimes,
new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
@Override
public void onFailure(Throwable caught) {
@@ -12,6 +12,7 @@ import com.google.gwt.user.client.ui.FocusWidget;
import com.google.gwt.user.client.ui.Grid;
import com.google.gwt.user.client.ui.TabPanel;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.base.Regatta;
import com.sap.sailing.domain.base.Series;
import com.sap.sailing.domain.common.LeaderboardNameConstants;
import com.sap.sailing.domain.common.dto.FleetDTO;
@@ -72,6 +73,10 @@ public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAnd
errorMessage = stringMessages.regattaWithThisNameAlreadyExists();
}
if (regattaToValidate.buoyZoneRadiusInHullLengths == null) {
errorMessage = stringMessages.incorrectValueForRegattaBuoyZoneRadiusInHullLengths();
}
if (errorMessage == null) {
List<SeriesDTO> seriesToValidate = regattaToValidate.series;
int index = 0;
@@ -109,6 +114,7 @@ public class RegattaWithSeriesAndFleetsCreateDialog extends RegattaWithSeriesAnd
List<EventDTO> existingEvents, EventDTO correspondingEvent, StringMessages stringMessages, DialogCallback<RegattaDTO> callback) {
super(new RegattaDTO(), Collections.<SeriesDTO>emptySet(), existingEvents, correspondingEvent, stringMessages.addRegatta(), stringMessages.ok(),
stringMessages, new RegattaParameterValidator(stringMessages, existingRegattas), callback);
buoyZoneRadiusInHullLengthsDoubleBox.setValue(Regatta.DEFAULT_BUOY_ZONE_RADIUS_IN_HULL_LENGTHS);
SeriesDTO series = new SeriesDTO();
series.setName(Series.DEFAULT_NAME);
series.setMedal(false);
@@ -7,6 +7,7 @@ import com.google.gwt.user.client.ui.Label;
import com.google.gwt.user.client.ui.SuggestBox;
import com.google.gwt.user.client.ui.TextBox;
import com.google.gwt.user.client.ui.VerticalPanel;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.dto.BoatClassDTO;
import com.sap.sailing.gwt.common.client.suggestion.BoatClassMasterdataSuggestOracle;
import com.sap.sailing.gwt.ui.client.StringMessages;
@@ -47,7 +48,7 @@ public abstract class RegattaWithSeriesAndFleetsDialog extends AbstractRegattaWi
protected RegattaDTO getResult() {
RegattaDTO result = getRegattaDTO();
result.setName(nameEntryField.getText().trim()); // trim to particularly avoid trailing blanks
result.boatClass = new BoatClassDTO(boatClassEntryField.getText(), 0.0);
result.boatClass = new BoatClassDTO(boatClassEntryField.getText(), Distance.NULL);
return result;
}
@@ -25,10 +25,27 @@ public class RegattaWithSeriesAndFleetsEditDialog extends RegattaWithSeriesAndFl
private RegattaConfigurationDTO currentRegattaConfiguration;
protected static class RegattaParameterValidator implements Validator<RegattaDTO> {
private StringMessages stringMessages;
public RegattaParameterValidator(StringMessages stringMessages) {
this.stringMessages = stringMessages;
}
@Override
public String getErrorMessage(RegattaDTO regattaToValidate) {
String errorMessage = null;
if (regattaToValidate.buoyZoneRadiusInHullLengths == null) {
errorMessage = stringMessages.incorrectValueForRegattaBuoyZoneRadiusInHullLengths();
}
return errorMessage;
}
}
public RegattaWithSeriesAndFleetsEditDialog(RegattaDTO regatta, Collection<RegattaDTO> existingRegattas,
List<EventDTO> existingEvents, EventDTO correspondingEvent, final StringMessages stringMessages, DialogCallback<RegattaDTO> callback) {
super(regatta, regatta.series, existingEvents, correspondingEvent, stringMessages.editRegatta(), stringMessages.ok(), stringMessages,
null, callback);
new RegattaParameterValidator(stringMessages), callback);
ensureDebugId("RegattaWithSeriesAndFleetsEditDialog");
currentRegattaConfiguration = regatta.configuration;
@@ -31,6 +31,7 @@ import com.google.gwt.view.client.ListDataProvider;
import com.google.gwt.view.client.SelectionChangeEvent;
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
import com.sap.sailing.domain.common.CourseDesignerMode;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.PassingInstruction;
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
import com.sap.sailing.domain.common.RegattaIdentifier;
@@ -49,6 +50,7 @@ import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
import com.sap.sailing.gwt.ui.client.StringMessages;
import com.sap.sailing.gwt.ui.client.shared.controls.FlushableCellTable;
import com.sap.sailing.gwt.ui.client.shared.controls.SelectionCheckboxColumn;
import com.sap.sailing.gwt.ui.client.shared.racemap.RaceMapSettings;
import com.sap.sailing.gwt.ui.shared.ControlPointDTO;
import com.sap.sailing.gwt.ui.shared.DeviceConfigurationDTO;
import com.sap.sailing.gwt.ui.shared.EventDTO;
@@ -336,8 +338,11 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC
List<RaceColumnDTOAndFleetDTOWithNameBasedEquality> races =
new ArrayList<>(raceColumnTable.getDataProvider().getList());
races.remove(raceColumnDTOAndFleetDTO);
Distance buoyZoneRadius = getSelectedRegatta() == null
? RaceMapSettings.DEFAULT_BUOY_ZONE_RADIUS
: getSelectedRegatta().getCalculatedBuoyZoneRadius();
new CopyCourseAndCompetitorsDialog(sailingService, errorReporter, stringMessages, races,
leaderboardName, new DialogCallback<CourseAndCompetitorCopyOperation>() {
leaderboardName, buoyZoneRadius, new DialogCallback<CourseAndCompetitorCopyOperation>() {
@Override
public void ok(CourseAndCompetitorCopyOperation operation) {
operation.perform(leaderboardName, raceColumnDTOAndFleetDTO, /* onSuccessCallback */ new Runnable() {
@@ -619,7 +624,7 @@ public class SmartphoneTrackingEventManagementPanel extends AbstractLeaderboardC
DeviceConfigurationDTO.RegattaConfigurationDTO configuration) {
final RegattaIdentifier regattaIdentifier = new RegattaName(regatta.getName());
sailingService.updateRegatta(regattaIdentifier, regatta.startDate, regatta.endDate,
regatta.defaultCourseAreaUuid, configuration, regatta.useStartTimeInference,
regatta.defaultCourseAreaUuid, configuration, regatta.buoyZoneRadiusInHullLengths, regatta.useStartTimeInference,
regatta.controlTrackingFromStartAndFinishTimes,
new MarkedAsyncCallback<Void>(new AsyncCallback<Void>() {
@Override
@@ -35,6 +35,7 @@ import com.google.gwt.view.client.SelectionChangeEvent.Handler;
import com.sap.sailing.domain.common.RankingMetrics;
import com.sap.sailing.domain.common.ScoringSchemeType;
import com.sap.sailing.domain.common.dto.BoatClassDTO;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.gwt.ui.adminconsole.StructureImportListComposite.RegattaStructureProvider;
import com.sap.sailing.gwt.ui.client.RegattaRefresher;
import com.sap.sailing.gwt.ui.client.SailingServiceAsync;
@@ -341,7 +342,7 @@ public class StructureImportManagementPanel extends SimplePanel implements Regat
*/
private RegattaDTO createRegattaDefaults(RegattaDTO regatta) {
RegattaDTO result = new RegattaDTO("Default", ScoringSchemeType.LOW_POINT);
result.boatClass = new BoatClassDTO(BoatClassDTO.DEFAULT_NAME, /* hull length in meters */ 5);
result.boatClass = new BoatClassDTO(BoatClassDTO.DEFAULT_NAME, /* hull length */ new MeterDistance(5));
result.series = regatta.series;
result.rankingMetricType = RankingMetrics.ONE_DESIGN;
return result;
@@ -215,7 +215,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
RegattaDTO createRegatta(String regattaName, String boatClassName, Date startDate, Date endDate,
RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent,
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, boolean useStartTimeInference,
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, RankingMetrics rankingMetricType);
void removeRegatta(RegattaIdentifier regattaIdentifier);
@@ -225,7 +225,7 @@ public interface SailingService extends RemoteService, FileStorageManagementGwtS
void removeRegattas(Collection<RegattaIdentifier> regattas);
void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, UUID defaultCourseAreaUuid,
RegattaConfigurationDTO regattaConfiguration, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes);
RegattaConfigurationDTO regattaConfiguration, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference, boolean controlTrackingFromStartAndFinishTimes);
List<RaceColumnInSeriesDTO> addRaceColumnsToSeries(RegattaIdentifier regattaIdentifier, String seriesName,
List<Pair<String, Integer>> columnNames);
@@ -441,7 +441,7 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
void createRegatta(String regattaName, String boatClassName, Date startDate, Date endDate,
RegattaCreationParametersDTO seriesNamesWithFleetNamesAndFleetOrderingAndMedal, boolean persistent,
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, boolean useStartTimeInference,
ScoringSchemeType scoringSchemeType, UUID defaultCourseAreaId, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, RankingMetrics rankingMetricType,
AsyncCallback<RegattaDTO> callback);
@@ -528,7 +528,7 @@ public interface SailingServiceAsync extends ServerInfoRetriever, FileStorageMan
void storeSwissTimingArchiveConfiguration(String swissTimingUrl, AsyncCallback<Void> asyncCallback);
void updateRegatta(RegattaIdentifier regattaIdentifier, Date startDate, Date endDate, UUID defaultCourseAreaUuid,
RegattaConfigurationDTO regattaConfiguration, boolean useStartTimeInference,
RegattaConfigurationDTO regattaConfiguration, Double buoyZoneRadiusInHullLengths, boolean useStartTimeInference,
boolean controlTrackingFromStartAndFinishTimes, AsyncCallback<Void> callback);
/**
@@ -1738,4 +1738,9 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
String lastFix();
String filterDeviceMappings();
String raceStartAnalysis();
String urlForLanguage(String language);
String urlForAllLanguages();
String externalLinks();
String buoyZoneRadiusInHullLengths();
String incorrectValueForRegattaBuoyZoneRadiusInHullLengths();
}
@@ -1616,10 +1616,12 @@ raceAnalysis=Race analysis
startAnalysis=Start analysis
winningLanes=Winning lanes
maximumNumberOfDiscards=Maximum number of discards
externalLinks=External links
urlForAllLanguages=URL for all languages
urlForLanguage=URL for language ''{0}''
notificationRegattaConfigurationUpdatedUsingByMarks=Active regatta configuration was set, using the "By Marks" course designer.
warningOverrideRegattaConfigurationCourseDesignerToByMarks=Active regatta configuration already exists, but with the wrong course designer setting.\nWould you like to override course designer setting to "By Marks"?
allRacesHaveBeenDenoted=All races have been denoted for racelog tracking.
chartSelectionHint=Hint: You can also use the leaderboard (on the LEADERBOARD tab) to select the competitors displayed in the chart.
chooseALanguage=Choose a language
solutions=Solutions
@@ -1710,4 +1712,6 @@ strategySimulatorReadMore=Learn More
impressum=Imprint
lastFix=Last fix
filterDeviceMappings=Filter device mappings
raceStartAnalysis=Race Start Analysis
raceStartAnalysis=Race Start Analysis
buoyZoneRadiusInHullLengths=Buoy zone radius in hull lengths (e.g., 2 or 3)
incorrectValueForRegattaBuoyZoneRadiusInHullLengths=Incorrect value for the buoy zone radius in hull lengths
Loaded 100 of 162 files, more files were not shown because too many files have changed in this diff. Show more