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
+1028
-221
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
|
||||
Bft12=Hurricane
|
||||
RelativeScore=Relative Score (0 best; 1 worst)
|
||||
RelativeScoreInPercent=Relative Score in %
|
||||
RelativeScoreInRaceInPercent=Relative Score in Race in %
|
||||
AbsoluteRank=Absolute Rank
|
||||
WindSpeedInBeaufort=Wind Speed in Bft
|
||||
DistanceAtStart=Distance to Start at Start
|
||||
@@ -76,4 +78,7 @@ PassingSide=Passing Side
|
||||
TackAtStart=Tack at Start
|
||||
SpeedTenSecondsAfterStart=Speed 10s 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
|
||||
Bft12=Orkan
|
||||
RelativeScore=Relative Wertung (0 am besten; 1 am schlechtesten)
|
||||
RelativeScoreInPercent=Relative Wertung in %
|
||||
RelativeScoreInRaceInPercent=Relative Wertung im Rennen in %
|
||||
AbsoluteRank=Absolute Platzierung
|
||||
WindSpeedInBeaufort=Windgeschwindigkeit in Bft.
|
||||
DistanceAtStart=Distanz zur Startlinie zum Startzeitpunkt
|
||||
@@ -76,4 +78,7 @@ PassingSide=Passierende Seite
|
||||
TackAtStart=Startbug
|
||||
SpeedTenSecondsAfterStart=Geschwindigkeit 10s 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.ParallelDistanceMinAggregationProcessor;
|
||||
import com.sap.sailing.datamining.impl.components.aggregators.ParallelDistanceSumAggregationProcessor;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sse.datamining.DataSourceProvider;
|
||||
import com.sap.sse.datamining.components.AggregationProcessorDefinition;
|
||||
import com.sap.sse.datamining.components.DataRetrieverChainDefinition;
|
||||
import com.sap.sse.datamining.data.ClusterGroup;
|
||||
import com.sap.sse.datamining.impl.AbstractDataMiningActivatorWithPredefinedQueries;
|
||||
import com.sap.sse.datamining.shared.dto.StatisticQueryDefinitionDTO;
|
||||
import com.sap.sse.datamining.shared.impl.PredefinedQueryIdentifier;
|
||||
@@ -127,8 +125,8 @@ public class Activator extends AbstractDataMiningActivatorWithPredefinedQueries
|
||||
dataSourceProviders.add(new RacingEventServiceProvider(context));
|
||||
}
|
||||
|
||||
public static ClusterGroup<Speed> getWindStrengthInBeaufortClusterGroup() {
|
||||
return clusterGroups.getWindStrengthInBeaufortCluster();
|
||||
public static SailingClusterGroups getClusterGroups() {
|
||||
return clusterGroups;
|
||||
}
|
||||
|
||||
public static Activator getDefault() {
|
||||
|
||||
+21
-4
@@ -3,6 +3,9 @@ package com.sap.sailing.datamining;
|
||||
import java.util.ArrayList;
|
||||
import java.util.Collection;
|
||||
|
||||
import com.sap.sailing.datamining.data.ClusterFormatter;
|
||||
import com.sap.sailing.datamining.impl.data.LinearDoubleClusterGroup;
|
||||
import com.sap.sailing.datamining.impl.data.PercentageClusterFormatter;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedImpl;
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
@@ -17,7 +20,10 @@ import com.sap.sse.datamining.impl.data.LocalizedCluster;
|
||||
|
||||
public class SailingClusterGroups {
|
||||
|
||||
private final ClusterGroup<Speed> windStrengthInBeaufortCluster;
|
||||
private final ClusterGroup<Speed> windStrengthInBeaufortClusterGroup;
|
||||
|
||||
private final ClusterGroup<Double> percentageClusterGroup;
|
||||
private final ClusterFormatter<Double> percentageClusterFormatter;
|
||||
|
||||
public SailingClusterGroups() {
|
||||
Collection<Cluster<Speed>> clusters = new ArrayList<>();
|
||||
@@ -98,11 +104,22 @@ public class SailingClusterGroups {
|
||||
lowerBound = new ComparableClusterBoundary<Speed>(lowerBoundWindSpeed, ComparisonStrategy.GREATER_EQUALS_THAN);
|
||||
clusters.add(new LocalizedCluster<Speed>("Bft12", new ClusterWithSingleBoundary<Speed>(lowerBound)));
|
||||
|
||||
windStrengthInBeaufortCluster = new FixClusterGroup<Speed>(clusters);
|
||||
windStrengthInBeaufortClusterGroup = new FixClusterGroup<Speed>(clusters);
|
||||
|
||||
percentageClusterGroup = new LinearDoubleClusterGroup(0.0, 1.0, 0.1, true);
|
||||
percentageClusterFormatter = new PercentageClusterFormatter();
|
||||
}
|
||||
|
||||
public ClusterGroup<Speed> getWindStrengthInBeaufortCluster() {
|
||||
return windStrengthInBeaufortCluster;
|
||||
public ClusterGroup<Speed> getWindStrengthInBeaufortClusterGroup() {
|
||||
return windStrengthInBeaufortClusterGroup;
|
||||
}
|
||||
|
||||
public ClusterGroup<Double> getPercentageClusterGroup() {
|
||||
return percentageClusterGroup;
|
||||
}
|
||||
|
||||
public ClusterFormatter<Double> getPercentageClusterFormatter() {
|
||||
return percentageClusterFormatter;
|
||||
}
|
||||
|
||||
}
|
||||
+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.Dimension;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||
|
||||
public interface HasRaceOfCompetitorContext {
|
||||
|
||||
@@ -23,43 +24,52 @@ public interface HasRaceOfCompetitorContext {
|
||||
@Dimension(messageKey="TackAtStart", ordinal=12)
|
||||
public Tack getTackAtStart() throws NoWindException;
|
||||
|
||||
@Dimension(messageKey="DistanceToStarboardSideAtStart", ordinal=13)
|
||||
public ClusterDTO getPercentageClusterForDistanceToStarboardSideAtStart();
|
||||
|
||||
@Dimension(messageKey="RelativeScoreInPercent", ordinal=14)
|
||||
public ClusterDTO getPercentageClusterForRelativeScore();
|
||||
|
||||
@Statistic(messageKey="DistanceAtStart", resultDecimals=2, ordinal=0)
|
||||
public Distance getDistanceToStartLineAtStart();
|
||||
|
||||
@Statistic(messageKey="DistanceToStarboardSideAtStart", resultDecimals=2, ordinal=1)
|
||||
public Double getNormalizedDistanceToStarboardSideAtStart();
|
||||
|
||||
// @Statistic(messageKey="StartAdvantage", resultDecimals=2, ordinal=2)
|
||||
// public Double getNormalizedDistanceToFavoredEndAtStart();
|
||||
@Statistic(messageKey="WindwardDistanceToAdvantageousEndOfLine", resultDecimals=2, ordinal=2)
|
||||
public Distance getWindwardDistanceToAdvantageousLineEndAtStart();
|
||||
|
||||
@Connector(messageKey="SpeedWhenStarting", ordinal=2)
|
||||
@Connector(messageKey="SpeedWhenStarting", ordinal=3)
|
||||
public Speed getSpeedWhenStarting();
|
||||
|
||||
@Connector(messageKey="SpeedTenSecondsBeforeStart", ordinal=3)
|
||||
@Connector(messageKey="SpeedTenSecondsBeforeStart", ordinal=4)
|
||||
public Speed getSpeedTenSecondsBeforeStart();
|
||||
|
||||
@Connector(messageKey="SpeedTenSecondsAfterStart", ordinal=4)
|
||||
@Connector(messageKey="SpeedTenSecondsAfterStart", ordinal=5)
|
||||
public Speed getSpeedTenSecondsAfterStart();
|
||||
|
||||
@Statistic(messageKey="RankThirtySecondsAfterStart", resultDecimals=2, ordinal=5)
|
||||
public Double getAbsoluteRankThirtySecondsAfterStart();
|
||||
@Statistic(messageKey="RankThirtySecondsAfterStart", resultDecimals=2, ordinal=6)
|
||||
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();
|
||||
|
||||
@Statistic(messageKey="RankGainsOrLossesBetweenFirstMarkAndFinish", resultDecimals=2, ordinal=7)
|
||||
@Statistic(messageKey="RankGainsOrLossesBetweenFirstMarkAndFinish", resultDecimals=2, ordinal=9)
|
||||
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish();
|
||||
|
||||
@Statistic(messageKey="NumberOfManeuvers", resultDecimals=0, ordinal=8)
|
||||
@Statistic(messageKey="NumberOfManeuvers", resultDecimals=0, ordinal=10)
|
||||
public int getNumberOfManeuvers();
|
||||
|
||||
@Statistic(messageKey="NumberOfTacks", resultDecimals=2, ordinal=9)
|
||||
@Statistic(messageKey="NumberOfTacks", resultDecimals=2, ordinal=11)
|
||||
public int getNumberOfTacks();
|
||||
|
||||
@Statistic(messageKey="NumberOfJibes", resultDecimals=2, ordinal=10)
|
||||
@Statistic(messageKey="NumberOfJibes", resultDecimals=2, ordinal=12)
|
||||
public int getNumberOfJibes();
|
||||
|
||||
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=11)
|
||||
@Statistic(messageKey="NumberOfPenaltyCircles", resultDecimals=2, ordinal=13)
|
||||
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.Dimension;
|
||||
import com.sap.sse.datamining.annotations.Statistic;
|
||||
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||
|
||||
public interface HasTrackedLegOfCompetitorContext {
|
||||
|
||||
@@ -17,6 +18,9 @@ public interface HasTrackedLegOfCompetitorContext {
|
||||
@Dimension(messageKey="CompetitorSearchTag", ordinal=11) // TODO Clean me: Move Dimension to Competitor when possible
|
||||
public String getCompetitorSearchTag();
|
||||
|
||||
@Dimension(messageKey="RelativeScoreInRaceInPercent", ordinal=12)
|
||||
public ClusterDTO getPercentageClusterForRelativeScoreInRace();
|
||||
|
||||
@Connector(messageKey="Competitor")
|
||||
public Competitor getCompetitor();
|
||||
|
||||
@@ -32,4 +36,7 @@ public interface HasTrackedLegOfCompetitorContext {
|
||||
@Statistic(messageKey="AbsoluteRank", resultDecimals=2, ordinal=3)
|
||||
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;
|
||||
|
||||
import com.sap.sailing.domain.base.BoatClass;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.CourseArea;
|
||||
import com.sap.sailing.domain.base.Fleet;
|
||||
import com.sap.sailing.domain.base.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.datamining.annotations.Connector;
|
||||
import com.sap.sse.datamining.annotations.Dimension;
|
||||
@@ -35,7 +37,10 @@ public interface HasTrackedRaceContext {
|
||||
@Dimension(messageKey="Year", ordinal=2)
|
||||
public Integer getYear();
|
||||
|
||||
@Dimension(messageKey="IsTracked", ordinal=6)
|
||||
@Dimension(messageKey="AdvantageousEndOfLine", ordinal=6)
|
||||
public NauticalSide getAdvantageousEndOfLine();
|
||||
|
||||
@Dimension(messageKey="IsTracked", ordinal=7)
|
||||
public Boolean isTracked();
|
||||
|
||||
@Statistic(messageKey="NumberOfCompetitorFixes", resultDecimals=0, ordinal=0)
|
||||
@@ -44,4 +49,9 @@ public interface HasTrackedRaceContext {
|
||||
@Statistic(messageKey="NumberOfMarkFixes", resultDecimals=0, ordinal=1)
|
||||
public int getNumberOfMarkFixes();
|
||||
|
||||
// Convenience methods for race dependent calculation to avoid code duplication
|
||||
public Double getRelativeScoreForCompetitor(Competitor competitor);
|
||||
|
||||
public Double getRankAtFinishForCompetitor(Competitor competitor);
|
||||
|
||||
}
|
||||
+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
|
||||
public ClusterDTO getWindStrengthAsBeaufortCluster(Locale locale, ResourceBundleStringMessages stringMessages) {
|
||||
Wind wind = getWind();
|
||||
Cluster<?> cluster = Activator.getWindStrengthInBeaufortClusterGroup().getClusterFor(wind);
|
||||
Cluster<?> cluster = Activator.getClusterGroups().getWindStrengthInBeaufortClusterGroup().getClusterFor(wind);
|
||||
return new ClusterDTO(cluster.asLocalizedString(locale, stringMessages));
|
||||
}
|
||||
|
||||
|
||||
+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 com.sap.sailing.datamining.Activator;
|
||||
import com.sap.sailing.datamining.SailingClusterGroups;
|
||||
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
|
||||
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
import com.sap.sailing.domain.base.Course;
|
||||
import com.sap.sailing.domain.base.Mark;
|
||||
import com.sap.sailing.domain.base.Waypoint;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.ManeuverType;
|
||||
import com.sap.sailing.domain.common.NoWindException;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.Speed;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.Maneuver;
|
||||
import com.sap.sailing.domain.tracking.TrackedLeg;
|
||||
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindPositionMode;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||
|
||||
public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
||||
|
||||
@@ -52,6 +64,18 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
||||
return startOfRace == null ? null : getTrackedRace().getTack(getCompetitor(), startOfRace);
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterDTO getPercentageClusterForDistanceToStarboardSideAtStart() {
|
||||
Double normalizedDistance = getNormalizedDistanceToStarboardSideAtStart();
|
||||
if (normalizedDistance == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SailingClusterGroups clusterGroups = Activator.getClusterGroups();
|
||||
Cluster<Double> cluster = clusterGroups.getPercentageClusterGroup().getClusterFor(normalizedDistance);
|
||||
return new ClusterDTO(clusterGroups.getPercentageClusterFormatter().format(cluster));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getDistanceToStartLineAtStart() {
|
||||
return getTrackedRace().getDistanceToStartLine(getCompetitor(), 0);
|
||||
@@ -71,6 +95,46 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
||||
return distance / length;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getWindwardDistanceToAdvantageousLineEndAtStart() {
|
||||
TrackedRace trackedRace = getTrackedRace();
|
||||
TimePoint startOfRace = trackedRace.getStartOfRace();
|
||||
LineDetails startLine = trackedRace.getStartLine(startOfRace);
|
||||
Mark advantageousMark = null;
|
||||
switch (startLine.getAdvantageousSideWhileApproachingLine()) {
|
||||
case PORT:
|
||||
advantageousMark = startLine.getPortMarkWhileApproachingLine();
|
||||
break;
|
||||
case STARBOARD:
|
||||
advantageousMark = startLine.getStarboardMarkWhileApproachingLine();
|
||||
break;
|
||||
}
|
||||
if (advantageousMark == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
GPSFixTrack<Mark, GPSFix> advantageousMarkTrack = trackedRace.getOrCreateTrack(advantageousMark);
|
||||
Position advantageousMarkPosition = advantageousMarkTrack.getEstimatedPosition(startOfRace, false);
|
||||
GPSFixTrack<Competitor, GPSFixMoving> competitorTrack = trackedRace.getTrack(getCompetitor());
|
||||
Position competitorPosition = competitorTrack.getEstimatedPosition(startOfRace, false);
|
||||
|
||||
TrackedLeg trackedLeg = trackedRace.getTrackedLeg(trackedRace.getRace().getCourse().getFirstLeg());
|
||||
Distance distance = trackedLeg.getWindwardDistance(competitorPosition, advantageousMarkPosition, startOfRace, WindPositionMode.LEG_MIDDLE);
|
||||
return distance;
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterDTO getPercentageClusterForRelativeScore() {
|
||||
Double relativeScore = getTrackedRaceContext().getRelativeScoreForCompetitor(getCompetitor());
|
||||
if (relativeScore == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SailingClusterGroups clusterGroups = Activator.getClusterGroups();
|
||||
Cluster<Double> cluster = clusterGroups.getPercentageClusterGroup().getClusterFor(relativeScore);
|
||||
return new ClusterDTO(clusterGroups.getPercentageClusterFormatter().format(cluster));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Speed getSpeedWhenStarting() {
|
||||
return getTrackedRace().getSpeedWhenCrossingStartLine(getCompetitor());
|
||||
@@ -91,7 +155,7 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAbsoluteRankThirtySecondsAfterStart() {
|
||||
public Double getRankThirtySecondsAfterStart() {
|
||||
TimePoint startOfRace = getTrackedRace().getStartOfRace();
|
||||
if (startOfRace == null) {
|
||||
return null;
|
||||
@@ -101,6 +165,21 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
||||
return rank == 0 ? null : Double.valueOf(rank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getRankAfterHalfOfTheFirstLeg() {
|
||||
Course course = getTrackedRace().getRace().getCourse();
|
||||
TrackedLegOfCompetitor trackedLeg = getTrackedRace().getTrackedLeg(getCompetitor(), course.getFirstLeg());
|
||||
TimePoint startTime = trackedLeg.getStartTime();
|
||||
TimePoint finishTime = trackedLeg.getFinishTime();
|
||||
if (startTime == null || finishTime == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
long halfOffset = (finishTime.asMillis() - startTime.asMillis()) / 2;
|
||||
int rank = getTrackedRace().getRank(getCompetitor(), startTime.plus(halfOffset));
|
||||
return rank == 0 ? null : Double.valueOf(rank);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getRankAtFirstMark() {
|
||||
Course course = getTrackedRace().getRace().getCourse();
|
||||
@@ -113,15 +192,10 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
|
||||
@Override
|
||||
public Double getRankGainsOrLossesBetweenFirstMarkAndFinish() {
|
||||
Double rankAtFirstMark = getRankAtFirstMark();
|
||||
Double rankAtFinish = getRankAtFinish();
|
||||
Double rankAtFinish = getTrackedRaceContext().getRankAtFinishForCompetitor(getCompetitor());
|
||||
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
|
||||
public int getNumberOfManeuvers() {
|
||||
return getNumberOfTacks() + getNumberOfJibes();
|
||||
|
||||
+27
@@ -1,5 +1,7 @@
|
||||
package com.sap.sailing.datamining.impl.data;
|
||||
|
||||
import com.sap.sailing.datamining.Activator;
|
||||
import com.sap.sailing.datamining.SailingClusterGroups;
|
||||
import com.sap.sailing.datamining.data.HasTrackedLegContext;
|
||||
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
|
||||
import com.sap.sailing.domain.base.Competitor;
|
||||
@@ -9,6 +11,8 @@ import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
import com.sap.sse.datamining.data.Cluster;
|
||||
import com.sap.sse.datamining.shared.impl.dto.ClusterDTO;
|
||||
|
||||
public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompetitorContext {
|
||||
|
||||
@@ -48,6 +52,18 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
|
||||
return getCompetitor().getSearchTag();
|
||||
}
|
||||
|
||||
@Override
|
||||
public ClusterDTO getPercentageClusterForRelativeScoreInRace() {
|
||||
Double relativeScore = getTrackedLegContext().getTrackedRaceContext().getRelativeScoreForCompetitor(getCompetitor());
|
||||
if (relativeScore == null) {
|
||||
return null;
|
||||
}
|
||||
|
||||
SailingClusterGroups clusterGroups = Activator.getClusterGroups();
|
||||
Cluster<Double> cluster = clusterGroups.getPercentageClusterGroup().getClusterFor(relativeScore);
|
||||
return new ClusterDTO(clusterGroups.getPercentageClusterFormatter().format(cluster));
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getDistanceTraveled() {
|
||||
TimePoint timePoint = getTrackedLegContext().getTrackedRaceContext().getTrackedRace().getEndOfTracking();
|
||||
@@ -94,4 +110,15 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
|
||||
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.RaceDefinition;
|
||||
import com.sap.sailing.domain.base.Regatta;
|
||||
import com.sap.sailing.domain.common.NauticalSide;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.tracking.GPSFixTrack;
|
||||
import com.sap.sailing.domain.tracking.LineDetails;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.Util;
|
||||
@@ -91,6 +93,12 @@ public class TrackedRaceWithContext implements HasTrackedRaceContext {
|
||||
return calendar.get(Calendar.YEAR);
|
||||
}
|
||||
|
||||
@Override
|
||||
public NauticalSide getAdvantageousEndOfLine() {
|
||||
LineDetails startLine = getTrackedRace().getStartLine(getTrackedRace().getStartOfRace());
|
||||
return startLine.getAdvantageousSideWhileApproachingLine();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Boolean isTracked() {
|
||||
return getTrackedRace().hasStarted(MillisecondsTimePoint.now());
|
||||
@@ -126,4 +134,19 @@ public class TrackedRaceWithContext implements HasTrackedRaceContext {
|
||||
return number;
|
||||
}
|
||||
|
||||
// Convenience methods for race dependent calculation to avoid code duplication
|
||||
public Double getRelativeScoreForCompetitor(Competitor competitor) {
|
||||
Double rankAtFinish = getRankAtFinishForCompetitor(competitor);
|
||||
if (rankAtFinish == null) {
|
||||
return null;
|
||||
}
|
||||
return rankAtFinish / Util.size(getTrackedRace().getRace().getCompetitors());
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getRankAtFinishForCompetitor(Competitor competitor) {
|
||||
int rank = getTrackedRace().getRank(competitor, getTrackedRace().getEndOfTracking());
|
||||
return rank == 0 ? null : Double.valueOf(rank);
|
||||
}
|
||||
|
||||
}
|
||||
+49
@@ -3,10 +3,13 @@ package com.sap.sailing.domain.common.dto;
|
||||
import java.io.Serializable;
|
||||
import java.util.List;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.MaxPointsReason;
|
||||
import com.sap.sailing.domain.common.RaceIdentifier;
|
||||
import com.sap.sailing.domain.common.Tack;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sse.common.Duration;
|
||||
import com.sap.sse.common.impl.MillisecondsDurationImpl;
|
||||
|
||||
/**
|
||||
* Holds a single competitor's scoring details for a single race. It may optionally contain
|
||||
@@ -78,6 +81,8 @@ public class LeaderboardEntryDTO implements Serializable {
|
||||
*/
|
||||
public Duration averageSamplingInterval;
|
||||
|
||||
public Double averageRideHeightInMeters;
|
||||
|
||||
/**
|
||||
* The time gap to the competitor leading the race; for one-design races this is the time the competitor is expected
|
||||
* to need to reach the leader's (windward) position (if in the same leg) or the difference between the time at
|
||||
@@ -133,6 +138,44 @@ public class LeaderboardEntryDTO implements Serializable {
|
||||
return totalPointsCorrected || (reasonForMaxPoints != null && reasonForMaxPoints != MaxPointsReason.NONE);
|
||||
}
|
||||
|
||||
public Duration getTimeSailedInMillis() {
|
||||
final Duration result;
|
||||
if (legDetails != null) {
|
||||
long timeInMilliseconds = 0;
|
||||
for (LegEntryDTO legDetail : legDetails) {
|
||||
if (legDetail != null) {
|
||||
if (legDetail.distanceTraveledInMeters != null && legDetail.timeInMilliseconds != null) {
|
||||
timeInMilliseconds += legDetail.timeInMilliseconds;
|
||||
} else {
|
||||
timeInMilliseconds = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
result = new MillisecondsDurationImpl(timeInMilliseconds);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Distance getDistanceTraveledInMeters() {
|
||||
Distance result = null;
|
||||
if (legDetails != null) {
|
||||
for (LegEntryDTO legDetail : legDetails) {
|
||||
if (legDetail != null) {
|
||||
if (legDetail.distanceTraveledInMeters != null) {
|
||||
if (result == null) {
|
||||
result = Distance.NULL;
|
||||
}
|
||||
result = result.add(new MeterDistance(legDetail.distanceTraveledInMeters));
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
final int prime = 31;
|
||||
@@ -174,6 +217,7 @@ public class LeaderboardEntryDTO implements Serializable {
|
||||
result = prime * result
|
||||
+ ((speedOverGroundAtStartOfRaceInKnots == null) ? 0 : speedOverGroundAtStartOfRaceInKnots.hashCode());
|
||||
result = prime * result + ((startTack == null) ? 0 : startTack.hashCode());
|
||||
result = prime * result + ((averageRideHeightInMeters == null) ? 0 : averageRideHeightInMeters.hashCode());
|
||||
result = prime * result + ((averageSamplingInterval == null) ? 0 : averageSamplingInterval.hashCode());
|
||||
result = prime * result + ((timeSinceLastPositionFixInSeconds == null) ? 0 : timeSinceLastPositionFixInSeconds.hashCode());
|
||||
result = prime * result + ((netPoints == null) ? 0 : netPoints.hashCode());
|
||||
@@ -282,6 +326,11 @@ public class LeaderboardEntryDTO implements Serializable {
|
||||
return false;
|
||||
if (startTack != other.startTack)
|
||||
return false;
|
||||
if (averageRideHeightInMeters == null) {
|
||||
if (other.averageRideHeightInMeters != null)
|
||||
return false;
|
||||
} else if (!averageRideHeightInMeters.equals(other.averageRideHeightInMeters))
|
||||
return false;
|
||||
if (averageSamplingInterval == null) {
|
||||
if (other.averageSamplingInterval != null)
|
||||
return false;
|
||||
|
||||
+25
@@ -4,6 +4,9 @@ import java.io.Serializable;
|
||||
import java.util.Date;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sse.common.Duration;
|
||||
|
||||
/**
|
||||
* Holds data about one competitor and all races represented by the owning {@link LeaderboardDTO leaderboard}.
|
||||
*
|
||||
@@ -117,6 +120,28 @@ public class LeaderboardRowDTO implements Serializable {
|
||||
return true;
|
||||
}
|
||||
|
||||
public Distance getDistanceTraveledInMeters(String raceColumnName) {
|
||||
final Distance result;
|
||||
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
|
||||
if (fieldsForRace != null) {
|
||||
result = fieldsForRace.getDistanceTraveledInMeters();
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
public Duration getTimeSailed(String raceColumnName) {
|
||||
final Duration result;
|
||||
LeaderboardEntryDTO fieldsForRace = fieldsByRaceColumnName.get(raceColumnName);
|
||||
if (fieldsForRace != null) {
|
||||
result = fieldsForRace.getTimeSailedInMillis();
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String toString() {
|
||||
return "LeaderboardRowDTO for competitor "+competitor;
|
||||
|
||||
+3
-1
@@ -1,5 +1,7 @@
|
||||
package com.sap.sailing.domain.common.tracking;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
|
||||
/**
|
||||
* Specific {@link SensorFix} for bravo devices (http://www.bravosystems.es/).
|
||||
*
|
||||
@@ -12,5 +14,5 @@ public interface BravoFix extends SensorFix {
|
||||
*
|
||||
* @return the ride height
|
||||
*/
|
||||
double getRideHeight();
|
||||
Distance getRideHeight();
|
||||
}
|
||||
+4
-2
@@ -1,5 +1,7 @@
|
||||
package com.sap.sailing.domain.common.tracking.impl;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
|
||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
@@ -32,8 +34,8 @@ public class BravoFixImpl implements BravoFix {
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getRideHeight() {
|
||||
return fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightColumn);
|
||||
public Distance getRideHeight() {
|
||||
return new MeterDistance(fix.get(BravoSensorDataMetadata.INSTANCE.rideHeightColumn));
|
||||
}
|
||||
|
||||
}
|
||||
+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.RegattaImpl;
|
||||
import com.sap.sailing.domain.base.impl.WaypointImpl;
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.DegreePosition;
|
||||
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
|
||||
import com.sap.sailing.domain.common.impl.MeterDistance;
|
||||
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
|
||||
import com.sap.sailing.domain.common.tracking.DoubleVectorFix;
|
||||
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
@@ -95,9 +97,9 @@ public class SensorFixStoreAndLoadTest {
|
||||
private static final long FIX_TIMESTAMP2 = 120;
|
||||
private static final long FIX_TIMESTAMP3 = 210;
|
||||
private static final long AFTER_LAST_FIX = FIX_TIMESTAMP3 + 1;
|
||||
private static final double FIX_RIDE_HEIGHT = 1337.0;
|
||||
private static final double FIX_RIDE_HEIGHT2 = 1338.0;
|
||||
private static final double FIX_RIDE_HEIGHT3 = 1336.0;
|
||||
private static final Distance FIX_RIDE_HEIGHT = new MeterDistance(1337.0);
|
||||
private static final Distance FIX_RIDE_HEIGHT2 = new MeterDistance(1338.0);
|
||||
private static final Distance FIX_RIDE_HEIGHT3 = new MeterDistance(1336.0);
|
||||
private static final double FIX_TEST_VALUE = 12.0;
|
||||
protected final MockSmartphoneImeiServiceFinderFactory serviceFinderFactory = new MockSmartphoneImeiServiceFinderFactory();
|
||||
protected final DeviceIdentifier device = new SmartphoneImeiIdentifier("a");
|
||||
@@ -384,20 +386,14 @@ public class SensorFixStoreAndLoadTest {
|
||||
public void testBravoFixIsCorrectlyWrapped() throws InterruptedException {
|
||||
regattaLog.add(new RegattaLogDeviceCompetitorBravoMappingEventImpl(new MillisecondsTimePoint(3), author, comp,
|
||||
device, new MillisecondsTimePoint(START_OF_TRACKING), new MillisecondsTimePoint(MID_OF_TRACKING)));
|
||||
|
||||
addBravoFixes();
|
||||
|
||||
FixLoaderAndTracker fixLoaderAndTracker = createFixLoaderAndTracker();
|
||||
|
||||
trackedRace.attachRaceLog(raceLog);
|
||||
trackedRace.attachRegattaLog(regattaLog);
|
||||
|
||||
trackedRace.waitForLoadingToFinish();
|
||||
|
||||
BravoFixTrack<Competitor> bravoFixTrack = trackedRace.getSensorTrack(comp, BravoFixTrack.TRACK_NAME);
|
||||
assertEquals(FIX_RIDE_HEIGHT,
|
||||
bravoFixTrack.getFirstFixAtOrAfter(new MillisecondsTimePoint(FIX_TIMESTAMP)).getRideHeight());
|
||||
|
||||
fixLoaderAndTracker.stop(true);
|
||||
}
|
||||
|
||||
@@ -482,9 +478,9 @@ public class SensorFixStoreAndLoadTest {
|
||||
}
|
||||
|
||||
private void addBravoFixes() {
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP, FIX_RIDE_HEIGHT));
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2));
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3));
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP, FIX_RIDE_HEIGHT.getMeters()));
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP2, FIX_RIDE_HEIGHT2.getMeters()));
|
||||
store.storeFix(device, createBravoDoubleVectorFixWithRideHeight(FIX_TIMESTAMP3, FIX_RIDE_HEIGHT3.getMeters()));
|
||||
}
|
||||
|
||||
private FixLoaderAndTracker createFixLoaderAndTracker() {
|
||||
|
||||
+5
-3
@@ -541,15 +541,12 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
|
||||
@Override
|
||||
public boolean isControlTrackingFromStartAndFinishTimes() {
|
||||
// TODO Auto-generated method stub
|
||||
return false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void setControlTrackingFromStartAndFinishTimes(
|
||||
boolean controlTrackingFromStartAndFinishTimes) {
|
||||
// TODO Auto-generated method stub
|
||||
|
||||
}
|
||||
};
|
||||
}
|
||||
@@ -1135,4 +1132,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
||||
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) {
|
||||
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);
|
||||
}
|
||||
|
||||
private void assertRideHeight(BravoFixTrack<Competitor> track, TimePoint timePoint, double expected) {
|
||||
assertEquals(expected, track.getRideHeight(timePoint), 0.0);
|
||||
private void assertRideHeight(BravoFixTrack<Competitor> track, TimePoint timePoint, double expectedRideHeightInMeters) {
|
||||
assertEquals(expectedRideHeightInMeters, track.getRideHeight(timePoint).getMeters(), 0.0);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
|
||||
+6
-2
@@ -1437,6 +1437,8 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
} else {
|
||||
entryDTO.timeSinceLastPositionFixInSeconds = null;
|
||||
}
|
||||
final Distance averageRideHeight = trackedRace.getAverageRideHeight(competitor, timePoint);
|
||||
entryDTO.averageRideHeightInMeters = averageRideHeight == null ? null : averageRideHeight.getMeters();
|
||||
}
|
||||
if (addLegDetails && trackedRace != null) {
|
||||
try {
|
||||
@@ -1721,11 +1723,13 @@ public abstract class AbstractSimpleLeaderboardImpl implements Leaderboard, Race
|
||||
Double speedOverGroundInKnots;
|
||||
if (trackedLeg.hasFinishedLeg(timePoint)) {
|
||||
speedOverGroundInKnots = averageSpeedOverGround == null ? null : averageSpeedOverGround.getKnots();
|
||||
result.currentRideHeightInMeters = trackedLeg.getAverageRideHeight(timePoint);
|
||||
final Distance averageRideHeight = trackedLeg.getAverageRideHeight(timePoint);
|
||||
result.currentRideHeightInMeters = averageRideHeight == null ? null : averageRideHeight.getMeters();
|
||||
} else {
|
||||
final SpeedWithBearing speedOverGround = trackedLeg.getSpeedOverGround(timePoint);
|
||||
speedOverGroundInKnots = speedOverGround == null ? null : speedOverGround.getKnots();
|
||||
result.currentRideHeightInMeters = trackedLeg.getRideHeight(timePoint);
|
||||
final Distance rideHeight = trackedLeg.getRideHeight(timePoint);
|
||||
result.currentRideHeightInMeters = rideHeight == null ? null : rideHeight.getMeters();
|
||||
}
|
||||
result.currentSpeedOverGroundInKnots = speedOverGroundInKnots == null ? null : speedOverGroundInKnots;
|
||||
Distance distanceTraveled = trackedLeg.getDistanceTraveled(timePoint);
|
||||
|
||||
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking;
|
||||
|
||||
import java.io.Serializable;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||
import com.sap.sse.common.TimePoint;
|
||||
import com.sap.sse.common.WithID;
|
||||
@@ -22,7 +23,7 @@ public interface BravoFixTrack<ItemType extends WithID & Serializable> extends S
|
||||
* @return the ride height for the given {@link TimePoint} or <code>null</code> if the {@link TimePoint} is not in the range
|
||||
* where we have fixes for.
|
||||
*/
|
||||
Double getRideHeight(TimePoint timePoint);
|
||||
Distance getRideHeight(TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* Calculates the average ride height for the given time range.
|
||||
@@ -34,5 +35,5 @@ public interface BravoFixTrack<ItemType extends WithID & Serializable> extends S
|
||||
* @return the average ride height for the given time range or <code>null</code> if there are no fixes in the given
|
||||
* time range.
|
||||
*/
|
||||
Double getAverageRideHeight(TimePoint from, TimePoint to);
|
||||
Distance getAverageRideHeight(TimePoint from, TimePoint to);
|
||||
}
|
||||
+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
|
||||
* mark passing event was already received, the average ride height for the entire leg (and no further) is computed.
|
||||
*/
|
||||
Double getAverageRideHeight(TimePoint timePoint);
|
||||
Distance getAverageRideHeight(TimePoint timePoint);
|
||||
|
||||
/**
|
||||
* @return <code>null</code> if the competitor hasn't started this leg yet, otherwise the fix where the maximum speed was
|
||||
@@ -237,7 +237,7 @@ public interface TrackedLegOfCompetitor extends Serializable {
|
||||
* <code>timePoint</code>, returns the current ride height for this time point. If the competitor has already
|
||||
* finished the leg, the ride height at the time the competitor finished the leg is returned.
|
||||
*/
|
||||
Double getRideHeight(TimePoint at);
|
||||
Distance getRideHeight(TimePoint at);
|
||||
|
||||
/**
|
||||
* Computes the distance along the wind track to the wind-projected position of the race's overall leader. If leader
|
||||
|
||||
@@ -47,6 +47,7 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
|
||||
import com.sap.sailing.domain.common.tracking.SensorFix;
|
||||
import com.sap.sailing.domain.polars.NotEnoughDataHasBeenAddedException;
|
||||
import com.sap.sailing.domain.polars.PolarDataService;
|
||||
import com.sap.sailing.domain.racelog.tracking.GPSFixStore;
|
||||
import com.sap.sailing.domain.ranking.RankingMetric;
|
||||
import com.sap.sailing.domain.ranking.RankingMetric.RankingInfo;
|
||||
import com.sap.sailing.domain.tracking.impl.NonCachingMarkPositionAtTimePointCache;
|
||||
@@ -691,6 +692,8 @@ public interface TrackedRace extends Serializable, IsManagedByCache<SharedDomain
|
||||
Distance getAverageSignedCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
|
||||
boolean waitForLatestAnalysis) throws NoWindException;
|
||||
|
||||
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint);
|
||||
|
||||
WindStore getWindStore();
|
||||
|
||||
Competitor getOverallLeader(TimePoint timePoint);
|
||||
|
||||
+15
-10
@@ -1,10 +1,8 @@
|
||||
package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import java.io.Serializable;
|
||||
import java.util.OptionalDouble;
|
||||
import java.util.Spliterator;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import com.sap.sailing.domain.common.Distance;
|
||||
import com.sap.sailing.domain.common.sensordata.BravoSensorDataMetadata;
|
||||
import com.sap.sailing.domain.common.tracking.BravoFix;
|
||||
import com.sap.sailing.domain.tracking.BravoFixTrack;
|
||||
@@ -34,7 +32,7 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getRideHeight(TimePoint timePoint) {
|
||||
public Distance getRideHeight(TimePoint timePoint) {
|
||||
BravoFix fixAfter = getFirstFixAtOrAfter(timePoint);
|
||||
if (fixAfter != null && fixAfter.getTimePoint().compareTo(timePoint) == 0) {
|
||||
// exact match of timepoint -> no interpolation necessary
|
||||
@@ -54,17 +52,24 @@ public class BravoFixTrackImpl<ItemType extends WithID & Serializable> extends S
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAverageRideHeight(TimePoint from, TimePoint to) {
|
||||
public Distance getAverageRideHeight(TimePoint from, TimePoint to) {
|
||||
final Distance result;
|
||||
lockForRead();
|
||||
try {
|
||||
Spliterator<BravoFix> fixes = getFixes(from, true, to, true).spliterator();
|
||||
OptionalDouble average = StreamSupport.stream(fixes, false).mapToDouble(BravoFix::getRideHeight).average();
|
||||
if (average.isPresent()) {
|
||||
return average.getAsDouble();
|
||||
Distance sum = Distance.NULL;
|
||||
int count = 0;
|
||||
for (final BravoFix fix : getFixes(from, true, to, true)) {
|
||||
sum = sum.add(fix.getRideHeight());
|
||||
count++;
|
||||
}
|
||||
if (count > 0) {
|
||||
result = sum.scale(1./count);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
} finally {
|
||||
unlockAfterRead();
|
||||
}
|
||||
return null;
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+7
-6
@@ -174,8 +174,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
if (timePointToUse != null) {
|
||||
Distance d = getDistanceTraveled(timePointToUse);
|
||||
long millis = timePointToUse.asMillis() - legStart.getTimePoint().asMillis();
|
||||
result = d.inTime(millis);
|
||||
result = d.inTime(legStart.getTimePoint().until(timePointToUse));
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
@@ -184,7 +183,7 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getAverageRideHeight(TimePoint timePoint) {
|
||||
public Distance getAverageRideHeight(TimePoint timePoint) {
|
||||
MarkPassing legStart = getMarkPassingForLegStart();
|
||||
if (legStart != null) {
|
||||
BravoFixTrack<Competitor> track = getTrackedRace()
|
||||
@@ -677,15 +676,17 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Double getRideHeight(TimePoint at) {
|
||||
public Distance getRideHeight(TimePoint at) {
|
||||
final Distance result;
|
||||
if (hasStartedLeg(at)) {
|
||||
TimePoint timePoint =hasFinishedLeg(at) ? getMarkPassingForLegEnd().getTimePoint() : at;
|
||||
BravoFixTrack<Competitor> track = getTrackedRace()
|
||||
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||
return track == null ? null : track.getRideHeight(timePoint);
|
||||
result = track == null ? null : track.getRideHeight(timePoint);
|
||||
} else {
|
||||
return null;
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+16
@@ -1548,6 +1548,22 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageRideHeight(Competitor competitor, TimePoint timePoint) {
|
||||
final Distance result;
|
||||
BravoFixTrack<Competitor> track = getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||
final Leg firstLeg;
|
||||
final TrackedLegOfCompetitor firstTrackedLeg;
|
||||
if (track != null && (firstLeg = getRace().getCourse().getFirstLeg()) != null && (firstTrackedLeg = getTrackedLeg(competitor, firstLeg)).hasStartedLeg(timePoint)) {
|
||||
final TrackedLegOfCompetitor lastTrackedLeg = getTrackedLegFinishingAt(getRace().getCourse().getLastWaypoint()).getTrackedLeg(competitor);
|
||||
TimePoint endTimePoint = lastTrackedLeg.hasFinishedLeg(timePoint) ? lastTrackedLeg.getFinishTime() : timePoint;
|
||||
result = track.getAverageRideHeight(firstTrackedLeg.getStartTime(), endTimePoint);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
@Override
|
||||
public TrackedLegOfCompetitor getCurrentLeg(Competitor competitor, TimePoint timePoint) {
|
||||
// If the mark passing that starts a leg happened exactly at timePoint, the MarkPassingByTimeComparator won't consider
|
||||
|
||||
+5
@@ -865,4 +865,9 @@ public class MockedTrackedRace implements DynamicTrackedRace {
|
||||
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
||||
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.rpc.AsyncCallback;
|
||||
import com.google.gwt.user.client.ui.AcceptsOneWidget;
|
||||
import com.sap.sailing.gwt.autoplay.client.shared.leaderboard.LeaderboardWithHeaderPerspectiveLifecycle;
|
||||
import com.sap.sailing.gwt.autoplay.client.shared.leaderboard.LeaderboardWithHeaderPerspectiveSettings;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.raceboard.RaceBoardPerspectiveLifecycle;
|
||||
import com.sap.sailing.gwt.ui.raceboard.RaceBoardPerspectiveSettings;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.StrippedLeaderboardDTO;
|
||||
import com.sap.sse.gwt.client.mvp.ErrorView;
|
||||
import com.sap.sse.gwt.client.shared.perspective.PerspectiveLifecycleWithAllSettings;
|
||||
import com.sap.sse.gwt.client.useragent.UserAgentDetails;
|
||||
import com.sap.sse.gwt.shared.GwtHttpRequestUtils;
|
||||
|
||||
@@ -26,6 +34,17 @@ public class PlayerActivity extends AbstractActivity {
|
||||
this.autoPlayController = null;
|
||||
}
|
||||
|
||||
private StrippedLeaderboardDTO getSelectedLeaderboard(EventDTO event,String leaderBoardName) {
|
||||
for (LeaderboardGroupDTO leaderboardGroup : event.getLeaderboardGroups()) {
|
||||
for (StrippedLeaderboardDTO leaderboard : leaderboardGroup.getLeaderboards()) {
|
||||
if (leaderboard.name.equals(leaderBoardName)) {
|
||||
return leaderboard;
|
||||
}
|
||||
}
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void start(final AcceptsOneWidget panel, EventBus eventBus) {
|
||||
final boolean showRaceDetails = GwtHttpRequestUtils.getBooleanParameter(PARAM_SHOW_RACE_DETAILS, false);
|
||||
@@ -36,6 +55,16 @@ public class PlayerActivity extends AbstractActivity {
|
||||
clientFactory.getSailingService().getEventById(eventUUID, true, new AsyncCallback<EventDTO>() {
|
||||
@Override
|
||||
public void onSuccess(final EventDTO event) {
|
||||
|
||||
//This place fixes a specific null pointer bug3950, once the settings are redone, this should be removed, and instead be done via playerplace (see comment there)
|
||||
StrippedLeaderboardDTO leaderBoardDTO = getSelectedLeaderboard(event,playerPlace.getConfiguration().getLeaderboardName());
|
||||
LeaderboardWithHeaderPerspectiveLifecycle leaderboardPerspectiveLifecycle = new LeaderboardWithHeaderPerspectiveLifecycle(leaderBoardDTO, StringMessages.INSTANCE);
|
||||
PerspectiveLifecycleWithAllSettings<LeaderboardWithHeaderPerspectiveLifecycle, LeaderboardWithHeaderPerspectiveSettings> leaderboardPerspectiveLifecyclesAndSettings = new PerspectiveLifecycleWithAllSettings<>(leaderboardPerspectiveLifecycle, leaderboardPerspectiveLifecycle.createDefaultSettings());
|
||||
RaceBoardPerspectiveLifecycle raceboardPerspectiveLifecycle = new RaceBoardPerspectiveLifecycle(leaderBoardDTO, StringMessages.INSTANCE);
|
||||
PerspectiveLifecycleWithAllSettings<RaceBoardPerspectiveLifecycle, RaceBoardPerspectiveSettings> raceboardPerspectiveLifecyclesAndSettings = new PerspectiveLifecycleWithAllSettings<>(raceboardPerspectiveLifecycle, raceboardPerspectiveLifecycle.createDefaultSettings());
|
||||
|
||||
|
||||
|
||||
UserAgentDetails userAgent = new UserAgentDetails(Window.Navigator.getUserAgent());
|
||||
|
||||
PlayerView view = clientFactory.createPlayerView();
|
||||
@@ -44,8 +73,8 @@ public class PlayerActivity extends AbstractActivity {
|
||||
autoPlayController = new AutoPlayController(clientFactory.getSailingService(), clientFactory
|
||||
.getMediaService(), clientFactory.getUserService(), clientFactory.getErrorReporter(),
|
||||
playerPlace.getConfiguration(), userAgent, delayToLiveMillis, showRaceDetails, view,
|
||||
playerPlace.getLeaderboardPerspectiveLifecycleWithAllSettings(),
|
||||
playerPlace.getRaceboardPerspectiveLifecycleWithAllSettings());
|
||||
leaderboardPerspectiveLifecyclesAndSettings,
|
||||
raceboardPerspectiveLifecyclesAndSettings);
|
||||
autoPlayController.updatePlayMode(AutoPlayModes.Leaderboard);
|
||||
}
|
||||
|
||||
|
||||
+2
@@ -23,6 +23,8 @@ public class PlayerPlace extends AbstractBasePlace {
|
||||
Integer timeToSwitchBeforeRaceStartInSeconds = Integer.valueOf(getParameter(AutoPlayerConfiguration.PARAM_TIME_TO_SWITCH_BEFORE_RACE_START));
|
||||
|
||||
this.playerConfiguration = new AutoPlayerConfiguration(eventUuidAsString, leaderboardName, fullscreen, timeToSwitchBeforeRaceStartInSeconds);
|
||||
|
||||
//these are currently regenerated in the PlayerActivity, based on the historydata. Once the settings are properly stored, this should be replaced.
|
||||
this.leaderboardPerspectiveLifecycleWithAllSettings = null;
|
||||
this.raceboardPerspectiveLifecycleWithAllSettings = null;
|
||||
}
|
||||
|
||||
+1
-1
@@ -55,7 +55,7 @@ public class TabletAndDesktopRegattaEventView extends Composite implements Event
|
||||
|
||||
if(currentPresenter.getEventDTO().getType() == EventType.SERIES_EVENT) {
|
||||
final PlaceNavigation<SeriesDefaultPlace> currentEventSeriesNavigation = currentPresenter.getCurrentEventSeriesNavigation();
|
||||
Anchor seriesAnchor = new Anchor(currentPresenter.getEventDTO().getSeriesName());
|
||||
Anchor seriesAnchor = new Anchor(i18n.overallLeaderboardSelection());
|
||||
seriesAnchor.setHref(currentEventSeriesNavigation.getTargetUrl());
|
||||
seriesAnchor.addClickHandler(new ClickHandler() {
|
||||
@Override
|
||||
|
||||
+1
-1
@@ -41,7 +41,7 @@ public class DeviceConfigurationQRIdentifierDialog extends DialogBox {
|
||||
Window.alert(stringMessages.notCapableOfGeneratingACodeForIdentifier());
|
||||
} else if (!identifierBox.getValue().isEmpty() && !serverBox.getValue().isEmpty()) {
|
||||
String apkUrl = getServerUrlWithoutFinalSlash() + rcAppApkPath;
|
||||
return DeviceConfigurationQRCodeUtils.composeQRContent(URL.encodeQueryString(identifierBox.getValue()), apkUrl, accessToken);
|
||||
return DeviceConfigurationQRCodeUtils.composeQRContent(URL.encodeQueryString(identifierBox.getValue()), apkUrl, URL.encodeQueryString(accessToken));
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
+2
-5
@@ -37,14 +37,11 @@ public class EventCreateDialog extends EventDialog {
|
||||
endDateBox = createDateTimeBox(now);
|
||||
endDateBox.setFormat("dd/mm/yyyy hh:ii");
|
||||
endDateBox.ensureDebugId("EndDateTimeBox");
|
||||
officialWebsiteURLEntryField = createTextBox(null);
|
||||
officialWebsiteURLEntryField.setVisibleLength(50);
|
||||
baseURLEntryField = createTextBox(null);
|
||||
baseURLEntryField.setVisibleLength(50);
|
||||
sailorsInfoWebsiteURLEntryFields = createTextBoxesForLocalesAndDefault(Collections.<String, String> emptyMap());
|
||||
isPublicCheckBox = createCheckbox("");
|
||||
isPublicCheckBox.setValue(false);
|
||||
isPublicCheckBox.ensureDebugId("IsPublicCheckBox");
|
||||
baseURLEntryField = createTextBox(null);
|
||||
baseURLEntryField.setVisibleLength(50);
|
||||
imagesListComposite.fillImages(Collections.<ImageDTO>emptyList());
|
||||
videosListComposite.fillVideos(Collections.<VideoDTO>emptyList());
|
||||
// add default course area
|
||||
|
||||
+9
-31
@@ -5,7 +5,6 @@ import java.util.Collection;
|
||||
import java.util.Collections;
|
||||
import java.util.Date;
|
||||
import java.util.HashMap;
|
||||
import java.util.LinkedHashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
@@ -32,7 +31,6 @@ import com.sap.sailing.gwt.ui.shared.CourseAreaDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.LeaderboardGroupDTO;
|
||||
import com.sap.sailing.gwt.ui.shared.VenueDTO;
|
||||
import com.sap.sse.gwt.client.GWTLocaleUtil;
|
||||
import com.sap.sse.gwt.client.IconResources;
|
||||
import com.sap.sse.gwt.client.controls.listedit.GenericStringListInlineEditorComposite;
|
||||
import com.sap.sse.gwt.client.controls.listedit.StringConstantsListEditorComposite;
|
||||
@@ -48,14 +46,13 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
||||
protected BetterDateTimeBox endDateBox;
|
||||
protected CheckBox isPublicCheckBox;
|
||||
protected UUID id;
|
||||
protected TextBox officialWebsiteURLEntryField;
|
||||
protected TextBox baseURLEntryField;
|
||||
protected Map<String, TextBox> sailorsInfoWebsiteURLEntryFields = new HashMap<>();
|
||||
protected CourseAreaListInlineEditorComposite courseAreaNameList;
|
||||
protected StringConstantsListEditorComposite leaderboardGroupList;
|
||||
protected Map<String, LeaderboardGroupDTO> availableLeaderboardGroupsByName;
|
||||
protected ImagesListComposite imagesListComposite;
|
||||
protected VideosListComposite videosListComposite;
|
||||
protected ExternalLinksComposite externalLinksComposite;
|
||||
|
||||
protected static class EventParameterValidator implements Validator<EventDTO> {
|
||||
|
||||
@@ -162,6 +159,7 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
||||
|
||||
imagesListComposite = new ImagesListComposite(sailingService, stringMessages);
|
||||
videosListComposite = new VideosListComposite(stringMessages);
|
||||
externalLinksComposite = new ExternalLinksComposite(stringMessages);
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -176,13 +174,9 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
||||
}
|
||||
result.setName(nameEntryField.getText());
|
||||
result.setDescription(descriptionEntryField.getText());
|
||||
result.setOfficialWebsiteURL(officialWebsiteURLEntryField.getText().trim().isEmpty() ? null : officialWebsiteURLEntryField.getText().trim());
|
||||
result.setOfficialWebsiteURL(externalLinksComposite.getOfficialWebsiteURLValue());
|
||||
result.setBaseURL(baseURLEntryField.getText().trim().isEmpty() ? null : baseURLEntryField.getText().trim());
|
||||
for (Map.Entry<String, TextBox> sailorsInfoWebsiteUrlEntry : sailorsInfoWebsiteURLEntryFields.entrySet()) {
|
||||
TextBox sailorsInfoWebsiteURLEntryField = sailorsInfoWebsiteUrlEntry.getValue();
|
||||
String sailorsInfoWebsiteURL = sailorsInfoWebsiteURLEntryField.getText().trim();
|
||||
result.setSailorsInfoWebsiteURL(sailorsInfoWebsiteUrlEntry.getKey(), sailorsInfoWebsiteURL.isEmpty() ? null : sailorsInfoWebsiteURLEntryField.getText().trim());
|
||||
}
|
||||
result.setSailorsInfoWebsiteURLs(externalLinksComposite.getSailorsInfoWebsiteURLs());
|
||||
result.startDate = startDateBox.getValue();
|
||||
result.endDate = endDateBox.getValue();
|
||||
result.isPublic = isPublicCheckBox.getValue();
|
||||
@@ -206,7 +200,7 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
||||
if (additionalWidget != null) {
|
||||
panel.add(additionalWidget);
|
||||
}
|
||||
Grid formGrid = new Grid(10 + GWTLocaleUtil.getLanguageCountWithDefault(), 2);
|
||||
Grid formGrid = new Grid(8, 2);
|
||||
int rowIndex = 0;
|
||||
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.name() + ":"));
|
||||
formGrid.setWidget(rowIndex++, 1, nameEntryField);
|
||||
@@ -222,24 +216,18 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
||||
formGrid.setWidget(rowIndex++, 1, endDateBox);
|
||||
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.isPublic() + ":"));
|
||||
formGrid.setWidget(rowIndex++, 1, isPublicCheckBox);
|
||||
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.eventOfficialWebsiteURL() + ":"));
|
||||
formGrid.setWidget(rowIndex++, 1, officialWebsiteURLEntryField);
|
||||
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.eventBaseURL() + ":"));
|
||||
formGrid.setWidget(rowIndex++, 1, baseURLEntryField);
|
||||
for (Map.Entry<String, TextBox> sailorsInfoWebsiteUrlEntry : sailorsInfoWebsiteURLEntryFields.entrySet()) {
|
||||
String suffix = " ["+ (sailorsInfoWebsiteUrlEntry.getKey() == null ? stringMessages.defaultLocale() + "*" : sailorsInfoWebsiteUrlEntry.getKey()) + "]";
|
||||
formGrid.setWidget(rowIndex, 0, new Label(stringMessages.eventSailorsInfoWebsiteURL() + suffix + ":"));
|
||||
formGrid.setWidget(rowIndex, 1, sailorsInfoWebsiteUrlEntry.getValue());
|
||||
rowIndex++;
|
||||
}
|
||||
formGrid.setText(rowIndex, 1, "[*] " + stringMessages.defaultSailorsInfoLinkInfoText());
|
||||
TabLayoutPanel tabPanel = new TabLayoutPanel(30, Unit.PX);
|
||||
tabPanel.ensureDebugId("EventDialogTabs");
|
||||
tabPanel.setHeight("525px");
|
||||
tabPanel.setHeight("400px");
|
||||
panel.add(tabPanel);
|
||||
final ScrollPanel eventTab = new ScrollPanel(formGrid);
|
||||
eventTab.ensureDebugId("EventTab");
|
||||
tabPanel.add(eventTab, stringMessages.event());
|
||||
final ScrollPanel externalLinksCompositeTab = new ScrollPanel(externalLinksComposite);
|
||||
externalLinksCompositeTab.ensureDebugId("ExternalLinksCompositeTab");
|
||||
tabPanel.add(externalLinksCompositeTab, stringMessages.externalLinks());
|
||||
final ScrollPanel leaderboardGroupTab = new ScrollPanel(leaderboardGroupList);
|
||||
leaderboardGroupTab.ensureDebugId("LeaderboardGroupsTab");
|
||||
tabPanel.add(leaderboardGroupTab, stringMessages.leaderboardGroups());
|
||||
@@ -259,14 +247,4 @@ public abstract class EventDialog extends DataEntryDialogWithBootstrap<EventDTO>
|
||||
protected FocusWidget getInitialFocusWidget() {
|
||||
return nameEntryField;
|
||||
}
|
||||
|
||||
protected Map<String, TextBox> createTextBoxesForLocalesAndDefault(Map<String, String> initialValues) {
|
||||
Map<String, TextBox> result = new LinkedHashMap<>();
|
||||
for(String localeName : GWTLocaleUtil.getAvailableLocalesAndDefault()) {
|
||||
TextBox sailorsInfoWebsiteURLEntryField = createTextBox(initialValues.get(localeName));
|
||||
sailorsInfoWebsiteURLEntryField.setVisibleLength(50);
|
||||
result.put(localeName, sailorsInfoWebsiteURLEntryField);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
}
|
||||
+2
-4
@@ -27,12 +27,9 @@ public class EventEditDialog extends EventDialog {
|
||||
endDateBox.setFormat("dd/mm/yyyy hh:ii");
|
||||
isPublicCheckBox = createCheckbox("");
|
||||
isPublicCheckBox.setValue(event.isPublic);
|
||||
id = event.id;
|
||||
officialWebsiteURLEntryField = createTextBox(event.getOfficialWebsiteURL());
|
||||
officialWebsiteURLEntryField.setVisibleLength(50);
|
||||
baseURLEntryField = createTextBox(event.getBaseURL());
|
||||
baseURLEntryField.setVisibleLength(50);
|
||||
sailorsInfoWebsiteURLEntryFields = createTextBoxesForLocalesAndDefault(event.getSailorsInfoWebsiteURLs());
|
||||
id = event.id;
|
||||
courseAreaNameList.setValue(new ArrayList<>(event.venue.getCourseAreas()));
|
||||
List<String> leaderboardGroupNames = new ArrayList<>();
|
||||
for(LeaderboardGroupDTO leaderboardGroupDTO: event.getLeaderboardGroups()) {
|
||||
@@ -41,5 +38,6 @@ public class EventEditDialog extends EventDialog {
|
||||
leaderboardGroupList.setValue(leaderboardGroupNames);
|
||||
imagesListComposite.fillImages(event.getImages());
|
||||
videosListComposite.fillVideos(event.getVideos());
|
||||
externalLinksComposite.fillExternalLinks(event);
|
||||
}
|
||||
}
|
||||
+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 filterDeviceMappings();
|
||||
String raceStartAnalysis();
|
||||
String urlForLanguage(String language);
|
||||
String urlForAllLanguages();
|
||||
String externalLinks();
|
||||
String buoyZoneRadiusInHullLengths();
|
||||
String incorrectValueForRegattaBuoyZoneRadiusInHullLengths();
|
||||
}
|
||||
+3
-1
@@ -1614,10 +1614,12 @@ raceAnalysis=Race analysis
|
||||
startAnalysis=Start analysis
|
||||
winningLanes=Winning lanes
|
||||
maximumNumberOfDiscards=Maximum number of discards
|
||||
externalLinks=External links
|
||||
urlForAllLanguages=URL for all languages
|
||||
urlForLanguage=URL for language ''{0}''
|
||||
notificationRegattaConfigurationUpdatedUsingByMarks=Active regatta configuration was set, using the "By Marks" course designer.
|
||||
warningOverrideRegattaConfigurationCourseDesignerToByMarks=Active regatta configuration already exists, but with the wrong course designer setting.\nWould you like to override course designer setting to "By Marks"?
|
||||
allRacesHaveBeenDenoted=All races have been denoted for racelog tracking.
|
||||
|
||||
chartSelectionHint=Hint: You can also use the leaderboard (on the LEADERBOARD tab) to select the competitors displayed in the chart.
|
||||
chooseALanguage=Choose a language
|
||||
solutions=Solutions
|
||||
|
||||
+3
-1
@@ -1600,10 +1600,12 @@ raceAnalysis=Renn-Analyse
|
||||
startAnalysis=Start-Analyse
|
||||
winningLanes=Sieger-Kurse
|
||||
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.
|
||||
warningOverrideRegattaConfigurationCourseDesignerToByMarks=Aktive Regattakonfiguration existiert bereits, verwendet aber den falschenKursdesigner.\nKursdesigner auf "By Marks" umstellen?
|
||||
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.
|
||||
chooseALanguage=Wähle eine Sprache
|
||||
solutions=Lösungen
|
||||
|
||||
+3
@@ -1612,6 +1612,9 @@ raceAnalysis=レース分析
|
||||
startAnalysis=スタート分析
|
||||
winningLanes=勝利レーン
|
||||
maximumNumberOfDiscards=カットレースの最大数
|
||||
externalLinks=External links
|
||||
urlForAllLanguages=URL for all languages
|
||||
urlForLanguage=URL for language ''{0}''
|
||||
notificationRegattaConfigurationUpdatedUsingByMarks="マーク別" コースデザイナーを使用して、有効なレガッタ設定が行われました。
|
||||
warningOverrideRegattaConfigurationCourseDesignerToByMarks=有効なレガッタ設定がすでに登録されていますが、コースデザイナーの設定が適切でありません。\n"マーク別" へのコースデザイナー設定を上書きしますか。
|
||||
allRacesHaveBeenDenoted=すべてのレースがレースログ追跡に表示されています。
|
||||
|
||||
+3
@@ -1612,6 +1612,9 @@ raceAnalysis=Анализ гонки
|
||||
startAnalysis=Начать анализ
|
||||
winningLanes=Выигрышные маршруты
|
||||
maximumNumberOfDiscards=Максимальное число исключений
|
||||
externalLinks=External links
|
||||
urlForAllLanguages=URL for all languages
|
||||
urlForLanguage=URL for language ''{0}''
|
||||
notificationRegattaConfigurationUpdatedUsingByMarks=Активная конфигурация регаты была настроена с использованием дизайнера курса "По отметкам".
|
||||
warningOverrideRegattaConfigurationCourseDesignerToByMarks=Активная конфигурация регаты уже существует, но с неверно выбранным дизайнером курса.\nПереопределить выбор дизайнера курса на "По отметкам"?
|
||||
allRacesHaveBeenDenoted=Отслеживание журнала всех гонок отменено.
|
||||
|
||||
+3
-1
@@ -1610,7 +1610,9 @@ raceAnalysis=比赛轮次分析
|
||||
startAnalysis=起航分析
|
||||
winningLanes=获胜的船道
|
||||
maximumNumberOfDiscards=最大放弃次数
|
||||
|
||||
externalLinks=External links
|
||||
urlForAllLanguages=URL for all languages
|
||||
urlForLanguage=URL for language ''{0}''
|
||||
chartSelectionHint=提示:您还可以使用积分榜(在 LEADERBOARD 选项卡中)选择在记录中显示的参赛队。
|
||||
chooseALanguage=选择一种语言
|
||||
solutions=解决方案
|
||||
|
||||
+3
@@ -1612,6 +1612,9 @@ raceAnalysis=比赛轮次分析
|
||||
startAnalysis=起航分析
|
||||
winningLanes=获胜的船道
|
||||
maximumNumberOfDiscards=最大放弃次数
|
||||
externalLinks=External links
|
||||
urlForAllLanguages=URL for all languages
|
||||
urlForLanguage=URL for language ''{0}''
|
||||
notificationRegattaConfigurationUpdatedUsingByMarks=已使用“按标志”场地设计器设置有效的比赛配置。
|
||||
warningOverrideRegattaConfigurationCourseDesignerToByMarks=有效的比赛配置已存在,但是场地设计器设置错误。\n是否将场地设计器设置覆盖为“按标志”?
|
||||
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.RegattaNameAndRaceName;
|
||||
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.GlobalNavigationPanel;
|
||||
import com.sap.sailing.gwt.ui.client.LogoAndTitlePanel;
|
||||
import com.sap.sailing.gwt.ui.client.StringMessages;
|
||||
import com.sap.sailing.gwt.ui.leaderboard.LeaderboardSettings.RaceColumnSelectionStrategies;
|
||||
import com.sap.sailing.gwt.ui.shared.EventDTO;
|
||||
@@ -45,7 +45,6 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint {
|
||||
private String leaderboardName;
|
||||
private String leaderboardGroupName;
|
||||
private LeaderboardType leaderboardType;
|
||||
private GlobalNavigationPanel globalNavigationPanel;
|
||||
private EventDTO event;
|
||||
|
||||
@Override
|
||||
@@ -121,30 +120,15 @@ public class LeaderboardEntryPoint extends AbstractSailingEntryPoint {
|
||||
private void createUI(boolean showRaceDetails, boolean embedded, boolean hideToolbar, EventDTO event) {
|
||||
DockLayoutPanel mainPanel = new DockLayoutPanel(Unit.PX);
|
||||
RootLayoutPanel.get().add(mainPanel);
|
||||
LogoAndTitlePanel logoAndTitlePanel = null;
|
||||
if (!embedded) {
|
||||
// Hack to shorten the leaderboardName in case of overall leaderboards
|
||||
String leaderboardDisplayName = Window.Location.getParameter("displayName");
|
||||
if (leaderboardDisplayName == null || leaderboardDisplayName.isEmpty()) {
|
||||
leaderboardDisplayName = leaderboardName;
|
||||
}
|
||||
globalNavigationPanel = new GlobalNavigationPanel(getStringMessages(), true, null, leaderboardGroupName, event, null);
|
||||
logoAndTitlePanel = new LogoAndTitlePanel(leaderboardGroupName, leaderboardDisplayName, getStringMessages(), this, getUserService()) {
|
||||
@Override
|
||||
public void onResize() {
|
||||
super.onResize();
|
||||
if (isSmallWidth()) {
|
||||
remove(globalNavigationPanel);
|
||||
} else {
|
||||
add(globalNavigationPanel);
|
||||
}
|
||||
}
|
||||
};
|
||||
logoAndTitlePanel.addStyleName("LogoAndTitlePanel");
|
||||
if (!isSmallWidth()) {
|
||||
logoAndTitlePanel.add(globalNavigationPanel);
|
||||
}
|
||||
mainPanel.addNorth(logoAndTitlePanel, 68);
|
||||
SAPSailingHeaderWithAuthentication header = new SAPSailingHeaderWithAuthentication(leaderboardDisplayName);
|
||||
new FixedSailingAuthentication(getUserService(), header.getAuthenticationMenuView());
|
||||
mainPanel.addNorth(header, 75);
|
||||
}
|
||||
ScrollPanel contentScrollPanel = new ScrollPanel();
|
||||
long delayBetweenAutoAdvancesInMilliseconds = DEFAULT_REFRESH_INTERVAL_MILLIS;
|
||||
|
||||
+40
-44
@@ -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.Handler;
|
||||
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.LeaderboardNameConstants;
|
||||
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.LeaderboardSettings.RaceColumnSelectionStrategies;
|
||||
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.Pair;
|
||||
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(),
|
||||
LEG_COLUMN_HEADER_STYLE, LEG_COLUMN_STYLE, LeaderboardPanel.this));
|
||||
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));
|
||||
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,
|
||||
new FormattedDoubleDetailTypeColumn(DetailType.RACE_DISTANCE_TO_COMPETITOR_FARTHEST_AHEAD_IN_METERS, new RaceDistanceToCompetitorFarthestAheadInMeters(),
|
||||
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> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
Double result = null;
|
||||
LeaderboardEntryDTO fieldsForRace = row.fieldsByRaceColumnName.get(getRaceColumnName());
|
||||
if (fieldsForRace != null && fieldsForRace.legDetails != null) {
|
||||
double distanceTraveledInMeters = 0;
|
||||
long timeInMilliseconds = 0;
|
||||
for (LegEntryDTO legDetail : fieldsForRace.legDetails) {
|
||||
if (legDetail != null) {
|
||||
if (legDetail.distanceTraveledInMeters != null && legDetail.timeInMilliseconds != null) {
|
||||
distanceTraveledInMeters += legDetail.distanceTraveledInMeters;
|
||||
timeInMilliseconds += legDetail.timeInMilliseconds;
|
||||
final Distance distanceTraveledInMeters = row.getDistanceTraveledInMeters(getRaceColumnName());
|
||||
final Duration time = row.getTimeSailed(getRaceColumnName());
|
||||
final Double result;
|
||||
if (distanceTraveledInMeters != null && time != null) {
|
||||
result = distanceTraveledInMeters.inTime(time).getKnots();
|
||||
} else {
|
||||
distanceTraveledInMeters = 0;
|
||||
timeInMilliseconds = 0;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (timeInMilliseconds != 0) {
|
||||
result = distanceTraveledInMeters / (double) timeInMilliseconds * 1000 * 3600 / 1852;
|
||||
}
|
||||
result = null;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
@@ -1299,21 +1288,7 @@ public class LeaderboardPanel extends SimplePanel implements Component<Leaderboa
|
||||
private class RaceDistanceTraveledInMeters implements LegDetailField<Double> {
|
||||
@Override
|
||||
public Double get(LeaderboardRowDTO row) {
|
||||
Double result = null;
|
||||
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;
|
||||
return row.getDistanceTraveledInMeters(getRaceColumnName()).getMeters();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1446,27 +1421,48 @@ public class LeaderboardPanel extends SimplePanel implements Component<Leaderboa
|
||||
lastLegDetail = fieldsForRace.legDetails.get(--lastLegIndex);
|
||||
}
|
||||
if (lastLegDetail != null) {
|
||||
result = get(lastLegDetail);
|
||||
if (lastLegDetail.finished) {
|
||||
result = getAfterLastLegFinished(row);
|
||||
} else {
|
||||
result = getBeforeLastLegFinished(lastLegDetail);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
protected Double get(LegEntryDTO lastLegDetail) {
|
||||
return lastLegDetail.currentSpeedOverGroundInKnots;
|
||||
protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) {
|
||||
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
|
||||
protected Double get(LegEntryDTO lastLegDetail) {
|
||||
return lastLegDetail.currentRideHeightInMeters;
|
||||
protected Double getBeforeLastLegFinished(LegEntryDTO currentLegDetail) {
|
||||
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;
|
||||
margin: 0 0 0 0;
|
||||
padding: 2px 7px 2px 3px;
|
||||
height: 18px;
|
||||
height: 22px;
|
||||
display: inline-block;
|
||||
text-decoration: none;
|
||||
-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
|
||||
.<BravoFix, BravoFixTrack<Competitor>> getSensorTrack(competitor, BravoFixTrack.TRACK_NAME);
|
||||
if (bravoFixTrack != null) {
|
||||
result = bravoFixTrack.getRideHeight(timePoint);
|
||||
final Distance rideHeight = bravoFixTrack.getRideHeight(timePoint);
|
||||
result = rideHeight == null ? null : rideHeight.getMeters();
|
||||
}
|
||||
break;
|
||||
default:
|
||||
|
||||
+6
@@ -4,6 +4,12 @@
|
||||
<div id="mainContent">
|
||||
<h4 class="articleHeadline">What's New - SAP Sailing Race Manager</h4>
|
||||
<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>
|
||||
|
||||
<ul class="bulletList">
|
||||
|
||||
+1
@@ -37,4 +37,5 @@
|
||||
<inherits name="com.sap.sailing.gwt.ui.SourceAdminConsole" />
|
||||
|
||||
<inherits name='com.sap.sse.gwt.CommonControlCSS_Autoinject'/>
|
||||
<inherits name='com.sap.sailing.gwt.common.AuthenticationCommon'/>
|
||||
</module>
|
||||
+8
-1
@@ -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.PolarDataService;
|
||||
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.polars.PolarDataOperation;
|
||||
import com.sap.sailing.polars.mining.BearingClusterGroup;
|
||||
@@ -236,9 +237,15 @@ public class PolarDataServiceImpl implements PolarDataService,
|
||||
@Override
|
||||
public void insertExistingFixes(TrackedRace trackedRace) {
|
||||
for (Competitor competitor : trackedRace.getRace().getCompetitors()) {
|
||||
for (GPSFixMoving fix : trackedRace.getTrack(competitor).getFixes()) {
|
||||
GPSFixTrack<Competitor, GPSFixMoving> track = trackedRace.getTrack(competitor);
|
||||
track.lockForRead();
|
||||
try {
|
||||
for (GPSFixMoving fix : track.getFixes()) {
|
||||
competitorPositionChanged(fix, competitor, trackedRace);
|
||||
}
|
||||
} finally {
|
||||
track.unlockAfterRead();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+7
-6
@@ -9,8 +9,6 @@ import java.util.logging.Logger;
|
||||
|
||||
import javax.servlet.http.HttpServletRequest;
|
||||
import javax.servlet.http.HttpServletResponse;
|
||||
import javax.ws.rs.WebApplicationException;
|
||||
import javax.ws.rs.core.Response;
|
||||
import javax.ws.rs.core.Response.Status;
|
||||
|
||||
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)
|
||||
*/
|
||||
private static final int MAX_SIZE_IN_MB = 5;
|
||||
private static final int MAX_SIZE_IN_MB = 500;
|
||||
|
||||
@Override
|
||||
protected void process(List<FileItem> fileItems, HttpServletRequest req, HttpServletResponse resp) throws UnsupportedEncodingException, IOException {
|
||||
@@ -61,13 +59,16 @@ public class FileUploadServlet extends AbstractFileUploadServlet {
|
||||
}
|
||||
try {
|
||||
if (fileItem.getSize() > 1024 * 1024 * MAX_SIZE_IN_MB) {
|
||||
throw new WebApplicationException(Response.status(Status.BAD_REQUEST)
|
||||
.entity("Image is larger than " + MAX_SIZE_IN_MB + "MB").build());
|
||||
}
|
||||
final String errorMessage = "Image is larger than " + MAX_SIZE_IN_MB + "MB";
|
||||
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());
|
||||
}
|
||||
} catch (IOException | OperationFailedException | InvalidPropertiesException | NoCorrespondingServiceRegisteredException e) {
|
||||
final String errorMessage = "Could not store file"+ (e.getMessage()==null?"":(": " + e.getMessage()));
|
||||
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 int callbackCallCount = 0;
|
||||
private double sumRideHeight = 0.0;
|
||||
private double sumRideHeightInMeters = 0.0;
|
||||
|
||||
@Before
|
||||
public void setUp() {
|
||||
this.callbackCallCount = 0;
|
||||
this.sumRideHeight = 0.0;
|
||||
this.sumRideHeightInMeters = 0.0;
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -51,12 +51,12 @@ public class BravoDataImportTest {
|
||||
bravoDataImporter.importFixes(importData.getInputStream(), (fixes, device) -> {
|
||||
for (DoubleVectorFix fix : fixes) {
|
||||
callbackCallCount++;
|
||||
sumRideHeight += new BravoFixImpl(fix).getRideHeight();
|
||||
sumRideHeightInMeters += new BravoFixImpl(fix).getRideHeight().getMeters();
|
||||
}
|
||||
}, "filename", "source");
|
||||
Assert.assertEquals(importData.expectedFixesCount, callbackCallCount);
|
||||
Assert.assertEquals(importData.expectedAverageRideHeight,
|
||||
callbackCallCount == 0 ? sumRideHeight : sumRideHeight / callbackCallCount, 0.00001);
|
||||
callbackCallCount == 0 ? sumRideHeightInMeters : sumRideHeightInMeters / callbackCallCount, 0.00001);
|
||||
}
|
||||
|
||||
private enum ImportData {
|
||||
|
||||
+2
@@ -3484,6 +3484,8 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes
|
||||
trackedRace.setPolarDataService(service);
|
||||
service.insertExistingFixes(trackedRace);
|
||||
}
|
||||
} catch (Throwable e) {
|
||||
logger.log(Level.SEVERE, "Error reconstructing the polars for tracked races", e);
|
||||
} finally {
|
||||
trackedRegatta.unlockTrackedRacesAfterRead();
|
||||
}
|
||||
|
||||
+26
-15
@@ -67,18 +67,22 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
private static final long serialVersionUID = -11522605089325440L;
|
||||
private Regatta regatta;
|
||||
|
||||
public DummyTrackedRace(final Serializable raceId, final Iterable<? extends Competitor> competitors, final Regatta regatta, final TrackedRegatta trackedRegatta, final WindStore windStore) {
|
||||
this(competitors, regatta, trackedRegatta, new RaceDefinitionImpl("DummyRace", new CourseImpl("Dummy Course", Collections.<Waypoint>emptyList()),
|
||||
regatta.getBoatClass(), competitors, raceId), windStore);
|
||||
public DummyTrackedRace(final Serializable raceId, final Iterable<? extends Competitor> competitors,
|
||||
final Regatta regatta, final TrackedRegatta trackedRegatta, final WindStore 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,
|
||||
RaceDefinition race, WindStore windStore) {
|
||||
public DummyTrackedRace(final Iterable<? extends Competitor> competitors, final Regatta regatta,
|
||||
final TrackedRegatta trackedRegatta, RaceDefinition race, WindStore windStore) {
|
||||
super(race, trackedRegatta, windStore, -1);
|
||||
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,
|
||||
final TrackedRegatta trackedRegatta, RaceDefinition race) {
|
||||
this(competitors, regatta, trackedRegatta, race, EmptyWindStore.INSTANCE);
|
||||
|
||||
}
|
||||
@@ -214,7 +218,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint, MarkPositionAtTimePointCache markPositionCache) {
|
||||
public Position getApproximatePosition(Waypoint waypoint, TimePoint timePoint,
|
||||
MarkPositionAtTimePointCache markPositionCache) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -355,7 +360,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint, WindPositionMode windPositionMode) {
|
||||
public Distance getWindwardDistanceToCompetitorFarthestAhead(Competitor competitor, TimePoint timePoint,
|
||||
WindPositionMode windPositionMode) {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -386,8 +392,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint, boolean waitForLatestAnalysis)
|
||||
throws NoWindException {
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint timePoint,
|
||||
boolean waitForLatestAnalysis) throws NoWindException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -431,8 +437,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to, boolean upwindOnly,
|
||||
boolean waitForLatestAnalyses) throws NoWindException {
|
||||
public Distance getAverageAbsoluteCrossTrackError(Competitor competitor, TimePoint from, TimePoint to,
|
||||
boolean upwindOnly, boolean waitForLatestAnalyses) throws NoWindException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -598,8 +604,8 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
}
|
||||
|
||||
@Override
|
||||
public Duration getEstimatedTimeToComplete(TimePoint timepoint) throws NotEnoughDataHasBeenAddedException,
|
||||
NoWindException {
|
||||
public Duration getEstimatedTimeToComplete(TimePoint timepoint)
|
||||
throws NotEnoughDataHasBeenAddedException, NoWindException {
|
||||
return null;
|
||||
}
|
||||
|
||||
@@ -661,4 +667,9 @@ public class DummyTrackedRace extends TrackedRaceWithWindEssentials {
|
||||
public NavigableSet<MarkPassing> getUpdatedMarkPassings(Competitor competitor) {
|
||||
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
|
||||
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>
|
||||
<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>
|
||||
|
||||
<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
|
||||
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 {
|
||||
result = aBlock.compareTo(bBlock);
|
||||
}
|
||||
|
||||
@@ -460,20 +460,20 @@
|
||||
<string name="not_available">N/A</string>
|
||||
<string name="accuracy_timestamp">(vor %s)</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="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_2">Zurücksetzen</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="onboarding_scan">Scan QR-Code</string>
|
||||
<string name="onboarding_scan">QR-Code scannen</string>
|
||||
<string name="onboarding_input">Manuelle Eingabe</string>
|
||||
<string name="race_group">Group Race</string>
|
||||
<string name="wind_select_race">Select Race</string>
|
||||
<string name="race_group">Gruppenwettfahrt</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="get_started">Getting Started</string>
|
||||
<string name="get_started">Anmelden</string>
|
||||
<string name="position">Position</string>
|
||||
<string name="server_deeplink_message">Server erfolgreich geändert auf %1$s.</string>
|
||||
<string name="penalty">Strafe</string>
|
||||
|
||||
Reference in new issue
Block a user