mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-22 20:13:52 +00:00
Merge branch 'master' into bug3816_2
This commit is contained in:
commit
d57652edf0
62 files changed
+1046
-239
No files matched your search
+86
@@ -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));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+30
@@ -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%["));
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+6
-1
@@ -47,6 +47,8 @@ Bft10=Storm
|
|||||||
Bft11=Violent storm
|
Bft11=Violent storm
|
||||||
Bft12=Hurricane
|
Bft12=Hurricane
|
||||||
RelativeScore=Relative Score (0 best; 1 worst)
|
RelativeScore=Relative Score (0 best; 1 worst)
|
||||||
|
RelativeScoreInPercent=Relative Score in %
|
||||||
|
RelativeScoreInRaceInPercent=Relative Score in Race in %
|
||||||
AbsoluteRank=Absolute Rank
|
AbsoluteRank=Absolute Rank
|
||||||
WindSpeedInBeaufort=Wind Speed in Bft
|
WindSpeedInBeaufort=Wind Speed in Bft
|
||||||
DistanceAtStart=Distance to Start at Start
|
DistanceAtStart=Distance to Start at Start
|
||||||
@@ -76,4 +78,7 @@ PassingSide=Passing Side
|
|||||||
TackAtStart=Tack at Start
|
TackAtStart=Tack at Start
|
||||||
SpeedTenSecondsAfterStart=Speed 10s After Start
|
SpeedTenSecondsAfterStart=Speed 10s After Start
|
||||||
RankThirtySecondsAfterStart=Rank 30s After Start
|
RankThirtySecondsAfterStart=Rank 30s After Start
|
||||||
StartAdvantage=Start Advantage
|
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
|
||||||
+6
-1
@@ -47,6 +47,8 @@ Bft10=schwerer Sturm
|
|||||||
Bft11=orkanartiger Sturm
|
Bft11=orkanartiger Sturm
|
||||||
Bft12=Orkan
|
Bft12=Orkan
|
||||||
RelativeScore=Relative Wertung (0 am besten; 1 am schlechtesten)
|
RelativeScore=Relative Wertung (0 am besten; 1 am schlechtesten)
|
||||||
|
RelativeScoreInPercent=Relative Wertung in %
|
||||||
|
RelativeScoreInRaceInPercent=Relative Wertung im Rennen in %
|
||||||
AbsoluteRank=Absolute Platzierung
|
AbsoluteRank=Absolute Platzierung
|
||||||
WindSpeedInBeaufort=Windgeschwindigkeit in Bft.
|
WindSpeedInBeaufort=Windgeschwindigkeit in Bft.
|
||||||
DistanceAtStart=Distanz zur Startlinie zum Startzeitpunkt
|
DistanceAtStart=Distanz zur Startlinie zum Startzeitpunkt
|
||||||
@@ -76,4 +78,7 @@ PassingSide=Passierende Seite
|
|||||||
TackAtStart=Startbug
|
TackAtStart=Startbug
|
||||||
SpeedTenSecondsAfterStart=Geschwindigkeit 10s nach dem Startzeitpunkt
|
SpeedTenSecondsAfterStart=Geschwindigkeit 10s nach dem Startzeitpunkt
|
||||||
RankThirtySecondsAfterStart=Absolute Platzierung 30s nach dem Startzeitpunkt
|
RankThirtySecondsAfterStart=Absolute Platzierung 30s nach dem Startzeitpunkt
|
||||||
StartAdvantage=Startvorteil
|
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
|
||||||
@@ -23,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.ParallelDistanceMedianAggregationProcessor;
|
||||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceMinAggregationProcessor;
|
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceMinAggregationProcessor;
|
||||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceSumAggregationProcessor;
|
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.DataSourceProvider;
|
||||||
import com.sap.sse.datamining.components.AggregationProcessorDefinition;
|
import com.sap.sse.datamining.components.AggregationProcessorDefinition;
|
||||||
import com.sap.sse.datamining.components.DataRetrieverChainDefinition;
|
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.impl.AbstractDataMiningActivatorWithPredefinedQueries;
|
||||||
import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO;
|
import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO;
|
||||||
import com.sap.sse.datamining.shared.impl.PredefinedQueryIdentifier;
|
import com.sap.sse.datamining.shared.impl.PredefinedQueryIdentifier;
|
||||||
@@ -126,9 +124,9 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries
|
|||||||
dataSourceProviders = new HashSet<>();
|
dataSourceProviders = new HashSet<>();
|
||||||
dataSourceProviders.add(new RacingEventServiceProvider(context));
|
dataSourceProviders.add(new RacingEventServiceProvider(context));
|
||||||
}
|
}
|
||||||
|
|
||||||
public static ClusterGroup<Speed> getWindStrengthInBeaufortClusterGroup() {
|
public static SailingClusterGroups getClusterGroups() {
|
||||||
return clusterGroups.getWindStrengthInBeaufortCluster();
|
return clusterGroups;
|
||||||
}
|
}
|
||||||
|
|
||||||
public static Activator getDefault() {
|
public static Activator getDefault() {
|
||||||
|
|||||||
+21
-4
@@ -3,6 +3,9 @@ package com.sap.sailing.datamining;
|
|||||||
import java.util.ArrayList;
|
import java.util.ArrayList;
|
||||||
import java.util.Collection;
|
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.Speed;
|
||||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||||
import com.sap.sse.datamining.data.Cluster;
|
import com.sap.sse.datamining.data.Cluster;
|
||||||
@@ -17,7 +20,10 @@ import com.sap.sse.datamining.impl.data.LocalizedCluster;
|
|||||||
|
|
||||||
public class SailingClusterGroups {
|
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() {
|
public SailingClusterGroups() {
|
||||||
Collection<Cluster<Speed>> clusters = new ArrayList<>();
|
Collection<Cluster<Speed>> clusters = new ArrayList<>();
|
||||||
@@ -98,11 +104,22 @@ public class SailingClusterGroups {
|
|||||||
lowerBound = new ComparableClusterBoundary<Speed>(lowerBoundWindSpeed, ComparisonStrategy.GREATER_EQUALS_THAN);
|
lowerBound = new ComparableClusterBoundary<Speed>(lowerBoundWindSpeed, ComparisonStrategy.GREATER_EQUALS_THAN);
|
||||||
clusters.add(new LocalizedCluster<Speed>("Bft12", new ClusterWithSingleBoundary<Speed>(lowerBound)));
|
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() {
|
public ClusterGroup<Speed> getWindStrengthInBeaufortClusterGroup() {
|
||||||
return windStrengthInBeaufortCluster;
|
return windStrengthInBeaufortClusterGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterGroup<Double> getPercentageClusterGroup() {
|
||||||
|
return percentageClusterGroup;
|
||||||
|
}
|
||||||
|
|
||||||
|
public ClusterFormatter<Double> getPercentageClusterFormatter() {
|
||||||
|
return percentageClusterFormatter;
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+12
@@ -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);
|
||||||
|
|
||||||
|
}
|
||||||
+23
-13
@@ -8,6 +8,7 @@ import com.sap.sailing.domain.common.Tack;
|
|||||||
import com.sap.sse.datamining.annotations.Connector;
|
import com.sap.sse.datamining.annotations.Connector;
|
||||||
import com.sap.sse.datamining.annotations.Dimension;
|
import com.sap.sse.datamining.annotations.Dimension;
|
||||||
import com.sap.sse.datamining.annotations.Statistic;
|
import com.sap.sse.datamining.annotations.Statistic;
|
||||||
|
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||||
|
|
||||||
public interface HasRaceOfCompetitorContext {
|
public interface HasRaceOfCompetitorContext {
|
||||||
|
|
||||||
@@ -23,43 +24,52 @@ public interface HasRaceOfCompetitorContext {
|
|||||||
@Dimension(messageKey="TackAtStart", ordinal=12)
|
@Dimension(messageKey="TackAtStart", ordinal=12)
|
||||||
public Tack getTackAtStart() throws NoWindException;
|
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)
|
@Statistic(messageKey="DistanceAtStart", resultDecimals=2, ordinal=0)
|
||||||
public Distance getDistanceToStartLineAtStart();
|
public Distance getDistanceToStartLineAtStart();
|
||||||
|
|
||||||
@Statistic(messageKey="DistanceToStarboardSideAtStart", resultDecimals=2, ordinal=1)
|
@Statistic(messageKey="DistanceToStarboardSideAtStart", resultDecimals=2, ordinal=1)
|
||||||
public Double getNormalizedDistanceToStarboardSideAtStart();
|
public Double getNormalizedDistanceToStarboardSideAtStart();
|
||||||
|
|
||||||
// @Statistic(messageKey="StartAdvantage", resultDecimals=2, ordinal=2)
|
@Statistic(messageKey="WindwardDistanceToAdvantageousEndOfLine", resultDecimals=2, ordinal=2)
|
||||||
// public Double getNormalizedDistanceToFavoredEndAtStart();
|
public Distance getWindwardDistanceToAdvantageousLineEndAtStart();
|
||||||
|
|
||||||
@Connector(messageKey="SpeedWhenStarting", ordinal=2)
|
@Connector(messageKey="SpeedWhenStarting", ordinal=3)
|
||||||
public Speed getSpeedWhenStarting();
|
public Speed getSpeedWhenStarting();
|
||||||
|
|
||||||
@Connector(messageKey="SpeedTenSecondsBeforeStart", ordinal=3)
|
@Connector(messageKey="SpeedTenSecondsBeforeStart", ordinal=4)
|
||||||
public Speed getSpeedTenSecondsBeforeStart();
|
public Speed getSpeedTenSecondsBeforeStart();
|
||||||
|
|
||||||
@Connector(messageKey="SpeedTenSecondsAfterStart", ordinal=4)
|
@Connector(messageKey="SpeedTenSecondsAfterStart", ordinal=5)
|
||||||
public Speed getSpeedTenSecondsAfterStart();
|
public Speed getSpeedTenSecondsAfterStart();
|
||||||
|
|
||||||
@Statistic(messageKey="RankThirtySecondsAfterStart", resultDecimals=2, ordinal=5)
|
@Statistic(messageKey="RankThirtySecondsAfterStart", resultDecimals=2, ordinal=6)
|
||||||
public Double getAbsoluteRankThirtySecondsAfterStart();
|
public Double getRankThirtySecondsAfterStart();
|
||||||
|
|
||||||
@Statistic(messageKey="RankAtFirstMark", resultDecimals=2, ordinal=6)
|
@Statistic(messageKey="RankAfterHalfOfTheFirstLeg", resultDecimals=2, ordinal=7)
|
||||||
|
public Double getRankAfterHalfOfTheFirstLeg();
|
||||||
|
|
||||||
|
@Statistic(messageKey="RankAtFirstMark", resultDecimals=2, ordinal=8)
|
||||||
public Double getRankAtFirstMark();
|
public Double getRankAtFirstMark();
|
||||||
|
|
||||||
@Statistic(messageKey="RankGainsOrLossesBetweenFirstMarkAndFinish", resultDecimals=2, ordinal=7)
|
@Statistic(messageKey="RankGainsOrLossesBetweenFirstMarkAndFinish", resultDecimals=2, ordinal=9)
|
||||||
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish();
|
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish();
|
||||||
|
|
||||||
@Statistic(messageKey="NumberOfManeuvers", resultDecimals=0, ordinal=8)
|
@Statistic(messageKey="NumberOfManeuvers", resultDecimals=0, ordinal=10)
|
||||||
public int getNumberOfManeuvers();
|
public int getNumberOfManeuvers();
|
||||||
|
|
||||||
@Statistic(messageKey="NumberOfTacks", resultDecimals=2, ordinal=9)
|
@Statistic(messageKey="NumberOfTacks", resultDecimals=2, ordinal=11)
|
||||||
public int getNumberOfTacks();
|
public int getNumberOfTacks();
|
||||||
|
|
||||||
@Statistic(messageKey="NumberOfJibes", resultDecimals=2, ordinal=10)
|
@Statistic(messageKey="NumberOfJibes", resultDecimals=2, ordinal=12)
|
||||||
public int getNumberOfJibes();
|
public int getNumberOfJibes();
|
||||||
|
|
||||||
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=11)
|
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=13)
|
||||||
public int getNumberOfPenaltyCircles();
|
public int getNumberOfPenaltyCircles();
|
||||||
|
|
||||||
}
|
}
|
||||||
+7
@@ -6,6 +6,7 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
|||||||
import com.sap.sse.datamining.annotations.Connector;
|
import com.sap.sse.datamining.annotations.Connector;
|
||||||
import com.sap.sse.datamining.annotations.Dimension;
|
import com.sap.sse.datamining.annotations.Dimension;
|
||||||
import com.sap.sse.datamining.annotations.Statistic;
|
import com.sap.sse.datamining.annotations.Statistic;
|
||||||
|
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||||
|
|
||||||
public interface HasTrackedLegOfCompetitorContext {
|
public interface HasTrackedLegOfCompetitorContext {
|
||||||
|
|
||||||
@@ -16,6 +17,9 @@ public interface HasTrackedLegOfCompetitorContext {
|
|||||||
|
|
||||||
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
|
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
|
||||||
public String getCompetitorSearchTag();
|
public String getCompetitorSearchTag();
|
||||||
|
|
||||||
|
@Dimension(messageKey="RelativeScoreInRaceInPercent", ordinal=12)
|
||||||
|
public ClusterDTO getPercentageClusterForRelativeScoreInRace();
|
||||||
|
|
||||||
@Connector(messageKey="Competitor")
|
@Connector(messageKey="Competitor")
|
||||||
public Competitor getCompetitor();
|
public Competitor getCompetitor();
|
||||||
@@ -31,5 +35,8 @@ public interface HasTrackedLegOfCompetitorContext {
|
|||||||
|
|
||||||
@Statistic(messageKey="AbsoluteRank", resultDecimals=2, ordinal=3)
|
@Statistic(messageKey="AbsoluteRank", resultDecimals=2, ordinal=3)
|
||||||
public Double getAbsoluteRank();
|
public Double getAbsoluteRank();
|
||||||
|
|
||||||
|
@Statistic(messageKey="TimeSpentInSeconds", resultDecimals=0, ordinal=4)
|
||||||
|
public Long getTimeTakenInSeconds();
|
||||||
|
|
||||||
}
|
}
|
||||||
+11
-1
@@ -1,10 +1,12 @@
|
|||||||
package com.sap.sailing.datamining.data;
|
package com.sap.sailing.datamining.data;
|
||||||
|
|
||||||
import com.sap.sailing.domain.base.BoatClass;
|
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.CourseArea;
|
||||||
import com.sap.sailing.domain.base.Fleet;
|
import com.sap.sailing.domain.base.Fleet;
|
||||||
import com.sap.sailing.domain.base.RaceDefinition;
|
import com.sap.sailing.domain.base.RaceDefinition;
|
||||||
import com.sap.sailing.domain.base.Regatta;
|
import com.sap.sailing.domain.base.Regatta;
|
||||||
|
import com.sap.sailing.domain.common.NauticalSide;
|
||||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
import com.sap.sse.datamining.annotations.Connector;
|
import com.sap.sse.datamining.annotations.Connector;
|
||||||
import com.sap.sse.datamining.annotations.Dimension;
|
import com.sap.sse.datamining.annotations.Dimension;
|
||||||
@@ -35,7 +37,10 @@ public interface HasTrackedRaceContext {
|
|||||||
@Dimension(messageKey="Year", ordinal=2)
|
@Dimension(messageKey="Year", ordinal=2)
|
||||||
public Integer getYear();
|
public Integer getYear();
|
||||||
|
|
||||||
@Dimension(messageKey="IsTracked", ordinal=6)
|
@Dimension(messageKey="AdvantageousEndOfLine", ordinal=6)
|
||||||
|
public NauticalSide getAdvantageousEndOfLine();
|
||||||
|
|
||||||
|
@Dimension(messageKey="IsTracked", ordinal=7)
|
||||||
public Boolean isTracked();
|
public Boolean isTracked();
|
||||||
|
|
||||||
@Statistic(messageKey="NumberOfCompetitorFixes", resultDecimals=0, ordinal=0)
|
@Statistic(messageKey="NumberOfCompetitorFixes", resultDecimals=0, ordinal=0)
|
||||||
@@ -44,4 +49,9 @@ public interface HasTrackedRaceContext {
|
|||||||
@Statistic(messageKey="NumberOfMarkFixes", resultDecimals=0, ordinal=1)
|
@Statistic(messageKey="NumberOfMarkFixes", resultDecimals=0, ordinal=1)
|
||||||
public int getNumberOfMarkFixes();
|
public int getNumberOfMarkFixes();
|
||||||
|
|
||||||
|
// Convenience methods for race dependent calculation to avoid code duplication
|
||||||
|
public Double getRelativeScoreForCompetitor(Competitor competitor);
|
||||||
|
|
||||||
|
public Double getRankAtFinishForCompetitor(Competitor competitor);
|
||||||
|
|
||||||
}
|
}
|
||||||
+143
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
-1
@@ -36,7 +36,7 @@ public class GPSFixWithContext implements HasGPSFixContext {
|
|||||||
@Override
|
@Override
|
||||||
public ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages) {
|
public ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages) {
|
||||||
Wind wind = getWind();
|
Wind wind = getWind();
|
||||||
Cluster<?> cluster = Activator.getWindStrengthInBeaufortClusterGroup().getClusterFor(wind);
|
Cluster<?> cluster = Activator.getClusterGroups().getWindStrengthInBeaufortClusterGroup().getClusterFor(wind);
|
||||||
return new ClusterDTO(cluster.asLocalizedString(locale, stringMessages));
|
return new ClusterDTO(cluster.asLocalizedString(locale, stringMessages));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+110
@@ -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;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+10
@@ -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) + "%";
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+81
-7
@@ -2,20 +2,32 @@ package com.sap.sailing.datamining.impl.data;
|
|||||||
|
|
||||||
import java.util.concurrent.TimeUnit;
|
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.HasRaceOfCompetitorContext;
|
||||||
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
|
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
|
||||||
import com.sap.sailing.domain.base.Competitor;
|
import com.sap.sailing.domain.base.Competitor;
|
||||||
import com.sap.sailing.domain.base.Course;
|
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.base.Waypoint;
|
||||||
import com.sap.sailing.domain.common.Distance;
|
import com.sap.sailing.domain.common.Distance;
|
||||||
import com.sap.sailing.domain.common.ManeuverType;
|
import com.sap.sailing.domain.common.ManeuverType;
|
||||||
import com.sap.sailing.domain.common.NoWindException;
|
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.Speed;
|
||||||
import com.sap.sailing.domain.common.Tack;
|
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.Maneuver;
|
||||||
|
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
|
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||||
import com.sap.sse.common.TimePoint;
|
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 {
|
public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
||||||
|
|
||||||
@@ -52,6 +64,18 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
|||||||
return startOfRace == null ? null : getTrackedRace().getTack(getCompetitor(), startOfRace);
|
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
|
@Override
|
||||||
public Distance getDistanceToStartLineAtStart() {
|
public Distance getDistanceToStartLineAtStart() {
|
||||||
return getTrackedRace().getDistanceToStartLine(getCompetitor(), 0);
|
return getTrackedRace().getDistanceToStartLine(getCompetitor(), 0);
|
||||||
@@ -71,6 +95,46 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
|||||||
return distance / length;
|
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
|
@Override
|
||||||
public Speed getSpeedWhenStarting() {
|
public Speed getSpeedWhenStarting() {
|
||||||
return getTrackedRace().getSpeedWhenCrossingStartLine(getCompetitor());
|
return getTrackedRace().getSpeedWhenCrossingStartLine(getCompetitor());
|
||||||
@@ -91,7 +155,7 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Double getAbsoluteRankThirtySecondsAfterStart() {
|
public Double getRankThirtySecondsAfterStart() {
|
||||||
TimePoint startOfRace = getTrackedRace().getStartOfRace();
|
TimePoint startOfRace = getTrackedRace().getStartOfRace();
|
||||||
if (startOfRace == null) {
|
if (startOfRace == null) {
|
||||||
return null;
|
return null;
|
||||||
@@ -101,6 +165,21 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
|||||||
return rank == 0 ? null : Double.valueOf(rank);
|
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
|
@Override
|
||||||
public Double getRankAtFirstMark() {
|
public Double getRankAtFirstMark() {
|
||||||
Course course = getTrackedRace().getRace().getCourse();
|
Course course = getTrackedRace().getRace().getCourse();
|
||||||
@@ -113,15 +192,10 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
|||||||
@Override
|
@Override
|
||||||
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish() {
|
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish() {
|
||||||
Double rankAtFirstMark = getRankAtFirstMark();
|
Double rankAtFirstMark = getRankAtFirstMark();
|
||||||
Double rankAtFinish = getRankAtFinish();
|
Double rankAtFinish = getTrackedRaceContext().getRankAtFinishForCompetitor(getCompetitor());
|
||||||
return rankAtFirstMark != null && rankAtFinish != null ? rankAtFirstMark - rankAtFinish : null;
|
return rankAtFirstMark != null && rankAtFinish != null ? rankAtFirstMark - rankAtFinish : null;
|
||||||
}
|
}
|
||||||
|
|
||||||
private Double getRankAtFinish() {
|
|
||||||
int rank = getTrackedRace().getRank(getCompetitor(), getTrackedRace().getEndOfTracking());
|
|
||||||
return rank == 0 ? null : Double.valueOf(rank);
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public int getNumberOfManeuvers() {
|
public int getNumberOfManeuvers() {
|
||||||
return getNumberOfTacks() + getNumberOfJibes();
|
return getNumberOfTacks() + getNumberOfJibes();
|
||||||
|
|||||||
+27
@@ -1,5 +1,7 @@
|
|||||||
package com.sap.sailing.datamining.impl.data;
|
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.HasTrackedLegContext;
|
||||||
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
|
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
|
||||||
import com.sap.sailing.domain.base.Competitor;
|
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.sailing.domain.tracking.TrackedRace;
|
||||||
import com.sap.sse.common.TimePoint;
|
import com.sap.sse.common.TimePoint;
|
||||||
import com.sap.sse.common.Util;
|
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 {
|
public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompetitorContext {
|
||||||
|
|
||||||
@@ -48,6 +52,18 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
|
|||||||
return getCompetitor().getSearchTag();
|
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
|
@Override
|
||||||
public Distance getDistanceTraveled() {
|
public Distance getDistanceTraveled() {
|
||||||
TimePoint timePoint = getTrackedLegContext().getTrackedRaceContext().getTrackedRace().getEndOfTracking();
|
TimePoint timePoint = getTrackedLegContext().getTrackedRaceContext().getTrackedRace().getEndOfTracking();
|
||||||
@@ -94,4 +110,15 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
|
|||||||
return rankAtFinish;
|
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;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+23
@@ -11,9 +11,11 @@ import com.sap.sailing.domain.base.Fleet;
|
|||||||
import com.sap.sailing.domain.base.Mark;
|
import com.sap.sailing.domain.base.Mark;
|
||||||
import com.sap.sailing.domain.base.RaceDefinition;
|
import com.sap.sailing.domain.base.RaceDefinition;
|
||||||
import com.sap.sailing.domain.base.Regatta;
|
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.GPSFix;
|
||||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||||
|
import com.sap.sailing.domain.tracking.LineDetails;
|
||||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
import com.sap.sse.common.TimePoint;
|
import com.sap.sse.common.TimePoint;
|
||||||
import com.sap.sse.common.Util;
|
import com.sap.sse.common.Util;
|
||||||
@@ -90,6 +92,12 @@ public class TrackedRaceWithContext implements HasTrackedRaceContext {
|
|||||||
calendar.setTime(time.asDate());
|
calendar.setTime(time.asDate());
|
||||||
return calendar.get(Calendar.YEAR);
|
return calendar.get(Calendar.YEAR);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public NauticalSide getAdvantageousEndOfLine() {
|
||||||
|
LineDetails startLine = getTrackedRace().getStartLine(getTrackedRace().getStartOfRace());
|
||||||
|
return startLine.getAdvantageousSideWhileApproachingLine();
|
||||||
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Boolean isTracked() {
|
public Boolean isTracked() {
|
||||||
@@ -125,5 +133,20 @@ public class TrackedRaceWithContext implements HasTrackedRaceContext {
|
|||||||
}
|
}
|
||||||
return number;
|
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);
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+49
@@ -3,10 +3,13 @@ package com.sap.sailing.domain.common.dto;
|
|||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
|
||||||
|
import com.sap.sailing.domain.common.Distance;
|
||||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||||
import com.sap.sailing.domain.common.Tack;
|
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.Duration;
|
||||||
|
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Holds a single competitor's scoring details for a single race. It may optionally contain
|
* 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 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
|
* 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
|
* 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() {
|
public boolean hasScoreCorrection() {
|
||||||
return totalPointsCorrected || (reasonForMaxPoints != null && reasonForMaxPoints != MaxPointsReason.NONE);
|
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
|
@Override
|
||||||
public int hashCode() {
|
public int hashCode() {
|
||||||
@@ -174,6 +217,7 @@ public class LeaderboardEntryDTO implements Serializable {
|
|||||||
result = prime * result
|
result = prime * result
|
||||||
+ ((speedOverGroundAtStartOfRaceInKnots == null) ? 0 : speedOverGroundAtStartOfRaceInKnots.hashCode());
|
+ ((speedOverGroundAtStartOfRaceInKnots == null) ? 0 : speedOverGroundAtStartOfRaceInKnots.hashCode());
|
||||||
result = prime * result + ((startTack == null) ? 0 : startTack.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 + ((averageSamplingInterval == null) ? 0 : averageSamplingInterval.hashCode());
|
||||||
result = prime * result + ((timeSinceLastPositionFixInSeconds == null) ? 0 : timeSinceLastPositionFixInSeconds.hashCode());
|
result = prime * result + ((timeSinceLastPositionFixInSeconds == null) ? 0 : timeSinceLastPositionFixInSeconds.hashCode());
|
||||||
result = prime * result + ((netPoints == null) ? 0 : netPoints.hashCode());
|
result = prime * result + ((netPoints == null) ? 0 : netPoints.hashCode());
|
||||||
@@ -282,6 +326,11 @@ public class LeaderboardEntryDTO implements Serializable {
|
|||||||
return false;
|
return false;
|
||||||
if (startTack != other.startTack)
|
if (startTack != other.startTack)
|
||||||
return false;
|
return false;
|
||||||
|
if (averageRideHeightInMeters == null) {
|
||||||
|
if (other.averageRideHeightInMeters != null)
|
||||||
|
return false;
|
||||||
|
} else if (!averageRideHeightInMeters.equals(other.averageRideHeightInMeters))
|
||||||
|
return false;
|
||||||
if (averageSamplingInterval == null) {
|
if (averageSamplingInterval == null) {
|
||||||
if (other.averageSamplingInterval != null)
|
if (other.averageSamplingInterval != null)
|
||||||
return false;
|
return false;
|
||||||
|
|||||||
+25
@@ -4,6 +4,9 @@ import java.io.Serializable;
|
|||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.Map;
|
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}.
|
* 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;
|
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
|
@Override
|
||||||
public String toString() {
|
public String toString() {
|
||||||
return "LeaderboardRowDTO for competitor "+competitor;
|
return "LeaderboardRowDTO for competitor "+competitor;
|
||||||
|
|||||||
+3
-1
@@ -1,5 +1,7 @@
|
|||||||
package com.sap.sailing.domain.common.tracking;
|
package com.sap.sailing.domain.common.tracking;
|
||||||
|
|
||||||
|
import com.sap.sailing.domain.common.Distance;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Specific {@link SensorFix} for bravo devices (http://www.bravosystems.es/).
|
* Specific {@link SensorFix} for bravo devices (http://www.bravosystems.es/).
|
||||||
*
|
*
|
||||||
@@ -12,5 +14,5 @@ public interface BravoFix extends SensorFix {
|
|||||||
*
|
*
|
||||||
* @return the ride height
|
* @return the ride height
|
||||||
*/
|
*/
|
||||||
double getRideHeight();
|
Distance getRideHeight();
|
||||||
}
|
}
|
||||||
+4
-2
@@ -1,5 +1,7 @@
|
|||||||
package com.sap.sailing.domain.common.tracking.impl;
|
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.sensordata.BravoSensorDataMetadata;
|
||||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||||
@@ -32,8 +34,8 @@ public class BravoFixImpl implements BravoFix {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public double getRideHeight() {
|
public Distance getRideHeight() {
|
||||||
return fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightColumn);
|
return new MeterDistance(fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightColumn));
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+8
-12
@@ -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.RaceDefinitionImpl;
|
||||||
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
import com.sap.sailing.domain.base.impl.RegattaImpl;
|
||||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
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.DegreeBearingImpl;
|
||||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
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.sensordata.BravoSensorDataMetadata;
|
||||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
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_TIMESTAMP2 = 120;
|
||||||
private static final long FIX_TIMESTAMP3 = 210;
|
private static final long FIX_TIMESTAMP3 = 210;
|
||||||
private static final long AFTER_LAST_FIX = FIX_TIMESTAMP3 + 1;
|
private static final long AFTER_LAST_FIX = FIX_TIMESTAMP3 + 1;
|
||||||
private static final double FIX_RIDE_HEIGHT = 1337.0;
|
private static final Distance FIX_RIDE_HEIGHT = new MeterDistance(1337.0);
|
||||||
private static final double FIX_RIDE_HEIGHT2 = 1338.0;
|
private static final Distance FIX_RIDE_HEIGHT2 = new MeterDistance(1338.0);
|
||||||
private static final double FIX_RIDE_HEIGHT3 = 1336.0;
|
private static final Distance FIX_RIDE_HEIGHT3 = new MeterDistance(1336.0);
|
||||||
private static final double FIX_TEST_VALUE = 12.0;
|
private static final double FIX_TEST_VALUE = 12.0;
|
||||||
protected final MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory();
|
protected final MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory();
|
||||||
protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a");
|
protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a");
|
||||||
@@ -384,20 +386,14 @@ public class SensorFixStoreAndLoadTest {
|
|||||||
public void testBravoFixIsCorrectlyWrapped() throws InterruptedException {
|
public void testBravoFixIsCorrectlyWrapped() throws InterruptedException {
|
||||||
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
|
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
|
||||||
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
|
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
|
||||||
|
|
||||||
addBravoFixes();
|
addBravoFixes();
|
||||||
|
|
||||||
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
|
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
|
||||||
|
|
||||||
trackedRace.attachRaceLog(raceLog);
|
trackedRace.attachRaceLog(raceLog);
|
||||||
trackedRace.attachRegattaLog(regattaLog);
|
trackedRace.attachRegattaLog(regattaLog);
|
||||||
|
|
||||||
trackedRace.waitForLoadingToFinish();
|
trackedRace.waitForLoadingToFinish();
|
||||||
|
|
||||||
BravoFixTrack<Competitor> bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
|
BravoFixTrack<Competitor> bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
|
||||||
assertEquals(FIX_RIDE_HEIGHT,
|
assertEquals(FIX_RIDE_HEIGHT,
|
||||||
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP)).getRideHeight());
|
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP)).getRideHeight());
|
||||||
|
|
||||||
fixLoaderAndTracker.stop(true);
|
fixLoaderAndTracker.stop(true);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -482,9 +478,9 @@ public class SensorFixStoreAndLoadTest {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private void addBravoFixes() {
|
private void addBravoFixes() {
|
||||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP, FIX_RIDE_HEIGHT));
|
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP, FIX_RIDE_HEIGHT.getMeters()));
|
||||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2));
|
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2.getMeters()));
|
||||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3));
|
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3.getMeters()));
|
||||||
}
|
}
|
||||||
|
|
||||||
private FixLoaderAndTracker createFixLoaderAndTracker() {
|
private FixLoaderAndTracker createFixLoaderAndTracker() {
|
||||||
|
|||||||
+5
-3
@@ -541,15 +541,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public boolean isControlTrackingFromStartAndFinishTimes() {
|
public boolean isControlTrackingFromStartAndFinishTimes() {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setControlTrackingFromStartAndFinishTimes(
|
public void setControlTrackingFromStartAndFinishTimes(
|
||||||
boolean controlTrackingFromStartAndFinishTimes) {
|
boolean controlTrackingFromStartAndFinishTimes) {
|
||||||
// TODO Auto-generated method stub
|
|
||||||
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
@@ -1135,4 +1132,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
|||||||
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+5
@@ -686,4 +686,9 @@ public class MockedTrackedRaceWithStartTimeAndRanks implements TrackedRace {
|
|||||||
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+2
-2
@@ -82,8 +82,8 @@ public class BravoFixTrackSerializationTest {
|
|||||||
assertRideHeight(track, testFixData.timePoint, testFixData.rideHeight);
|
assertRideHeight(track, testFixData.timePoint, testFixData.rideHeight);
|
||||||
}
|
}
|
||||||
|
|
||||||
private void assertRideHeight(BravoFixTrack<Competitor> track, TimePoint timePoint, double expected) {
|
private void assertRideHeight(BravoFixTrack<Competitor> track, TimePoint timePoint, double expectedRideHeightInMeters) {
|
||||||
assertEquals(expected, track.getRideHeight(timePoint), 0.0);
|
assertEquals(expectedRideHeightInMeters, track.getRideHeight(timePoint).getMeters(), 0.0);
|
||||||
}
|
}
|
||||||
|
|
||||||
@SuppressWarnings("unchecked")
|
@SuppressWarnings("unchecked")
|
||||||
|
|||||||
+6
-2
@@ -1437,6 +1437,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
|||||||
} else {
|
} else {
|
||||||
entryDTO.timeSinceLastPositionFixInSeconds = null;
|
entryDTO.timeSinceLastPositionFixInSeconds = null;
|
||||||
}
|
}
|
||||||
|
final Distance averageRideHeight = trackedRace.getAverageRideHeight(competitor, timePoint);
|
||||||
|
entryDTO.averageRideHeightInMeters = averageRideHeight == null ? null : averageRideHeight.getMeters();
|
||||||
}
|
}
|
||||||
if (addLegDetails && trackedRace != null) {
|
if (addLegDetails && trackedRace != null) {
|
||||||
try {
|
try {
|
||||||
@@ -1721,11 +1723,13 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
|||||||
Double speedOverGroundInKnots;
|
Double speedOverGroundInKnots;
|
||||||
if (trackedLeg.hasFinishedLeg(timePoint)) {
|
if (trackedLeg.hasFinishedLeg(timePoint)) {
|
||||||
speedOverGroundInKnots = averageSpeedOverGround == null ? null : averageSpeedOverGround.getKnots();
|
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 {
|
} else {
|
||||||
final SpeedWithBearing speedOverGround = trackedLeg.getSpeedOverGround(timePoint);
|
final SpeedWithBearing speedOverGround = trackedLeg.getSpeedOverGround(timePoint);
|
||||||
speedOverGroundInKnots = speedOverGround == null ? null : speedOverGround.getKnots();
|
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;
|
result.currentSpeedOverGroundInKnots = speedOverGroundInKnots == null ? null : speedOverGroundInKnots;
|
||||||
Distance distanceTraveled = trackedLeg.getDistanceTraveled(timePoint);
|
Distance distanceTraveled = trackedLeg.getDistanceTraveled(timePoint);
|
||||||
|
|||||||
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking;
|
|||||||
|
|
||||||
import java.io.Serializable;
|
import java.io.Serializable;
|
||||||
|
|
||||||
|
import com.sap.sailing.domain.common.Distance;
|
||||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||||
import com.sap.sse.common.TimePoint;
|
import com.sap.sse.common.TimePoint;
|
||||||
import com.sap.sse.common.WithID;
|
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
|
* @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.
|
* where we have fixes for.
|
||||||
*/
|
*/
|
||||||
Double getRideHeight(TimePoint timePoint);
|
Distance getRideHeight(TimePoint timePoint);
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Calculates the average ride height for the given time range.
|
* 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
|
* @return the average ride height for the given time range or <code>null</code> if there are no fixes in the given
|
||||||
* time range.
|
* time range.
|
||||||
*/
|
*/
|
||||||
Double getAverageRideHeight(TimePoint from, TimePoint to);
|
Distance getAverageRideHeight(TimePoint from, TimePoint to);
|
||||||
}
|
}
|
||||||
+2
-2
@@ -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
|
* <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.
|
* 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
|
* @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
|
* <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.
|
* 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
|
* 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.common.tracking.SensorFix;
|
||||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||||
import com.sap.sailing.domain.polars.PolarDataService;
|
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;
|
||||||
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
||||||
import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache;
|
import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache;
|
||||||
@@ -691,6 +692,8 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
|||||||
Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
|
Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
|
||||||
boolean waitForLatestAnalysis) throws NoWindException;
|
boolean waitForLatestAnalysis) throws NoWindException;
|
||||||
|
|
||||||
|
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint);
|
||||||
|
|
||||||
WindStore getWindStore();
|
WindStore getWindStore();
|
||||||
|
|
||||||
Competitor getOverallLeader(TimePoint timePoint);
|
Competitor getOverallLeader(TimePoint timePoint);
|
||||||
|
|||||||
+15
-10
@@ -1,10 +1,8 @@
|
|||||||
package com.sap.sailing.domain.tracking.impl;
|
package com.sap.sailing.domain.tracking.impl;
|
||||||
|
|
||||||
import java.io.Serializable;
|
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.sensordata.BravoSensorDataMetadata;
|
||||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||||
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
||||||
@@ -34,7 +32,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Double getRideHeight(TimePoint timePoint) {
|
public Distance getRideHeight(TimePoint timePoint) {
|
||||||
BravoFix fixAfter = getFirstFixAtOrAfter(timePoint);
|
BravoFix fixAfter = getFirstFixAtOrAfter(timePoint);
|
||||||
if (fixAfter != null && fixAfter.getTimePoint().compareTo(timePoint) == 0) {
|
if (fixAfter != null && fixAfter.getTimePoint().compareTo(timePoint) == 0) {
|
||||||
// exact match of timepoint -> no interpolation necessary
|
// exact match of timepoint -> no interpolation necessary
|
||||||
@@ -54,17 +52,24 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Double getAverageRideHeight(TimePoint from, TimePoint to) {
|
public Distance getAverageRideHeight(TimePoint from, TimePoint to) {
|
||||||
|
final Distance result;
|
||||||
lockForRead();
|
lockForRead();
|
||||||
try {
|
try {
|
||||||
Spliterator<BravoFix> fixes = getFixes(from, true, to, true).spliterator();
|
Distance sum = Distance.NULL;
|
||||||
OptionalDouble average = StreamSupport.stream(fixes, false).mapToDouble(BravoFix::getRideHeight).average();
|
int count = 0;
|
||||||
if (average.isPresent()) {
|
for (final BravoFix fix : getFixes(from, true, to, true)) {
|
||||||
return average.getAsDouble();
|
sum = sum.add(fix.getRideHeight());
|
||||||
|
count++;
|
||||||
|
}
|
||||||
|
if (count > 0) {
|
||||||
|
result = sum.scale(1./count);
|
||||||
|
} else {
|
||||||
|
result = null;
|
||||||
}
|
}
|
||||||
} finally {
|
} finally {
|
||||||
unlockAfterRead();
|
unlockAfterRead();
|
||||||
}
|
}
|
||||||
return null;
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+7
-6
@@ -174,8 +174,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
|||||||
}
|
}
|
||||||
if (timePointToUse != null) {
|
if (timePointToUse != null) {
|
||||||
Distance d = getDistanceTraveled(timePointToUse);
|
Distance d = getDistanceTraveled(timePointToUse);
|
||||||
long millis = timePointToUse.asMillis() - legStart.getTimePoint().asMillis();
|
result = d.inTime(legStart.getTimePoint().until(timePointToUse));
|
||||||
result = d.inTime(millis);
|
|
||||||
} else {
|
} else {
|
||||||
result = null;
|
result = null;
|
||||||
}
|
}
|
||||||
@@ -184,7 +183,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Double getAverageRideHeight(TimePoint timePoint) {
|
public Distance getAverageRideHeight(TimePoint timePoint) {
|
||||||
MarkPassing legStart = getMarkPassingForLegStart();
|
MarkPassing legStart = getMarkPassingForLegStart();
|
||||||
if (legStart != null) {
|
if (legStart != null) {
|
||||||
BravoFixTrack<Competitor> track = getTrackedRace()
|
BravoFixTrack<Competitor> track = getTrackedRace()
|
||||||
@@ -677,15 +676,17 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Double getRideHeight(TimePoint at) {
|
public Distance getRideHeight(TimePoint at) {
|
||||||
|
final Distance result;
|
||||||
if (hasStartedLeg(at)) {
|
if (hasStartedLeg(at)) {
|
||||||
TimePoint timePoint =hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
|
TimePoint timePoint =hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
|
||||||
BravoFixTrack<Competitor> track = getTrackedRace()
|
BravoFixTrack<Competitor> track = getTrackedRace()
|
||||||
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||||
return track == null ? null : track.getRideHeight(timePoint);
|
result = track == null ? null : track.getRideHeight(timePoint);
|
||||||
} else {
|
} else {
|
||||||
return null;
|
result = null;
|
||||||
}
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+16
@@ -1548,6 +1548,22 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
|||||||
return result;
|
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
|
@Override
|
||||||
public TrackedLegOfCompetitor getCurrentLeg(Competitor competitor, TimePoint timePoint) {
|
public TrackedLegOfCompetitor getCurrentLeg(Competitor competitor, TimePoint timePoint) {
|
||||||
// If the mark passing that starts a leg happened exactly at timePoint, the MarkPassingByTimeComparator won't consider
|
// If the mark passing that starts a leg happened exactly at timePoint, the MarkPassingByTimeComparator won't consider
|
||||||
|
|||||||
+5
@@ -865,4 +865,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
|||||||
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
+31
-2
@@ -7,8 +7,16 @@ import com.google.gwt.event.shared.EventBus;
|
|||||||
import com.google.gwt.user.client.Window;
|
import com.google.gwt.user.client.Window;
|
||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
import com.google.gwt.user.client.ui.AcceptsOneWidget;
|
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.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.mvp.ErrorView;
|
||||||
|
import com.sap.sse.gwt.client.shared.perspective.PerspectiveLifecycleWithAllSettings;
|
||||||
import com.sap.sse.gwt.client.useragent.UserAgentDetails;
|
import com.sap.sse.gwt.client.useragent.UserAgentDetails;
|
||||||
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
|
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
|
||||||
|
|
||||||
@@ -26,6 +34,17 @@ public class PlayerActivity extends AbstractActivity {
|
|||||||
this.autoPlayController = null;
|
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
|
@Override
|
||||||
public void start(final AcceptsOneWidget panel, EventBus eventBus) {
|
public void start(final AcceptsOneWidget panel, EventBus eventBus) {
|
||||||
final boolean showRaceDetails = GwtHttpRequestUtils.getBooleanParameter(PARAM_SHOW_RACE_DETAILS, false);
|
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>() {
|
clientFactory.getSailingService().getEventById(eventUUID, true, new AsyncCallback<EventDTO>() {
|
||||||
@Override
|
@Override
|
||||||
public void onSuccess(final EventDTO event) {
|
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());
|
UserAgentDetails userAgent = new UserAgentDetails(Window.Navigator.getUserAgent());
|
||||||
|
|
||||||
PlayerView view = clientFactory.createPlayerView();
|
PlayerView view = clientFactory.createPlayerView();
|
||||||
@@ -44,8 +73,8 @@ public class PlayerActivity extends AbstractActivity {
|
|||||||
autoPlayController = new AutoPlayController(clientFactory.getSailingService(), clientFactory
|
autoPlayController = new AutoPlayController(clientFactory.getSailingService(), clientFactory
|
||||||
.getMediaService(), clientFactory.getUserService(), clientFactory.getErrorReporter(),
|
.getMediaService(), clientFactory.getUserService(), clientFactory.getErrorReporter(),
|
||||||
playerPlace.getConfiguration(), userAgent, delayToLiveMillis, showRaceDetails, view,
|
playerPlace.getConfiguration(), userAgent, delayToLiveMillis, showRaceDetails, view,
|
||||||
playerPlace.getLeaderboardPerspectiveLifecycleWithAllSettings(),
|
leaderboardPerspectiveLifecyclesAndSettings,
|
||||||
playerPlace.getRaceboardPerspectiveLifecycleWithAllSettings());
|
raceboardPerspectiveLifecyclesAndSettings);
|
||||||
autoPlayController.updatePlayMode(AutoPlayModes.Leaderboard);
|
autoPlayController.updatePlayMode(AutoPlayModes.Leaderboard);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+2
@@ -23,6 +23,8 @@ public class PlayerPlace extends AbstractBasePlace {
|
|||||||
Integer timeToSwitchBeforeRaceStartInSeconds = Integer.valueOf(getParameter(AutoPlayerConfiguration.PARAM_TIME_TO_SWITCH_BEFORE_RACE_START));
|
Integer timeToSwitchBeforeRaceStartInSeconds = Integer.valueOf(getParameter(AutoPlayerConfiguration.PARAM_TIME_TO_SWITCH_BEFORE_RACE_START));
|
||||||
|
|
||||||
this.playerConfiguration = new AutoPlayerConfiguration(eventUuidAsString, leaderboardName, fullscreen, timeToSwitchBeforeRaceStartInSeconds);
|
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.leaderboardPerspectiveLifecycleWithAllSettings = null;
|
||||||
this.raceboardPerspectiveLifecycleWithAllSettings = null;
|
this.raceboardPerspectiveLifecycleWithAllSettings = null;
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -55,7 +55,7 @@ public class TabletAndDesktopRegattaEventView extends Composite implements Event
|
|||||||
|
|
||||||
if(currentPresenter.getEventDTO().getType() == EventType.SERIES_EVENT) {
|
if(currentPresenter.getEventDTO().getType() == EventType.SERIES_EVENT) {
|
||||||
final PlaceNavigation<SeriesDefaultPlace> currentEventSeriesNavigation = currentPresenter.getCurrentEventSeriesNavigation();
|
final PlaceNavigation<SeriesDefaultPlace> currentEventSeriesNavigation = currentPresenter.getCurrentEventSeriesNavigation();
|
||||||
Anchor seriesAnchor = new Anchor(currentPresenter.getEventDTO().getSeriesName());
|
Anchor seriesAnchor = new Anchor(i18n.overallLeaderboardSelection());
|
||||||
seriesAnchor.setHref(currentEventSeriesNavigation.getTargetUrl());
|
seriesAnchor.setHref(currentEventSeriesNavigation.getTargetUrl());
|
||||||
seriesAnchor.addClickHandler(new ClickHandler() {
|
seriesAnchor.addClickHandler(new ClickHandler() {
|
||||||
@Override
|
@Override
|
||||||
|
|||||||
+1
-1
@@ -41,7 +41,7 @@ public class DeviceConfigurationQRIdentifierDialog extends DialogBox {
|
|||||||
Window.alert(stringMessages.notCapableOfGeneratingACodeForIdentifier());
|
Window.alert(stringMessages.notCapableOfGeneratingACodeForIdentifier());
|
||||||
} else if (!identifierBox.getValue().isEmpty() && !serverBox.getValue().isEmpty()) {
|
} else if (!identifierBox.getValue().isEmpty() && !serverBox.getValue().isEmpty()) {
|
||||||
String apkUrl = getServerUrlWithoutFinalSlash() + rcAppApkPath;
|
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;
|
return null;
|
||||||
}
|
}
|
||||||
|
|||||||
+2
-5
@@ -37,14 +37,11 @@ public class EventCreateDialog extends EventDialog {
|
|||||||
endDateBox = createDateTimeBox(now);
|
endDateBox = createDateTimeBox(now);
|
||||||
endDateBox.setFormat("dd/mm/yyyy hh:ii");
|
endDateBox.setFormat("dd/mm/yyyy hh:ii");
|
||||||
endDateBox.ensureDebugId("EndDateTimeBox");
|
endDateBox.ensureDebugId("EndDateTimeBox");
|
||||||
officialWebsiteURLEntryField = createTextBox(null);
|
|
||||||
officialWebsiteURLEntryField.setVisibleLength(50);
|
|
||||||
baseURLEntryField = createTextBox(null);
|
|
||||||
baseURLEntryField.setVisibleLength(50);
|
|
||||||
sailorsInfoWebsiteURLEntryFields = createTextBoxesForLocalesAndDefault(Collections.<String, String> emptyMap());
|
|
||||||
isPublicCheckBox = createCheckbox("");
|
isPublicCheckBox = createCheckbox("");
|
||||||
isPublicCheckBox.setValue(false);
|
isPublicCheckBox.setValue(false);
|
||||||
isPublicCheckBox.ensureDebugId("IsPublicCheckBox");
|
isPublicCheckBox.ensureDebugId("IsPublicCheckBox");
|
||||||
|
baseURLEntryField = createTextBox(null);
|
||||||
|
baseURLEntryField.setVisibleLength(50);
|
||||||
imagesListComposite.fillImages(Collections.<ImageDTO>emptyList());
|
imagesListComposite.fillImages(Collections.<ImageDTO>emptyList());
|
||||||
videosListComposite.fillVideos(Collections.<VideoDTO>emptyList());
|
videosListComposite.fillVideos(Collections.<VideoDTO>emptyList());
|
||||||
// add default course area
|
// add default course area
|
||||||
|
|||||||
+9
-31
@@ -5,7 +5,6 @@ import java.util.Collection;
|
|||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
import java.util.Date;
|
import java.util.Date;
|
||||||
import java.util.HashMap;
|
import java.util.HashMap;
|
||||||
import java.util.LinkedHashMap;
|
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Map;
|
import java.util.Map;
|
||||||
import java.util.UUID;
|
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.EventDTO;
|
||||||
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
|
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
|
||||||
import com.sap.sailing.gwt.ui.shared.VenueDTO;
|
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.IconResources;
|
||||||
import com.sap.sse.gwt.client.controls.listedit.GenericStringListInlineEditorComposite;
|
import com.sap.sse.gwt.client.controls.listedit.GenericStringListInlineEditorComposite;
|
||||||
import com.sap.sse.gwt.client.controls.listedit.StringConstantsListEditorComposite;
|
import com.sap.sse.gwt.client.controls.listedit.StringConstantsListEditorComposite;
|
||||||
@@ -48,14 +46,13 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
|||||||
protected BetterDateTimeBox endDateBox;
|
protected BetterDateTimeBox endDateBox;
|
||||||
protected CheckBox isPublicCheckBox;
|
protected CheckBox isPublicCheckBox;
|
||||||
protected UUID id;
|
protected UUID id;
|
||||||
protected TextBox officialWebsiteURLEntryField;
|
|
||||||
protected TextBox baseURLEntryField;
|
protected TextBox baseURLEntryField;
|
||||||
protected Map<String, TextBox> sailorsInfoWebsiteURLEntryFields = new HashMap<>();
|
|
||||||
protected CourseAreaListInlineEditorComposite courseAreaNameList;
|
protected CourseAreaListInlineEditorComposite courseAreaNameList;
|
||||||
protected StringConstantsListEditorComposite leaderboardGroupList;
|
protected StringConstantsListEditorComposite leaderboardGroupList;
|
||||||
protected Map<String, LeaderboardGroupDTO> availableLeaderboardGroupsByName;
|
protected Map<String, LeaderboardGroupDTO> availableLeaderboardGroupsByName;
|
||||||
protected ImagesListComposite imagesListComposite;
|
protected ImagesListComposite imagesListComposite;
|
||||||
protected VideosListComposite videosListComposite;
|
protected VideosListComposite videosListComposite;
|
||||||
|
protected ExternalLinksComposite externalLinksComposite;
|
||||||
|
|
||||||
protected static class EventParameterValidator implements Validator<EventDTO> {
|
protected static class EventParameterValidator implements Validator<EventDTO> {
|
||||||
|
|
||||||
@@ -162,6 +159,7 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
|||||||
|
|
||||||
imagesListComposite = new ImagesListComposite(sailingService, stringMessages);
|
imagesListComposite = new ImagesListComposite(sailingService, stringMessages);
|
||||||
videosListComposite = new VideosListComposite(stringMessages);
|
videosListComposite = new VideosListComposite(stringMessages);
|
||||||
|
externalLinksComposite = new ExternalLinksComposite(stringMessages);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -176,13 +174,9 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
|||||||
}
|
}
|
||||||
result.setName(nameEntryField.getText());
|
result.setName(nameEntryField.getText());
|
||||||
result.setDescription(descriptionEntryField.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());
|
result.setBaseURL(baseURLEntryField.getText().trim().isEmpty() ? null : baseURLEntryField.getText().trim());
|
||||||
for (Map.Entry<String, TextBox> sailorsInfoWebsiteUrlEntry : sailorsInfoWebsiteURLEntryFields.entrySet()) {
|
result.setSailorsInfoWebsiteURLs(externalLinksComposite.getSailorsInfoWebsiteURLs());
|
||||||
TextBox sailorsInfoWebsiteURLEntryField = sailorsInfoWebsiteUrlEntry.getValue();
|
|
||||||
String sailorsInfoWebsiteURL = sailorsInfoWebsiteURLEntryField.getText().trim();
|
|
||||||
result.setSailorsInfoWebsiteURL(sailorsInfoWebsiteUrlEntry.getKey(), sailorsInfoWebsiteURL.isEmpty() ? null : sailorsInfoWebsiteURLEntryField.getText().trim());
|
|
||||||
}
|
|
||||||
result.startDate = startDateBox.getValue();
|
result.startDate = startDateBox.getValue();
|
||||||
result.endDate = endDateBox.getValue();
|
result.endDate = endDateBox.getValue();
|
||||||
result.isPublic = isPublicCheckBox.getValue();
|
result.isPublic = isPublicCheckBox.getValue();
|
||||||
@@ -206,7 +200,7 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
|||||||
if (additionalWidget != null) {
|
if (additionalWidget != null) {
|
||||||
panel.add(additionalWidget);
|
panel.add(additionalWidget);
|
||||||
}
|
}
|
||||||
Grid formGrid = new Grid(10 + GWTLocaleUtil.getLanguageCountWithDefault(), 2);
|
Grid formGrid = new Grid(8, 2);
|
||||||
int rowIndex = 0;
|
int rowIndex = 0;
|
||||||
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.name() + ":"));
|
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.name() + ":"));
|
||||||
formGrid.setWidget(rowIndex++, 1, nameEntryField);
|
formGrid.setWidget(rowIndex++, 1, nameEntryField);
|
||||||
@@ -222,24 +216,18 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
|||||||
formGrid.setWidget(rowIndex++, 1, endDateBox);
|
formGrid.setWidget(rowIndex++, 1, endDateBox);
|
||||||
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.isPublic() + ":"));
|
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.isPublic() + ":"));
|
||||||
formGrid.setWidget(rowIndex++, 1, isPublicCheckBox);
|
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, 0, new Label(stringMessages.eventBaseURL() + ":"));
|
||||||
formGrid.setWidget(rowIndex++, 1, baseURLEntryField);
|
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);
|
TabLayoutPanel tabPanel = new TabLayoutPanel(30, Unit.PX);
|
||||||
tabPanel.ensureDebugId("EventDialogTabs");
|
tabPanel.ensureDebugId("EventDialogTabs");
|
||||||
tabPanel.setHeight("525px");
|
tabPanel.setHeight("400px");
|
||||||
panel.add(tabPanel);
|
panel.add(tabPanel);
|
||||||
final ScrollPanel eventTab = new ScrollPanel(formGrid);
|
final ScrollPanel eventTab = new ScrollPanel(formGrid);
|
||||||
eventTab.ensureDebugId("EventTab");
|
eventTab.ensureDebugId("EventTab");
|
||||||
tabPanel.add(eventTab, stringMessages.event());
|
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);
|
final ScrollPanel leaderboardGroupTab = new ScrollPanel(leaderboardGroupList);
|
||||||
leaderboardGroupTab.ensureDebugId("LeaderboardGroupsTab");
|
leaderboardGroupTab.ensureDebugId("LeaderboardGroupsTab");
|
||||||
tabPanel.add(leaderboardGroupTab, stringMessages.leaderboardGroups());
|
tabPanel.add(leaderboardGroupTab, stringMessages.leaderboardGroups());
|
||||||
@@ -259,14 +247,4 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
|||||||
protected FocusWidget getInitialFocusWidget() {
|
protected FocusWidget getInitialFocusWidget() {
|
||||||
return nameEntryField;
|
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;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
+2
-4
@@ -27,12 +27,9 @@ public class EventEditDialog extends EventDialog {
|
|||||||
endDateBox.setFormat("dd/mm/yyyy hh:ii");
|
endDateBox.setFormat("dd/mm/yyyy hh:ii");
|
||||||
isPublicCheckBox = createCheckbox("");
|
isPublicCheckBox = createCheckbox("");
|
||||||
isPublicCheckBox.setValue(event.isPublic);
|
isPublicCheckBox.setValue(event.isPublic);
|
||||||
id = event.id;
|
|
||||||
officialWebsiteURLEntryField = createTextBox(event.getOfficialWebsiteURL());
|
|
||||||
officialWebsiteURLEntryField.setVisibleLength(50);
|
|
||||||
baseURLEntryField = createTextBox(event.getBaseURL());
|
baseURLEntryField = createTextBox(event.getBaseURL());
|
||||||
baseURLEntryField.setVisibleLength(50);
|
baseURLEntryField.setVisibleLength(50);
|
||||||
sailorsInfoWebsiteURLEntryFields = createTextBoxesForLocalesAndDefault(event.getSailorsInfoWebsiteURLs());
|
id = event.id;
|
||||||
courseAreaNameList.setValue(new ArrayList<>(event.venue.getCourseAreas()));
|
courseAreaNameList.setValue(new ArrayList<>(event.venue.getCourseAreas()));
|
||||||
List<String> leaderboardGroupNames = new ArrayList<>();
|
List<String> leaderboardGroupNames = new ArrayList<>();
|
||||||
for(LeaderboardGroupDTO leaderboardGroupDTO: event.getLeaderboardGroups()) {
|
for(LeaderboardGroupDTO leaderboardGroupDTO: event.getLeaderboardGroups()) {
|
||||||
@@ -41,5 +38,6 @@ public class EventEditDialog extends EventDialog {
|
|||||||
leaderboardGroupList.setValue(leaderboardGroupNames);
|
leaderboardGroupList.setValue(leaderboardGroupNames);
|
||||||
imagesListComposite.fillImages(event.getImages());
|
imagesListComposite.fillImages(event.getImages());
|
||||||
videosListComposite.fillVideos(event.getVideos());
|
videosListComposite.fillVideos(event.getVideos());
|
||||||
|
externalLinksComposite.fillExternalLinks(event);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
+88
@@ -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;
|
||||||
|
}
|
||||||
|
}
|
||||||
+3
@@ -1736,6 +1736,9 @@ public interface StringMessages extends com.sap.sse.gwt.client.StringMessages,
|
|||||||
String lastFix();
|
String lastFix();
|
||||||
String filterDeviceMappings();
|
String filterDeviceMappings();
|
||||||
String raceStartAnalysis();
|
String raceStartAnalysis();
|
||||||
|
String urlForLanguage(String language);
|
||||||
|
String urlForAllLanguages();
|
||||||
|
String externalLinks();
|
||||||
String buoyZoneRadiusInHullLengths();
|
String buoyZoneRadiusInHullLengths();
|
||||||
String incorrectValueForRegattaBuoyZoneRadiusInHullLengths();
|
String incorrectValueForRegattaBuoyZoneRadiusInHullLengths();
|
||||||
}
|
}
|
||||||
+4
-2
@@ -1614,10 +1614,12 @@ raceAnalysis=Race analysis
|
|||||||
startAnalysis=Start analysis
|
startAnalysis=Start analysis
|
||||||
winningLanes=Winning lanes
|
winningLanes=Winning lanes
|
||||||
maximumNumberOfDiscards=Maximum number of discards
|
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.
|
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"?
|
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.
|
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.
|
chartSelectionHint=Hint: You can also use the leaderboard (on the LEADERBOARD tab) to select the competitors displayed in the chart.
|
||||||
chooseALanguage=Choose a language
|
chooseALanguage=Choose a language
|
||||||
solutions=Solutions
|
solutions=Solutions
|
||||||
@@ -1710,4 +1712,4 @@ lastFix=Last fix
|
|||||||
filterDeviceMappings=Filter device mappings
|
filterDeviceMappings=Filter device mappings
|
||||||
raceStartAnalysis=Race Start Analysis
|
raceStartAnalysis=Race Start Analysis
|
||||||
buoyZoneRadiusInHullLengths=Buoy zone radius in hull lengths (e.g., 2 or 3)
|
buoyZoneRadiusInHullLengths=Buoy zone radius in hull lengths (e.g., 2 or 3)
|
||||||
incorrectValueForRegattaBuoyZoneRadiusInHullLengths=Incorrect value for the buoy zone radius in hull lengths
|
incorrectValueForRegattaBuoyZoneRadiusInHullLengths=Incorrect value for the buoy zone radius in hull lengths
|
||||||
+3
-1
@@ -1600,10 +1600,12 @@ raceAnalysis=Renn-Analyse
|
|||||||
startAnalysis=Start-Analyse
|
startAnalysis=Start-Analyse
|
||||||
winningLanes=Sieger-Kurse
|
winningLanes=Sieger-Kurse
|
||||||
maximumNumberOfDiscards=Maximale Anzahl von Streichern
|
maximumNumberOfDiscards=Maximale Anzahl von Streichern
|
||||||
|
externalLinks=Externe Links
|
||||||
|
urlForAllLanguages=URL für alle Sprachen
|
||||||
|
urlForLanguage=URL für die Sprache ''{0}''
|
||||||
notificationRegattaConfigurationUpdatedUsingByMarks=Aktive Regattakonfiguration wurde gesetzt, "By Marks" Kursdesigner ist ausgewählt.
|
notificationRegattaConfigurationUpdatedUsingByMarks=Aktive Regattakonfiguration wurde gesetzt, "By Marks" Kursdesigner ist ausgewählt.
|
||||||
warningOverrideRegattaConfigurationCourseDesignerToByMarks=Aktive Regattakonfiguration existiert bereits, verwendet aber den falschenKursdesigner.\nKursdesigner auf "By Marks" umstellen?
|
warningOverrideRegattaConfigurationCourseDesignerToByMarks=Aktive Regattakonfiguration existiert bereits, verwendet aber den falschenKursdesigner.\nKursdesigner auf "By Marks" umstellen?
|
||||||
allRacesHaveBeenDenoted=Alle Rennen sind für Smartphone Tracking gekennzeichnet worden.
|
allRacesHaveBeenDenoted=Alle Rennen sind für Smartphone Tracking gekennzeichnet worden.
|
||||||
|
|
||||||
chartSelectionHint=Hinweis: Du kannst auch die Ergebnistabelle (im Menü ERGEBNISSE) nutzen, um die im Diagramm angezeigten Teilnehmer auszuwählen.
|
chartSelectionHint=Hinweis: Du kannst auch die Ergebnistabelle (im Menü ERGEBNISSE) nutzen, um die im Diagramm angezeigten Teilnehmer auszuwählen.
|
||||||
chooseALanguage=Wähle eine Sprache
|
chooseALanguage=Wähle eine Sprache
|
||||||
solutions=Lösungen
|
solutions=Lösungen
|
||||||
|
|||||||
+3
@@ -1612,6 +1612,9 @@ raceAnalysis=レース分析
|
|||||||
startAnalysis=スタート分析
|
startAnalysis=スタート分析
|
||||||
winningLanes=勝利レーン
|
winningLanes=勝利レーン
|
||||||
maximumNumberOfDiscards=カットレースの最大数
|
maximumNumberOfDiscards=カットレースの最大数
|
||||||
|
externalLinks=External links
|
||||||
|
urlForAllLanguages=URL for all languages
|
||||||
|
urlForLanguage=URL for language ''{0}''
|
||||||
notificationRegattaConfigurationUpdatedUsingByMarks="マーク別" コースデザイナーを使用して、有効なレガッタ設定が行われました。
|
notificationRegattaConfigurationUpdatedUsingByMarks="マーク別" コースデザイナーを使用して、有効なレガッタ設定が行われました。
|
||||||
warningOverrideRegattaConfigurationCourseDesignerToByMarks=有効なレガッタ設定がすでに登録されていますが、コースデザイナーの設定が適切でありません。\n"マーク別" へのコースデザイナー設定を上書きしますか。
|
warningOverrideRegattaConfigurationCourseDesignerToByMarks=有効なレガッタ設定がすでに登録されていますが、コースデザイナーの設定が適切でありません。\n"マーク別" へのコースデザイナー設定を上書きしますか。
|
||||||
allRacesHaveBeenDenoted=すべてのレースがレースログ追跡に表示されています。
|
allRacesHaveBeenDenoted=すべてのレースがレースログ追跡に表示されています。
|
||||||
|
|||||||
+3
@@ -1612,6 +1612,9 @@ raceAnalysis=Анализ гонки
|
|||||||
startAnalysis=Начать анализ
|
startAnalysis=Начать анализ
|
||||||
winningLanes=Выигрышные маршруты
|
winningLanes=Выигрышные маршруты
|
||||||
maximumNumberOfDiscards=Максимальное число исключений
|
maximumNumberOfDiscards=Максимальное число исключений
|
||||||
|
externalLinks=External links
|
||||||
|
urlForAllLanguages=URL for all languages
|
||||||
|
urlForLanguage=URL for language ''{0}''
|
||||||
notificationRegattaConfigurationUpdatedUsingByMarks=Активная конфигурация регаты была настроена с использованием дизайнера курса "По отметкам".
|
notificationRegattaConfigurationUpdatedUsingByMarks=Активная конфигурация регаты была настроена с использованием дизайнера курса "По отметкам".
|
||||||
warningOverrideRegattaConfigurationCourseDesignerToByMarks=Активная конфигурация регаты уже существует, но с неверно выбранным дизайнером курса.\nПереопределить выбор дизайнера курса на "По отметкам"?
|
warningOverrideRegattaConfigurationCourseDesignerToByMarks=Активная конфигурация регаты уже существует, но с неверно выбранным дизайнером курса.\nПереопределить выбор дизайнера курса на "По отметкам"?
|
||||||
allRacesHaveBeenDenoted=Отслеживание журнала всех гонок отменено.
|
allRacesHaveBeenDenoted=Отслеживание журнала всех гонок отменено.
|
||||||
|
|||||||
+3
-1
@@ -1610,7 +1610,9 @@ raceAnalysis=比赛轮次分析
|
|||||||
startAnalysis=起航分析
|
startAnalysis=起航分析
|
||||||
winningLanes=获胜的船道
|
winningLanes=获胜的船道
|
||||||
maximumNumberOfDiscards=最大放弃次数
|
maximumNumberOfDiscards=最大放弃次数
|
||||||
|
externalLinks=External links
|
||||||
|
urlForAllLanguages=URL for all languages
|
||||||
|
urlForLanguage=URL for language ''{0}''
|
||||||
chartSelectionHint=提示:您还可以使用积分榜(在 LEADERBOARD 选项卡中)选择在记录中显示的参赛队。
|
chartSelectionHint=提示:您还可以使用积分榜(在 LEADERBOARD 选项卡中)选择在记录中显示的参赛队。
|
||||||
chooseALanguage=选择一种语言
|
chooseALanguage=选择一种语言
|
||||||
solutions=解决方案
|
solutions=解决方案
|
||||||
|
|||||||
+3
@@ -1612,6 +1612,9 @@ raceAnalysis=比赛轮次分析
|
|||||||
startAnalysis=起航分析
|
startAnalysis=起航分析
|
||||||
winningLanes=获胜的船道
|
winningLanes=获胜的船道
|
||||||
maximumNumberOfDiscards=最大放弃次数
|
maximumNumberOfDiscards=最大放弃次数
|
||||||
|
externalLinks=External links
|
||||||
|
urlForAllLanguages=URL for all languages
|
||||||
|
urlForLanguage=URL for language ''{0}''
|
||||||
notificationRegattaConfigurationUpdatedUsingByMarks=已使用“按标志”场地设计器设置有效的比赛配置。
|
notificationRegattaConfigurationUpdatedUsingByMarks=已使用“按标志”场地设计器设置有效的比赛配置。
|
||||||
warningOverrideRegattaConfigurationCourseDesignerToByMarks=有效的比赛配置已存在,但是场地设计器设置错误。\n是否将场地设计器设置覆盖为“按标志”?
|
warningOverrideRegattaConfigurationCourseDesignerToByMarks=有效的比赛配置已存在,但是场地设计器设置错误。\n是否将场地设计器设置覆盖为“按标志”?
|
||||||
allRacesHaveBeenDenoted=已为比赛轮次日志跟踪描述所有比赛轮次。
|
allRacesHaveBeenDenoted=已为比赛轮次日志跟踪描述所有比赛轮次。
|
||||||
|
|||||||
+5
-21
@@ -21,9 +21,9 @@ import com.sap.sailing.domain.common.LeaderboardType;
|
|||||||
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
import com.sap.sailing.domain.common.RegattaAndRaceIdentifier;
|
||||||
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
|
import com.sap.sailing.domain.common.RegattaNameAndRaceName;
|
||||||
import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO;
|
import com.sap.sailing.domain.common.dto.AbstractLeaderboardDTO;
|
||||||
|
import com.sap.sailing.gwt.common.authentication.FixedSailingAuthentication;
|
||||||
|
import com.sap.sailing.gwt.common.authentication.SAPSailingHeaderWithAuthentication;
|
||||||
import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint;
|
import com.sap.sailing.gwt.ui.client.AbstractSailingEntryPoint;
|
||||||
import com.sap.sailing.gwt.ui.client.GlobalNavigationPanel;
|
|
||||||
import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel;
|
|
||||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
|
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
|
||||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||||
@@ -45,7 +45,6 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint {
|
|||||||
private String leaderboardName;
|
private String leaderboardName;
|
||||||
private String leaderboardGroupName;
|
private String leaderboardGroupName;
|
||||||
private LeaderboardType leaderboardType;
|
private LeaderboardType leaderboardType;
|
||||||
private GlobalNavigationPanel globalNavigationPanel;
|
|
||||||
private EventDTO event;
|
private EventDTO event;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -121,30 +120,15 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint {
|
|||||||
private void createUI(boolean showRaceDetails, boolean embedded, boolean hideToolbar, EventDTO event) {
|
private void createUI(boolean showRaceDetails, boolean embedded, boolean hideToolbar, EventDTO event) {
|
||||||
DockLayoutPanel mainPanel = new DockLayoutPanel(Unit.PX);
|
DockLayoutPanel mainPanel = new DockLayoutPanel(Unit.PX);
|
||||||
RootLayoutPanel.get().add(mainPanel);
|
RootLayoutPanel.get().add(mainPanel);
|
||||||
LogoAndTitlePanel logoAndTitlePanel = null;
|
|
||||||
if (!embedded) {
|
if (!embedded) {
|
||||||
// Hack to shorten the leaderboardName in case of overall leaderboards
|
// Hack to shorten the leaderboardName in case of overall leaderboards
|
||||||
String leaderboardDisplayName = Window.Location.getParameter("displayName");
|
String leaderboardDisplayName = Window.Location.getParameter("displayName");
|
||||||
if (leaderboardDisplayName == null || leaderboardDisplayName.isEmpty()) {
|
if (leaderboardDisplayName == null || leaderboardDisplayName.isEmpty()) {
|
||||||
leaderboardDisplayName = leaderboardName;
|
leaderboardDisplayName = leaderboardName;
|
||||||
}
|
}
|
||||||
globalNavigationPanel = new GlobalNavigationPanel(getStringMessages(), true, null, leaderboardGroupName, event, null);
|
SAPSailingHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication(leaderboardDisplayName);
|
||||||
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, getStringMessages(), this, getUserService()) {
|
new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView());
|
||||||
@Override
|
mainPanel.addNorth(header, 75);
|
||||||
public void onResize() {
|
|
||||||
super.onResize();
|
|
||||||
if (isSmallWidth()) {
|
|
||||||
remove(globalNavigationPanel);
|
|
||||||
} else {
|
|
||||||
add(globalNavigationPanel);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
};
|
|
||||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
|
||||||
if (!isSmallWidth()) {
|
|
||||||
logoAndTitlePanel.add(globalNavigationPanel);
|
|
||||||
}
|
|
||||||
mainPanel.addNorth(logoAndTitlePanel, 68);
|
|
||||||
}
|
}
|
||||||
ScrollPanel contentScrollPanel = new ScrollPanel();
|
ScrollPanel contentScrollPanel = new ScrollPanel();
|
||||||
long delayBetweenAutoAdvancesInMilliseconds = DEFAULT_REFRESH_INTERVAL_MILLIS;
|
long delayBetweenAutoAdvancesInMilliseconds = DEFAULT_REFRESH_INTERVAL_MILLIS;
|
||||||
|
|||||||
+41
-45
@@ -58,6 +58,7 @@ import com.google.gwt.view.client.MultiSelectionModel;
|
|||||||
import com.google.gwt.view.client.SelectionChangeEvent;
|
import com.google.gwt.view.client.SelectionChangeEvent;
|
||||||
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
|
import com.google.gwt.view.client.SelectionChangeEvent.Handler;
|
||||||
import com.sap.sailing.domain.common.DetailType;
|
import com.sap.sailing.domain.common.DetailType;
|
||||||
|
import com.sap.sailing.domain.common.Distance;
|
||||||
import com.sap.sailing.domain.common.InvertibleComparator;
|
import com.sap.sailing.domain.common.InvertibleComparator;
|
||||||
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
import com.sap.sailing.domain.common.LeaderboardNameConstants;
|
||||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||||
@@ -97,6 +98,7 @@ import com.sap.sailing.gwt.ui.common.client.DateAndTimeFormatterUtil;
|
|||||||
import com.sap.sailing.gwt.ui.leaderboard.DetailTypeColumn.LegDetailField;
|
import com.sap.sailing.gwt.ui.leaderboard.DetailTypeColumn.LegDetailField;
|
||||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
|
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
|
||||||
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
import com.sap.sailing.gwt.ui.shared.RaceTimesInfoDTO;
|
||||||
|
import com.sap.sse.common.Duration;
|
||||||
import com.sap.sse.common.Util;
|
import com.sap.sse.common.Util;
|
||||||
import com.sap.sse.common.Util.Pair;
|
import com.sap.sse.common.Util.Pair;
|
||||||
import com.sap.sse.common.filter.BinaryOperator;
|
import com.sap.sse.common.filter.BinaryOperator;
|
||||||
@@ -972,10 +974,10 @@ public class LeaderboardPanel extends SimplePanel implements Component<Leaderboa
|
|||||||
new FormattedDoubleDetailTypeColumn(DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, new RaceGapToLeaderInSeconds(),
|
new FormattedDoubleDetailTypeColumn(DetailType.RACE_GAP_TO_LEADER_IN_SECONDS, new RaceGapToLeaderInSeconds(),
|
||||||
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
|
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
|
||||||
result.put(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
|
result.put(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS,
|
||||||
new FormattedDoubleDetailTypeColumn(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, new CurrentSpeedOverGroundInKnots(),
|
new FormattedDoubleDetailTypeColumn(DetailType.RACE_CURRENT_SPEED_OVER_GROUND_IN_KNOTS, new RaceCurrentSpeedOverGroundInKnots(),
|
||||||
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
|
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
|
||||||
result.put(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS, new RideHeightColumn(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS,
|
result.put(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS, new RideHeightColumn(DetailType.RACE_CURRENT_RIDE_HEIGHT_IN_METERS,
|
||||||
new CurrentRideHeightInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
|
new RaceCurrentRideHeightInMeters(), LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
|
||||||
result.put(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS,
|
result.put(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS,
|
||||||
new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, new RaceDistanceToCompetitorFarthestAheadInMeters(),
|
new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, new RaceDistanceToCompetitorFarthestAheadInMeters(),
|
||||||
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
|
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
|
||||||
@@ -1098,26 +1100,13 @@ public class LeaderboardPanel extends SimplePanel implements Component<Leaderboa
|
|||||||
private class RaceAverageSpeedInKnots implements LegDetailField<Double> {
|
private class RaceAverageSpeedInKnots implements LegDetailField<Double> {
|
||||||
@Override
|
@Override
|
||||||
public Double get(LeaderboardRowDTO row) {
|
public Double get(LeaderboardRowDTO row) {
|
||||||
Double result = null;
|
final Distance distanceTraveledInMeters = row.getDistanceTraveledInMeters(getRaceColumnName());
|
||||||
LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName());
|
final Duration time = row.getTimeSailed(getRaceColumnName());
|
||||||
if (fieldsForRace != null && fieldsForRace.legDetails != null) {
|
final Double result;
|
||||||
double distanceTraveledInMeters = 0;
|
if (distanceTraveledInMeters != null && time != null) {
|
||||||
long timeInMilliseconds = 0;
|
result = distanceTraveledInMeters.inTime(time).getKnots();
|
||||||
for (LegEntryDTO legDetail : fieldsForRace.legDetails) {
|
} else {
|
||||||
if (legDetail != null) {
|
result = null;
|
||||||
if (legDetail.distanceTraveledInMeters != null && legDetail.timeInMilliseconds != null) {
|
|
||||||
distanceTraveledInMeters += legDetail.distanceTraveledInMeters;
|
|
||||||
timeInMilliseconds += legDetail.timeInMilliseconds;
|
|
||||||
} else {
|
|
||||||
distanceTraveledInMeters = 0;
|
|
||||||
timeInMilliseconds = 0;
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
if (timeInMilliseconds != 0) {
|
|
||||||
result = distanceTraveledInMeters / (double) timeInMilliseconds * 1000 * 3600 / 1852;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
@@ -1299,21 +1288,7 @@ public class LeaderboardPanel extends SimplePanel implements Component<Leaderboa
|
|||||||
private class RaceDistanceTraveledInMeters implements LegDetailField<Double> {
|
private class RaceDistanceTraveledInMeters implements LegDetailField<Double> {
|
||||||
@Override
|
@Override
|
||||||
public Double get(LeaderboardRowDTO row) {
|
public Double get(LeaderboardRowDTO row) {
|
||||||
Double result = null;
|
return row.getDistanceTraveledInMeters(getRaceColumnName()).getMeters();
|
||||||
LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName());
|
|
||||||
if (fieldsForRace != null && fieldsForRace.legDetails != null) {
|
|
||||||
for (LegEntryDTO legDetail : fieldsForRace.legDetails) {
|
|
||||||
if (legDetail != null) {
|
|
||||||
if (legDetail.distanceTraveledInMeters != null) {
|
|
||||||
if (result == null) {
|
|
||||||
result = 0.0;
|
|
||||||
}
|
|
||||||
result += legDetail.distanceTraveledInMeters;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return result;
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1446,27 +1421,48 @@ public class LeaderboardPanel extends SimplePanel implements Component<Leaderboa
|
|||||||
lastLegDetail = fieldsForRace.legDetails.get(--lastLegIndex);
|
lastLegDetail = fieldsForRace.legDetails.get(--lastLegIndex);
|
||||||
}
|
}
|
||||||
if (lastLegDetail != null) {
|
if (lastLegDetail != null) {
|
||||||
result = get(lastLegDetail);
|
if (lastLegDetail.finished) {
|
||||||
|
result = getAfterLastLegFinished(row);
|
||||||
|
} else {
|
||||||
|
result = getBeforeLastLegFinished(lastLegDetail);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected abstract T get(LegEntryDTO lastLegDetail);
|
protected abstract T getBeforeLastLegFinished(LegEntryDTO currentLegDetail);
|
||||||
|
|
||||||
|
protected abstract T getAfterLastLegFinished(LeaderboardRowDTO row);
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CurrentSpeedOverGroundInKnots extends AbstractLastLegDetailField<Double> {
|
private class RaceCurrentSpeedOverGroundInKnots extends AbstractLastLegDetailField<Double> {
|
||||||
@Override
|
@Override
|
||||||
protected Double get(LegEntryDTO lastLegDetail) {
|
protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) {
|
||||||
return lastLegDetail.currentSpeedOverGroundInKnots;
|
return currentLegDetail.currentSpeedOverGroundInKnots;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Double getAfterLastLegFinished(LeaderboardRowDTO row) {
|
||||||
|
return new RaceAverageSpeedInKnots().get(row);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private class CurrentRideHeightInMeters extends AbstractLastLegDetailField<Double> {
|
private class RaceCurrentRideHeightInMeters extends AbstractLastLegDetailField<Double> {
|
||||||
@Override
|
@Override
|
||||||
protected Double get(LegEntryDTO lastLegDetail) {
|
protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) {
|
||||||
return lastLegDetail.currentRideHeightInMeters;
|
return currentLegDetail.currentRideHeightInMeters;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
protected Double getAfterLastLegFinished(LeaderboardRowDTO row) {
|
||||||
|
Double result = null;
|
||||||
|
LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName());
|
||||||
|
if (fieldsForRace != null) {
|
||||||
|
result = fieldsForRace.averageRideHeightInMeters;
|
||||||
|
}
|
||||||
|
return result;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+1
-1
@@ -76,7 +76,7 @@
|
|||||||
font-size: 14px;
|
font-size: 14px;
|
||||||
margin: 0 0 0 0;
|
margin: 0 0 0 0;
|
||||||
padding: 2px 7px 2px 3px;
|
padding: 2px 7px 2px 3px;
|
||||||
height: 18px;
|
height: 22px;
|
||||||
display: inline-block;
|
display: inline-block;
|
||||||
text-decoration: none;
|
text-decoration: none;
|
||||||
-moz-border-radius: 5px 0 0 5px;
|
-moz-border-radius: 5px 0 0 5px;
|
||||||
|
|||||||
+2
-1
@@ -2941,7 +2941,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
|
|||||||
final BravoFixTrack<Competitor> bravoFixTrack = trackedRace
|
final BravoFixTrack<Competitor> bravoFixTrack = trackedRace
|
||||||
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||||
if (bravoFixTrack != null) {
|
if (bravoFixTrack != null) {
|
||||||
result = bravoFixTrack.getRideHeight(timePoint);
|
final Distance rideHeight = bravoFixTrack.getRideHeight(timePoint);
|
||||||
|
result = rideHeight == null ? null : rideHeight.getMeters();
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
|
|||||||
+6
@@ -4,6 +4,12 @@
|
|||||||
<div id="mainContent">
|
<div id="mainContent">
|
||||||
<h4 class="articleHeadline">What's New - SAP Sailing Race Manager</h4>
|
<h4 class="articleHeadline">What's New - SAP Sailing Race Manager</h4>
|
||||||
<div class="innerContent">
|
<div class="innerContent">
|
||||||
|
<h5 class="articleSubheadline">October 2016</h5>
|
||||||
|
|
||||||
|
<ul class="bulletList">
|
||||||
|
<li>Fixed missing German translations for some buttons and labels</li>
|
||||||
|
</ul>
|
||||||
|
|
||||||
<h5 class="articleSubheadline">September 2016</h5>
|
<h5 class="articleSubheadline">September 2016</h5>
|
||||||
|
|
||||||
<ul class="bulletList">
|
<ul class="bulletList">
|
||||||
|
|||||||
+1
@@ -37,4 +37,5 @@
|
|||||||
<inherits name="com.sap.sailing.gwt.ui.SourceAdminConsole" />
|
<inherits name="com.sap.sailing.gwt.ui.SourceAdminConsole" />
|
||||||
|
|
||||||
<inherits name='com.sap.sse.gwt.CommonControlCSS_Autoinject'/>
|
<inherits name='com.sap.sse.gwt.CommonControlCSS_Autoinject'/>
|
||||||
|
<inherits name='com.sap.sailing.gwt.common.AuthenticationCommon'/>
|
||||||
</module>
|
</module>
|
||||||
+9
-2
@@ -35,6 +35,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
|||||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||||
import com.sap.sailing.domain.polars.PolarDataService;
|
import com.sap.sailing.domain.polars.PolarDataService;
|
||||||
import com.sap.sailing.domain.polars.PolarsChangedListener;
|
import com.sap.sailing.domain.polars.PolarsChangedListener;
|
||||||
|
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||||
import com.sap.sailing.polars.PolarDataOperation;
|
import com.sap.sailing.polars.PolarDataOperation;
|
||||||
import com.sap.sailing.polars.mining.BearingClusterGroup;
|
import com.sap.sailing.polars.mining.BearingClusterGroup;
|
||||||
@@ -236,8 +237,14 @@ public class PolarDataServiceImpl implements PolarDataService,
|
|||||||
@Override
|
@Override
|
||||||
public void insertExistingFixes(TrackedRace trackedRace) {
|
public void insertExistingFixes(TrackedRace trackedRace) {
|
||||||
for (Competitor competitor : trackedRace.getRace().getCompetitors()) {
|
for (Competitor competitor : trackedRace.getRace().getCompetitors()) {
|
||||||
for (GPSFixMoving fix : trackedRace.getTrack(competitor).getFixes()) {
|
GPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
|
||||||
competitorPositionChanged(fix, competitor, trackedRace);
|
track.lockForRead();
|
||||||
|
try {
|
||||||
|
for (GPSFixMoving fix : track.getFixes()) {
|
||||||
|
competitorPositionChanged(fix, competitor, trackedRace);
|
||||||
|
}
|
||||||
|
} finally {
|
||||||
|
track.unlockAfterRead();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+10
-9
@@ -9,8 +9,6 @@ import java.util.logging.Logger;
|
|||||||
|
|
||||||
import javax.servlet.http.HttpServletRequest;
|
import javax.servlet.http.HttpServletRequest;
|
||||||
import javax.servlet.http.HttpServletResponse;
|
import javax.servlet.http.HttpServletResponse;
|
||||||
import javax.ws.rs.WebApplicationException;
|
|
||||||
import javax.ws.rs.core.Response;
|
|
||||||
import javax.ws.rs.core.Response.Status;
|
import javax.ws.rs.core.Response.Status;
|
||||||
|
|
||||||
import org.apache.commons.fileupload.FileItem;
|
import org.apache.commons.fileupload.FileItem;
|
||||||
@@ -40,7 +38,7 @@ public class FileUploadServlet extends AbstractFileUploadServlet {
|
|||||||
/**
|
/**
|
||||||
* The maximum size of an image uploaded by a user as a team image, in megabytes (1024*1024 bytes)
|
* The maximum size of an image uploaded by a user as a team image, in megabytes (1024*1024 bytes)
|
||||||
*/
|
*/
|
||||||
private static final int MAX_SIZE_IN_MB = 5;
|
private static final int MAX_SIZE_IN_MB = 500;
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void process(List<FileItem> fileItems, HttpServletRequest req, HttpServletResponse resp) throws UnsupportedEncodingException, IOException {
|
protected void process(List<FileItem> fileItems, HttpServletRequest req, HttpServletResponse resp) throws UnsupportedEncodingException, IOException {
|
||||||
@@ -61,13 +59,16 @@ public class FileUploadServlet extends AbstractFileUploadServlet {
|
|||||||
}
|
}
|
||||||
try {
|
try {
|
||||||
if (fileItem.getSize() > 1024 * 1024 * MAX_SIZE_IN_MB) {
|
if (fileItem.getSize() > 1024 * 1024 * MAX_SIZE_IN_MB) {
|
||||||
throw new WebApplicationException(Response.status(Status.BAD_REQUEST)
|
final String errorMessage = "Image is larger than " + MAX_SIZE_IN_MB + "MB";
|
||||||
.entity("Image is larger than " + MAX_SIZE_IN_MB + "MB").build());
|
logger.warning("Ignoring file storage request because file "+fileItem.getName()+" is larger than "+MAX_SIZE_IN_MB+"MB");
|
||||||
|
result.put("status", Status.INTERNAL_SERVER_ERROR.name());
|
||||||
|
result.put("message", errorMessage);
|
||||||
|
} else {
|
||||||
|
final URI fileUri = getService().getFileStorageManagementService().getActiveFileStorageService()
|
||||||
|
.storeFile(fileItem.getInputStream(), fileExtension, fileItem.getSize());
|
||||||
|
result.put(JSON_FILE_NAME, fileItem.getName());
|
||||||
|
result.put(JSON_FILE_URI, fileUri.toString());
|
||||||
}
|
}
|
||||||
final URI fileUri = getService().getFileStorageManagementService().getActiveFileStorageService()
|
|
||||||
.storeFile(fileItem.getInputStream(), fileExtension, fileItem.getSize());
|
|
||||||
result.put(JSON_FILE_NAME, fileItem.getName());
|
|
||||||
result.put(JSON_FILE_URI, fileUri.toString());
|
|
||||||
} catch (IOException | OperationFailedException | InvalidPropertiesException | NoCorrespondingServiceRegisteredException e) {
|
} catch (IOException | OperationFailedException | InvalidPropertiesException | NoCorrespondingServiceRegisteredException e) {
|
||||||
final String errorMessage = "Could not store file"+ (e.getMessage()==null?"":(": " + e.getMessage()));
|
final String errorMessage = "Could not store file"+ (e.getMessage()==null?"":(": " + e.getMessage()));
|
||||||
logger.log(Level.WARNING, "Could not store file", e);
|
logger.log(Level.WARNING, "Could not store file", e);
|
||||||
|
|||||||
+4
-4
@@ -19,12 +19,12 @@ public class BravoDataImportTest {
|
|||||||
|
|
||||||
private final DoubleVectorFixImporter bravoDataImporter = new BravoDataImporterImpl();
|
private final DoubleVectorFixImporter bravoDataImporter = new BravoDataImporterImpl();
|
||||||
private int callbackCallCount = 0;
|
private int callbackCallCount = 0;
|
||||||
private double sumRideHeight = 0.0;
|
private double sumRideHeightInMeters = 0.0;
|
||||||
|
|
||||||
@Before
|
@Before
|
||||||
public void setUp() {
|
public void setUp() {
|
||||||
this.callbackCallCount = 0;
|
this.callbackCallCount = 0;
|
||||||
this.sumRideHeight = 0.0;
|
this.sumRideHeightInMeters = 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@@ -51,12 +51,12 @@ public class BravoDataImportTest {
|
|||||||
bravoDataImporter.importFixes(importData.getInputStream(), (fixes, device) -> {
|
bravoDataImporter.importFixes(importData.getInputStream(), (fixes, device) -> {
|
||||||
for (DoubleVectorFix fix : fixes) {
|
for (DoubleVectorFix fix : fixes) {
|
||||||
callbackCallCount++;
|
callbackCallCount++;
|
||||||
sumRideHeight += new BravoFixImpl(fix).getRideHeight();
|
sumRideHeightInMeters += new BravoFixImpl(fix).getRideHeight().getMeters();
|
||||||
}
|
}
|
||||||
}, "filename", "source");
|
}, "filename", "source");
|
||||||
Assert.assertEquals(importData.expectedFixesCount, callbackCallCount);
|
Assert.assertEquals(importData.expectedFixesCount, callbackCallCount);
|
||||||
Assert.assertEquals(importData.expectedAverageRideHeight,
|
Assert.assertEquals(importData.expectedAverageRideHeight,
|
||||||
callbackCallCount == 0 ? sumRideHeight : sumRideHeight / callbackCallCount, 0.00001);
|
callbackCallCount == 0 ? sumRideHeightInMeters : sumRideHeightInMeters / callbackCallCount, 0.00001);
|
||||||
}
|
}
|
||||||
|
|
||||||
private enum ImportData {
|
private enum ImportData {
|
||||||
|
|||||||
+2
@@ -3484,6 +3484,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
|||||||
trackedRace.setPolarDataService(service);
|
trackedRace.setPolarDataService(service);
|
||||||
service.insertExistingFixes(trackedRace);
|
service.insertExistingFixes(trackedRace);
|
||||||
}
|
}
|
||||||
|
} catch (Throwable e) {
|
||||||
|
logger.log(Level.SEVERE, "Error reconstructing the polars for tracked races", e);
|
||||||
} finally {
|
} finally {
|
||||||
trackedRegatta.unlockTrackedRacesAfterRead();
|
trackedRegatta.unlockTrackedRacesAfterRead();
|
||||||
}
|
}
|
||||||
|
|||||||
+37
-26
@@ -67,22 +67,26 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
private static final long serialVersionUID = -11522605089325440L;
|
private static final long serialVersionUID = -11522605089325440L;
|
||||||
private Regatta regatta;
|
private Regatta regatta;
|
||||||
|
|
||||||
public DummyTrackedRace(final Serializable raceId, final Iterable<? extends Competitor> competitors, final Regatta regatta, final TrackedRegatta trackedRegatta, final WindStore windStore) {
|
public DummyTrackedRace(final Serializable raceId, final Iterable<? extends Competitor> competitors,
|
||||||
this(competitors, regatta, trackedRegatta, new RaceDefinitionImpl("DummyRace", new CourseImpl("Dummy Course", Collections.<Waypoint>emptyList()),
|
final Regatta regatta, final TrackedRegatta trackedRegatta, final WindStore windStore) {
|
||||||
regatta.getBoatClass(), competitors, raceId), windStore);
|
this(competitors, regatta, trackedRegatta,
|
||||||
|
new RaceDefinitionImpl("DummyRace", new CourseImpl("Dummy Course", Collections.<Waypoint> emptyList()),
|
||||||
|
regatta.getBoatClass(), competitors, raceId),
|
||||||
|
windStore);
|
||||||
}
|
}
|
||||||
|
|
||||||
public DummyTrackedRace(final Iterable<? extends Competitor> competitors, final Regatta regatta, final TrackedRegatta trackedRegatta,
|
public DummyTrackedRace(final Iterable<? extends Competitor> competitors, final Regatta regatta,
|
||||||
RaceDefinition race, WindStore windStore) {
|
final TrackedRegatta trackedRegatta, RaceDefinition race, WindStore windStore) {
|
||||||
super(race, trackedRegatta, windStore, -1);
|
super(race, trackedRegatta, windStore, -1);
|
||||||
this.regatta = regatta;
|
this.regatta = regatta;
|
||||||
}
|
}
|
||||||
public DummyTrackedRace(final Iterable<? extends Competitor> competitors, final Regatta regatta, final TrackedRegatta trackedRegatta,
|
|
||||||
RaceDefinition race) {
|
public DummyTrackedRace(final Iterable<? extends Competitor> competitors, final Regatta regatta,
|
||||||
this(competitors, regatta, trackedRegatta, race, EmptyWindStore.INSTANCE);
|
final TrackedRegatta trackedRegatta, RaceDefinition race) {
|
||||||
|
this(competitors, regatta, trackedRegatta, race, EmptyWindStore.INSTANCE);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public DummyTrackedRace(final String raceName, final Serializable raceId) {
|
public DummyTrackedRace(final String raceName, final Serializable raceId) {
|
||||||
super(new RaceDefinitionImpl(raceName, new CourseImpl("Dummy Course", Collections.<Waypoint> emptyList()),
|
super(new RaceDefinitionImpl(raceName, new CourseImpl("Dummy Course", Collections.<Waypoint> emptyList()),
|
||||||
/* boatClass */ null, new HashSet<Competitor>(), raceId), null, EmptyWindStore.INSTANCE, -1);
|
/* boatClass */ null, new HashSet<Competitor>(), raceId), null, EmptyWindStore.INSTANCE, -1);
|
||||||
@@ -214,7 +218,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
|
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint,
|
||||||
|
MarkPositionAtTimePointCache markPositionCache) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -294,7 +299,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
public Tack getTack(Competitor competitor, TimePoint timePoint) throws NoWindException {
|
public Tack getTack(Competitor competitor, TimePoint timePoint) throws NoWindException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) {
|
public Tack getTack(SpeedWithBearing speedWithBearing, Wind wind, TimePoint timePoint) {
|
||||||
return null;
|
return null;
|
||||||
@@ -346,7 +351,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void removeListener(RaceChangeListener listener) {
|
public void removeListener(RaceChangeListener listener) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -355,7 +360,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) {
|
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint,
|
||||||
|
WindPositionMode windPositionMode) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -382,12 +388,12 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void setWindSourcesToExclude(Iterable<? extends WindSource> windSourcesToExclude) {
|
public void setWindSourcesToExclude(Iterable<? extends WindSource> windSourcesToExclude) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
|
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||||
throws NoWindException {
|
boolean waitForLatestAnalysis) throws NoWindException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -431,8 +437,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
|
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to,
|
||||||
boolean waitForLatestAnalyses) throws NoWindException {
|
boolean upwindOnly, boolean waitForLatestAnalyses) throws NoWindException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -465,7 +471,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void addCourseDesignChangedListener(CourseDesignChangedListener listener) {
|
public void addCourseDesignChangedListener(CourseDesignChangedListener listener) {
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -596,13 +602,13 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
@Override
|
@Override
|
||||||
public void setPolarDataService(PolarDataService polarDataService) {
|
public void setPolarDataService(PolarDataService polarDataService) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Duration getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException,
|
public Duration getEstimatedTimeToComplete(TimePoint timepoint)
|
||||||
NoWindException {
|
throws NotEnoughDataHasBeenAddedException, NoWindException {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint,
|
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint,
|
||||||
WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) {
|
WindPositionMode windPositionMode, RankingInfo rankingInfo, WindLegTypeAndLegBearingCache cache) {
|
||||||
@@ -640,7 +646,7 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
@Override
|
@Override
|
||||||
public void updateStartAndEndOfTracking(boolean waitForGPSFixesToLoad) {
|
public void updateStartAndEndOfTracking(boolean waitForGPSFixesToLoad) {
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public <FixT extends SensorFix, TrackT extends SensorFixTrack<Competitor, FixT>> TrackT getSensorTrack(
|
public <FixT extends SensorFix, TrackT extends SensorFixTrack<Competitor, FixT>> TrackT getSensorTrack(
|
||||||
Competitor competitor, String trackName) {
|
Competitor competitor, String trackName) {
|
||||||
@@ -661,4 +667,9 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
|||||||
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
||||||
return null;
|
return null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
@@ -29,6 +29,11 @@
|
|||||||
can be used to determine the radius of the zone around the marks in terms of
|
can be used to determine the radius of the zone around the marks in terms of
|
||||||
hull lengths. This sets the default for the race viewer (RaceBoard.html entry point)
|
hull lengths. This sets the default for the race viewer (RaceBoard.html entry point)
|
||||||
based on the hull length as known from the boat class.</li>
|
based on the hull length as known from the boat class.</li>
|
||||||
|
<li>When producing the QR code for the Race Management App's device configurations,
|
||||||
|
the access token parameter was not properly URL-encoded. In case the access token
|
||||||
|
contains a '+' character it is incorrectly decoded by the app, letting the authentication
|
||||||
|
fail. This issue has now been fixed in the AdminConsole, and the access token parameter is
|
||||||
|
now properly URL-encoded.</li>
|
||||||
</ul>
|
</ul>
|
||||||
|
|
||||||
<h2 class="articleSubheadline">September 2016</h2>
|
<h2 class="articleSubheadline">September 2016</h2>
|
||||||
|
|||||||
@@ -47,7 +47,7 @@ public class NaturalComparator implements Comparator<String> {
|
|||||||
|
|
||||||
// Compare all-digit blocks as numbers - all-char blocks as strings
|
// Compare all-digit blocks as numbers - all-char blocks as strings
|
||||||
if (Character.isDigit(aBlock.charAt(0)) && Character.isDigit(bBlock.charAt(0))) {
|
if (Character.isDigit(aBlock.charAt(0)) && Character.isDigit(bBlock.charAt(0))) {
|
||||||
result = Integer.valueOf(aBlock).compareTo(Integer.valueOf(bBlock));
|
result = Long.valueOf(aBlock).compareTo(Long.valueOf(bBlock));
|
||||||
} else {
|
} else {
|
||||||
result = aBlock.compareTo(bBlock);
|
result = aBlock.compareTo(bBlock);
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -460,20 +460,20 @@
|
|||||||
<string name="not_available">N/A</string>
|
<string name="not_available">N/A</string>
|
||||||
<string name="accuracy_timestamp">(vor %s)</string>
|
<string name="accuracy_timestamp">(vor %s)</string>
|
||||||
<string name="prev_course">Vorheriger Kurs</string>
|
<string name="prev_course">Vorheriger Kurs</string>
|
||||||
<string name="layout_course">Kurs Layout</string>
|
<string name="layout_course">Kurs-Layout</string>
|
||||||
<string name="wind_degree">°</string>
|
<string name="wind_degree">°</string>
|
||||||
<string name="add_waypoint">Wegpunkt hinzufügen</string>
|
<string name="add_waypoint">Wegpunkt hinzufügen</string>
|
||||||
<string name="unpublished_course">Unpublished course design</string>
|
<string name="unpublished_course">Unveröffentlichtes Kurs-Layout</string>
|
||||||
<string name="reset_state_1">Neu</string>
|
<string name="reset_state_1">Neu</string>
|
||||||
<string name="reset_state_2">Zurücksetzen</string>
|
<string name="reset_state_2">Zurücksetzen</string>
|
||||||
<string name="reset_message_1">Soll die Liste komplett gelöscht werden?</string>
|
<string name="reset_message_1">Soll die Liste komplett gelöscht werden?</string>
|
||||||
<string name="reset_message_2">Soll die Liste zum letzten bekannten Stand zurückgesetzt werden?</string>
|
<string name="reset_message_2">Soll die Liste zum letzten bekannten Stand zurückgesetzt werden?</string>
|
||||||
<string name="onboarding_scan">Scan QR-Code</string>
|
<string name="onboarding_scan">QR-Code scannen</string>
|
||||||
<string name="onboarding_input">Manuelle Eingabe</string>
|
<string name="onboarding_input">Manuelle Eingabe</string>
|
||||||
<string name="race_group">Group Race</string>
|
<string name="race_group">Gruppenwettfahrt</string>
|
||||||
<string name="wind_select_race">Select Race</string>
|
<string name="wind_select_race">Wettfahrt auswählen</string>
|
||||||
<string name="added_to_result_list">\'%1$s\' zur Ergebnisliste hinzugefügt an Position %2$s</string>
|
<string name="added_to_result_list">\'%1$s\' zur Ergebnisliste hinzugefügt an Position %2$s</string>
|
||||||
<string name="get_started">Getting Started</string>
|
<string name="get_started">Anmelden</string>
|
||||||
<string name="position">Position</string>
|
<string name="position">Position</string>
|
||||||
<string name="server_deeplink_message">Server erfolgreich geändert auf %1$s.</string>
|
<string name="server_deeplink_message">Server erfolgreich geändert auf %1$s.</string>
|
||||||
<string name="penalty">Strafe</string>
|
<string name="penalty">Strafe</string>
|
||||||
|
|||||||
Reference in new issue
Block a user