> candidateDeltas = finder.getCandidateDeltas(c, fixes);
- logger.finer("Received " + Util.size(candidateDeltas.getA()) + " new Candidates and will remove "
+ logger.finer("Received " + Util.size(candidateDeltas.getA()) + " new Candidates for race "+raceName+
+ " and competitor "+c+" and will remove "
+ Util.size(candidateDeltas.getB()) + " old Candidates for " + c);
chooser.calculateMarkPassDeltas(c, candidateDeltas.getA(), candidateDeltas.getB());
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java
index 093f167be45..3b2435361bf 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/CandidateChooserImpl.java
@@ -20,6 +20,7 @@ import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.impl.MeterDistance;
+import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.markpassingcalculation.Candidate;
import com.sap.sailing.domain.markpassingcalculation.CandidateChooser;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
@@ -46,12 +47,24 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
*
*/
public class CandidateChooserImpl implements CandidateChooser {
+ /**
+ * Earlier finish mark passings are to be preferred over later ones if they otherwise seem equally likely. While the
+ * {@link #getProbabilityOfActualDistanceGivenGreatCircleDistance(Distance, Distance, double)} method should usually
+ * assign an equal probability of 1.0 for edges whose distance is in the range of 1.0 and
+ * {@link #MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH} times the leg length, for the finishing
+ * leg the candidates that require more distance than the minimum distance required receive an increasing penalty.
+ * The maximum penalty for finishing candidates that have required
+ * {@link #MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH} times the leg distance is expressed by
+ * this consttant.
+ */
+ private static final double PENALTY_FOR_LATEST_FINISH_PASSING = 0.95;
+
/**
* Distance ratios of actual distance traveled and leg length above this threshold will receive
* penalties on their probability. Ratios below 1.0 receive the ratio as the penalty.
* See {@link #getDistanceEstimationBasedProbability(Competitor, Candidate, Candidate)}.
*/
- final double MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH = 2.0;
+ private static final double MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH = 2.0;
/**
* Start mark passings will be considered this much before the actual race start. The race start
@@ -67,7 +80,7 @@ public class CandidateChooserImpl implements CandidateChooser {
*/
private static final Duration DELAY_AFTER_WHICH_PROBABILITY_OF_START_HALVES = Duration.ONE_MINUTE;
- private final static double MINIMUM_PROBABILITY = Edge.getPenaltyForSkipping();
+ private static final double MINIMUM_PROBABILITY = Edge.getPenaltyForSkipping();
private static final Logger logger = Logger.getLogger(CandidateChooserImpl.class.getName());
@@ -308,7 +321,7 @@ public class CandidateChooserImpl implements CandidateChooser {
edgeSet = new HashSet<>();
edges.put(e.getStart(), edgeSet);
}
- edgeSet.add(e);
+ edgeSet.add(e); // FIXME what about edges that should replace an edge between the same two candidates? Will those edges somehow be removed?
}
/**
@@ -437,9 +450,16 @@ public class CandidateChooserImpl implements CandidateChooser {
first = c1.getWaypoint();
}
final Waypoint second = c2.getWaypoint();
- final Distance totalGreatCircleDistance = getApproximateTotalGreatCircleDistanceBetweenWaypoints(first, second, middleOfc1Andc2);
- final Distance actualDistanceTraveled = race.getTrack(c).getDistanceTraveled(c1.getTimePoint(), c2.getTimePoint());
- result = getProbabilityOfActualDistanceGivenGreatCircleDistance(totalGreatCircleDistance, actualDistanceTraveled);
+ final Distance totalGreatCircleDistance = getMinimumTotalGreatCircleDistanceBetweenWaypoints(first, second, middleOfc1Andc2);
+ if (totalGreatCircleDistance == null) {
+ result = 0; // no distance known; cannot tell; low probability
+ } else {
+ final Distance actualDistanceTraveled = race.getTrack(c).getDistanceTraveled(c1.getTimePoint(), c2.getTimePoint());
+ final double probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength =
+ c2.getWaypoint() == race.getRace().getCourse().getLastWaypoint() ? PENALTY_FOR_LATEST_FINISH_PASSING : 1.0;
+ result = getProbabilityOfActualDistanceGivenGreatCircleDistance(totalGreatCircleDistance, actualDistanceTraveled,
+ probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength);
+ }
return result;
}
@@ -450,31 +470,44 @@ public class CandidateChooserImpl implements CandidateChooser {
* mark on a great circle segment, distances sailed will exceed the great line circle distances.
*
*
- * A smaller distance than great circle from mark to mark is very unlikely, somewhere between the distance estimated
- * and double that is likely and anything greater than that gradually becomes unlikely.
+ * A smaller distance than great circle from mark to mark is getting the more unlikely the shorter the distance is,
+ * somewhere between the distance estimated and twice that is likely, and anything greater than that gradually
+ * becomes unlikely.
+ *
+ *
+ * Finishing legs are a special case. Here, we'd like to prefer an earlier candidate over a later one as long as the
+ * earlier one still leads to a "reasonable" distance sailed, particularly if two such candidates are otherwise
+ * equally highly likely. Therefore, this method accepts a parameter
+ * {@code probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength} that configures a slight "slope" in
+ * the interval that for non-finishing legs receives a constant probability of 1.0. This slope will give 1.0 for the
+ * shortest possible distance and slightly less for the longest distance that for non-finishing legs would still
+ * result in 1.0. The probabilities of even greater distances then starts contiguously at the end value of that
+ * slope.
*
* @return a number between 0 and 1 with 1 representing a "fair chance" that the actual distance sailed could have
* been sailed for the given great circle distance; 1 is returned for actual distances being in the range of
* 1..2 times the great circle distance. Actual distances outside this interval reduce probability linearly
- * for smaller distances (gradient 0.5) and varies with the one over the ratio for distances that exceed twice the
- * great circle distance.
+ * for smaller distances (gradient 0.5) and varies with the one over the ratio for distances that exceed
+ * twice the great circle distance.
*/
- private double getProbabilityOfActualDistanceGivenGreatCircleDistance(Distance totalGreatCircleDistance,
- Distance actualDistanceTraveled) {
+ private double getProbabilityOfActualDistanceGivenGreatCircleDistance(Distance totalGreatCircleDistance, Distance actualDistanceTraveled,
+ double probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength) {
final double result;
final double ratio = actualDistanceTraveled.getMeters() / totalGreatCircleDistance.getMeters();
// A smaller distance than great circle from mark to mark is very unlikely, somewhere between the distance
// estimated and double that is likely and anything greater than that gradually becomes unlikely
- if (ratio <= MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH) {
- result = Math.min(1.0, ratio); // maximum probability is 1; use for the interval between 1 and MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH
+ if (ratio <= 1) {
+ result = ratio;
+ } else if (ratio <= MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH) {
+ result = 1 - (1-probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength)*(ratio-1)/(MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH-1);
} else {
- result = 1./(ratio-1.); // start at probability 1 for ratio==2
+ // start at probability probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength for ratio==MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH
+ result = probabilityForMaxReasonableRatioBetweenDistanceTraveledAndLegLength/(ratio-MAX_REASONABLE_RATIO_BETWEEN_DISTANCE_TRAVELED_AND_LEG_LENGTH + 1.);
}
return result;
}
- private Distance getApproximateTotalGreatCircleDistanceBetweenWaypoints(Waypoint first, final Waypoint second,
- final TimePoint timePoint) {
+ private Distance getMinimumTotalGreatCircleDistanceBetweenWaypoints(Waypoint first, final Waypoint second, final TimePoint timePoint) {
Distance totalGreatCircleDistance = new MeterDistance(0);
boolean legsAreBetweenCandidates = false;
for (TrackedLeg leg : race.getTrackedLegs()) {
@@ -486,8 +519,15 @@ public class CandidateChooserImpl implements CandidateChooser {
legsAreBetweenCandidates = true;
}
if (legsAreBetweenCandidates) {
- totalGreatCircleDistance = totalGreatCircleDistance.add(
- waypointPositionAndDistanceCache.getApproximateDistance(from, leg.getLeg().getTo(), timePoint));
+ final Distance minimumDistanceToNextWaypoint = waypointPositionAndDistanceCache.getMinimumDistance(from, leg.getLeg().getTo(), timePoint);
+ if (minimumDistanceToNextWaypoint == null) {
+ totalGreatCircleDistance = null;
+ break;
+ } else {
+ // subtract twice the typical error margin of the position fixes of the marks, assuming that the leg could have been
+ // a little shorter in fact:
+ totalGreatCircleDistance = totalGreatCircleDistance.add(minimumDistanceToNextWaypoint).add(GPSFix.TYPICAL_HDOP.scale(-2));
+ }
}
}
return totalGreatCircleDistance;
@@ -502,14 +542,16 @@ public class CandidateChooserImpl implements CandidateChooser {
private void removeCandidates(Competitor c, Iterable wrongCandidates) {
for (Candidate can : wrongCandidates) {
- logger.finest("Removing all edges containing " + can + "of "+ c);
+ if (logger.isLoggable(Level.FINEST)) {
+ logger.finest("Removing all edges containing " + can + "of "+ c);
+ }
candidates.get(c).remove(can);
Map> edges = allEdges.get(c);
edges.remove(can);
for (Set set : edges.values()) {
for (Iterator i = set.iterator(); i.hasNext();) {
final Edge e = i.next();
- if (e.getStart().equals(can) || e.getEnd().equals(can)) {
+ if (e.getStart() == can || e.getEnd() == can) {
i.remove();
}
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/WaypointPositionAndDistanceCache.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/WaypointPositionAndDistanceCache.java
index cf4d3fad43b..5260fbce609 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/WaypointPositionAndDistanceCache.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/markpassingcalculation/impl/WaypointPositionAndDistanceCache.java
@@ -1,6 +1,7 @@
package com.sap.sailing.domain.markpassingcalculation.impl;
import java.util.ArrayList;
+import java.util.Arrays;
import java.util.Collections;
import java.util.Iterator;
import java.util.List;
@@ -57,13 +58,30 @@ public class WaypointPositionAndDistanceCache {
private final ConcurrentWeakHashMap> waypointPositionCache;
/**
- * Caches distances between the positions cached in {@link #waypointPositionCache}. As soon as an entry
+ * Weak keys are the marks; values are time-ordered maps mapping the center of a time range
+ * to the mark position at that time.
+ */
+ private final ConcurrentWeakHashMap> markPositionCache;
+
+ /**
+ * Caches distances between the approximate positions cached in {@link #waypointPositionCache}. For two-mark
+ * control points those are the positions in the middle between the two marks. As soon as an entry
* is invalidated in {@link #waypointPositionCache}, it is also invalidated here. Keys always occur
* symmetrically under the map's monitor being held, i.e., if (w1, w2) is a cache key then so is
* (w2, w1), with equal value.
*/
private final ConcurrentHashMap, SortedMap> distanceCache;
+ /**
+ * Caches minimum distances between the positions cached in {@link #waypointPositionCache}. For two-mark control
+ * points those distances are the shortest distance between any two mark pairs from the two waypoints. Note that
+ * this is not the absolute minimum distance to be sailed, e.g., if the two waypoints are lines, where their closest
+ * approximation point may lie somewhere on the middle of at least one line. As soon as an entry is invalidated in
+ * {@link #waypointPositionCache}, it is also invalidated here. Keys always occur symmetrically under the map's
+ * monitor being held, i.e., if (w1, w2) is a cache key then so is (w2, w1), with equal value.
+ */
+ private final ConcurrentHashMap, SortedMap> minimumDistanceCache;
+
/**
* The duration of the time ranges whose center time points serve as keys for the {@link NavigableMap}s used as
* values in the {@link #waypointPositionCache} structure. Each cache entry extends half this duration into the past and into the
@@ -84,7 +102,9 @@ public class WaypointPositionAndDistanceCache {
public WaypointPositionAndDistanceCache(TrackedRace race, Duration timeRangeResolution) {
this.trackedRace = race;
this.waypointPositionCache = new ConcurrentWeakHashMap<>();
+ this.markPositionCache = new ConcurrentWeakHashMap<>();
this.distanceCache = new ConcurrentHashMap<>();
+ this.minimumDistanceCache = new ConcurrentHashMap<>();
this.timeRangeResolution = timeRangeResolution;
this.waypoints = Collections.synchronizedList(new ArrayList<>());
final Course course = this.trackedRace.getRace().getCourse();
@@ -119,6 +139,12 @@ public class WaypointPositionAndDistanceCache {
/* alternateKeySupplier */ null);
}
+ public Position getApproximatePosition(Mark mark, TimePoint timePoint) {
+ return getApproximateResult(mark, timePoint, markPositionCache,
+ roundedToTimeRangeCenter->trackedRace.getOrCreateTrack(mark).getEstimatedPosition(roundedToTimeRangeCenter, /* extrapolate */ false),
+ /* alternateKeySupplier */ null);
+ }
+
/**
* @param resultCalculator
* function that computes the result to be cached, assuming the caller knows the key which it just passed
@@ -174,6 +200,27 @@ public class WaypointPositionAndDistanceCache {
/* alternate key supplier */ ()->new Pair<>(w2.getControlPoint(), w1.getControlPoint()));
}
+ public Distance getMinimumDistance(Waypoint w1, Waypoint w2, TimePoint timePoint) {
+ return getApproximateResult(new Pair<>(w1.getControlPoint(), w2.getControlPoint()), timePoint, minimumDistanceCache,
+ roundedToTimeRangeCenter->{
+ Distance minDist = null;
+ for (final Mark w1m : w1.getMarks()) {
+ for (final Mark w2m : w2.getMarks()) {
+ final Position approximatePositionW1m = getApproximatePosition(w1m, roundedToTimeRangeCenter);
+ final Position approximatePositionW2m = getApproximatePosition(w2m, roundedToTimeRangeCenter);
+ if (approximatePositionW1m != null && approximatePositionW2m != null) {
+ final Distance w1mToW2m = approximatePositionW1m.getDistance(approximatePositionW2m);
+ if (minDist == null || minDist.compareTo(w1mToW2m) > 0) {
+ minDist = w1mToW2m;
+ }
+ }
+ }
+ }
+ return minDist;
+ },
+ /* alternate key supplier */ ()->new Pair<>(w2.getControlPoint(), w1.getControlPoint()));
+ }
+
/**
* Invalidates those elements of the cache affected by the position fix for the mark. This considers for which time
* range the {@link GPSFixTrack#getEstimatedPosition(TimePoint, boolean)} result will be affected by the fix and checks
@@ -181,6 +228,10 @@ public class WaypointPositionAndDistanceCache {
*/
private void invalidate(Mark mark, GPSFix fix) {
TimeRange affectedTimeRange = trackedRace.getOrCreateTrack(mark).getEstimatedPositionTimePeriodAffectedBy(fix);
+ final SortedMap map = markPositionCache.get(mark);
+ if (map != null) {
+ removeAffectedEntries(mark, affectedTimeRange, map, /* entryRemovedCallback */ null);
+ }
synchronized (waypoints) { // it's a synchronized list, so grabbing the list's monitor will grant us exclusive access here
for (Waypoint waypoint : waypoints) {
if (Util.contains(waypoint.getMarks(), mark)) {
@@ -190,34 +241,50 @@ public class WaypointPositionAndDistanceCache {
}
}
- private void invalidate(ControlPoint controlPoint, TimeRange affectedTimeRange) {
- SortedMap map = waypointPositionCache.get(controlPoint);
+ private void invalidate(final ControlPoint controlPoint, TimeRange affectedTimeRange) {
+ final SortedMap map = waypointPositionCache.get(controlPoint);
if (map != null) {
- final SortedMap tailMap = map.tailMap(roundToResolution(affectedTimeRange.from()));
- synchronized (map) {
- for (Iterator> i=tailMap.entrySet().iterator(); i.hasNext(); ) {
- final Entry e = i.next();
- final TimePoint timePoint = e.getKey();
- if (timePoint.after(affectedTimeRange.to())) {
- break;
- }
- assert timePoint.equals(roundToResolution(timePoint));
- i.remove();
- synchronized (waypoints) {
- for (Waypoint otherWaypoint : waypoints) {
- ControlPoint otherControlPoint = otherWaypoint.getControlPoint();
- if (otherControlPoint != controlPoint) {
- final Map distanceMap = distanceCache.get(new Pair<>(controlPoint,
- otherControlPoint));
- if (distanceMap != null) {
- distanceMap.remove(timePoint);
- }
- final Map otherDistanceMap = distanceCache.get(new Pair<>(
- otherControlPoint, controlPoint));
- if (otherDistanceMap != null) {
- otherDistanceMap.remove(timePoint);
- }
- }
+ removeAffectedEntries(controlPoint, affectedTimeRange, map, this::controlPointCacheMapEntryRemoved);
+ }
+ }
+
+ private void removeAffectedEntries(T controlPoint, TimeRange affectedTimeRange,
+ SortedMap map, EntryRemovedCallback entryRemovedCallback) {
+ final SortedMap tailMap = map.tailMap(roundToResolution(affectedTimeRange.from()));
+ synchronized (map) {
+ for (Iterator> i=tailMap.entrySet().iterator(); i.hasNext(); ) {
+ final Entry e = i.next();
+ final TimePoint timePoint = e.getKey();
+ if (timePoint.after(affectedTimeRange.to())) {
+ break;
+ }
+ assert timePoint.equals(roundToResolution(timePoint));
+ i.remove();
+ if (entryRemovedCallback != null) {
+ entryRemovedCallback.cacheMapEntryRemoved(controlPoint, timePoint);
+ }
+ }
+ }
+ }
+
+ @FunctionalInterface
+ private interface EntryRemovedCallback {
+ void cacheMapEntryRemoved(T controlPoint, final TimePoint timePoint);
+ }
+
+ private void controlPointCacheMapEntryRemoved(ControlPoint controlPoint, final TimePoint timePoint) {
+ synchronized (waypoints) {
+ for (Waypoint otherWaypoint : waypoints) {
+ ControlPoint otherControlPoint = otherWaypoint.getControlPoint();
+ if (otherControlPoint != controlPoint) {
+ for (ConcurrentHashMap, SortedMap> cache : Arrays.asList(distanceCache, minimumDistanceCache)) {
+ final Map distanceMap = cache.get(new Pair<>(controlPoint, otherControlPoint));
+ if (distanceMap != null) {
+ distanceMap.remove(timePoint);
+ }
+ final Map otherDistanceMap = cache.get(new Pair<>(otherControlPoint, controlPoint));
+ if (otherDistanceMap != null) {
+ otherDistanceMap.remove(timePoint);
}
}
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java
index 3b30d768dbc..5514307549f 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/AbstractRankingMetric.java
@@ -475,10 +475,12 @@ public abstract class AbstractRankingMetric implements RankingMetric {
TrackedLegOfCompetitor currentLegWho = getTrackedRace().getCurrentLeg(who, timePoint);
if (currentLegWho == null) { // already finished or not yet started; if already finished, use last leg
final Waypoint lastWaypoint = getTrackedRace().getRace().getCourse().getLastWaypoint();
- final TrackedLeg lastTrackedLeg = getTrackedRace().getTrackedLegFinishingAt(lastWaypoint);
- TrackedLegOfCompetitor whosLastTrackedLeg = lastTrackedLeg.getTrackedLeg(who);
- if (whosLastTrackedLeg.hasFinishedLeg(timePoint)) {
- currentLegWho = whosLastTrackedLeg;
+ if (lastWaypoint != null) { // could be an empty course
+ final TrackedLeg lastTrackedLeg = getTrackedRace().getTrackedLegFinishingAt(lastWaypoint);
+ TrackedLegOfCompetitor whosLastTrackedLeg = lastTrackedLeg.getTrackedLeg(who);
+ if (whosLastTrackedLeg.hasFinishedLeg(timePoint)) {
+ currentLegWho = whosLastTrackedLeg;
+ }
}
}
return currentLegWho;
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java
index c5015b70cf9..019c61b47b1 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/ranking/OneDesignRankingMetric.java
@@ -88,12 +88,16 @@ public class OneDesignRankingMetric extends AbstractRankingMetric {
} else {
// after passing the next mark, who still needs to travel about the same time that to
// traveled after passing the next mark until finishing the race
- timeToTookToFinishRaceStartingAtNextMark =
- getTrackedRace().getMarkPassing(to, whosNextMark).getTimePoint().
- until(tosLegFinishingTime);
+ final MarkPassing tosMarkPassingForWhosNextMark = getTrackedRace().getMarkPassing(to, whosNextMark);
+ timeToTookToFinishRaceStartingAtNextMark = tosMarkPassingForWhosNextMark == null ? null :
+ tosMarkPassingForWhosNextMark.getTimePoint().until(tosLegFinishingTime);
+ }
+ if (timeToTookToFinishRaceStartingAtNextMark == null) {
+ result = null;
+ } else {
+ final TimePoint etaAtFinish = rankingInfo.getTimePoint().plus(etaNextMark.plus(timeToTookToFinishRaceStartingAtNextMark));
+ result = tosLegFinishingTime.until(etaAtFinish);
}
- final TimePoint etaAtFinish = rankingInfo.getTimePoint().plus(etaNextMark.plus(timeToTookToFinishRaceStartingAtNextMark));
- result = tosLegFinishingTime.until(etaAtFinish);
}
}
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/HasRegattaLike.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/HasRegattaLike.java
index deb887970fb..9a73f72b3d6 100644
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/HasRegattaLike.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/HasRegattaLike.java
@@ -26,7 +26,8 @@ public interface HasRegattaLike {
/**
* Determines the combined set of all competitors from all race columns that this object's {@link IsRegattaLike}
- * has.
+ * has, as well as those coming from registrations on the {@link #getRegattaLike()}'s
+ * {@link IsRegattaLike#getRegattaLog() regatta log}.
*
* @see #getRegattaLike()
* @see IsRegattaLike#getRaceColumnByName(String)
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRace.java
index 3c4177fce24..49f07eb9da6 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRace.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/DynamicTrackedRace.java
@@ -14,10 +14,39 @@ import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sse.common.TimePoint;
public interface DynamicTrackedRace extends TrackedRace {
- void recordFix(Competitor competitor, GPSFixMoving fix);
+ /**
+ * Records a position and speed and course over ground fix for a competitor, but only if the fix's {@link GPSFixMoving#getTimePoint()}
+ * is within this race's {@link #getStartOfTracking() start} and {@link #getEndOfTracking() end} of tracking time interval.
+ */
+ default void recordFix(Competitor competitor, GPSFixMoving fix) {
+ recordFix(competitor, fix, /* onlyWhenInTrackingTimeInterval */ true);
+ }
+
+ /**
+ * Records a position and speed and course over ground fix for a competitor. If
+ * {@code onlyWhenInTrackingTimeInterval} is {@code true}, the fix is recorded only if the fix's
+ * {@link GPSFixMoving#getTimePoint()} is within this race's {@link #getStartOfTracking() start} and
+ * {@link #getEndOfTracking() end} of tracking time interval. If {@code onlyWhenInTrackingTimeInterval} is
+ * {@code false}, the fix is recorded regardless of this race's tracking times interval.
+ */
+ void recordFix(Competitor competitor, GPSFixMoving fix, boolean onlyWhenInTrackingTimeInterval);
- void recordFix(Mark mark, GPSFix fix);
+ /**
+ * Records a position fix for a mark, but only if the fix's {@link GPSFixMoving#getTimePoint()} is within this
+ * race's {@link #getStartOfTracking() start} and {@link #getEndOfTracking() end} of tracking time interval.
+ */
+ default void recordFix(Mark mark, GPSFix fix) {
+ recordFix(mark, fix, /* onlyWhenInTrackingTimeInterval */ true);
+ }
+ /**
+ * Records a position fix for a mark. If {@code onlyWhenInTrackingTimeInterval} is {@code true}, the fix is recorded
+ * only if the fix's {@link GPSFixMoving#getTimePoint()} is within this race's {@link #getStartOfTracking() start}
+ * and {@link #getEndOfTracking() end} of tracking time interval. If {@code onlyWhenInTrackingTimeInterval} is
+ * {@code false}, the fix is recorded regardless of this race's tracking times interval.
+ */
+ void recordFix(Mark mark, GPSFix fix, boolean onlyWhenInTrackingTimeInterval);
+
/**
* Inserts a wind fix into a {@link WindTrack} for the windSource if the current filtering
* rules accept the wind fix. Filtering applies based upon timing considerations, assuming that wind fixes are not
@@ -128,10 +157,12 @@ public interface DynamicTrackedRace extends TrackedRace {
void onCourseDesignChangedByRaceCommittee(CourseBase courseDesign);
void onStartTimeChangedByRaceCommittee(TimePoint newStartTime);
+ void onFinishedTimeChangedByRaceCommittee(TimePoint newFinishedTime);
void onAbortedByRaceCommittee(Flags flag);
void invalidateStartTime();
void invalidateEndTime();
+
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java
index a9ec7959111..77cd1df350c 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/TrackedRace.java
@@ -36,6 +36,7 @@ import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
+import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog;
import com.sap.sailing.domain.common.dto.TrackedRaceDTO;
import com.sap.sailing.domain.common.racelog.Flags;
import com.sap.sailing.domain.common.racelog.RacingProcedureType;
@@ -870,7 +871,11 @@ public interface TrackedRace extends Serializable, IsManagedByCache getTrackingTimesFromRaceLogs() {
+ default Pair getTrackingTimesFromRaceLogs() {
+ return null;
+ }
+
+ default Pair getStartAndFinishedTimeFromRaceLogs() {
return null;
}
@@ -883,4 +888,15 @@ public interface TrackedRace extends Serializable, IsManagedByCache getMarksFromRegattaLogs() {
return getMarks();
}
+
+ /**
+ * Updates the start and end of tracking in the following precedence order:
+ *
+ *
+ * - start/end of tracking in Racelog
+ * - manually set start/end of tracking via {@link #setStartOfTrackingReceived(TimePoint, boolean)} and {@link #setEndOfTrackingReceived(TimePoint, boolean)}
+ * - start/end of race in Racelog +/- TRACKING_BUFFER_IN_MINUTES
+ *
+ */
+ public void updateStartAndEndOfTracking();
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java
index c6b2997f97c..adb90c43932 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceImpl.java
@@ -156,8 +156,8 @@ DynamicTrackedRace, GPSTrackListener {
}
@Override
- public void recordFix(Competitor competitor, GPSFixMoving fix) {
- if (isFixWithinStartAndEndOfTracking(fix.getTimePoint())) {
+ public void recordFix(Competitor competitor, GPSFixMoving fix, boolean onlyWhenInTrackingTimesInterval) {
+ if (!onlyWhenInTrackingTimesInterval || isFixWithinStartAndEndOfTracking(fix.getTimePoint())) {
DynamicGPSFixTrack track = getTrack(competitor);
if (track != null) {
if (logger != null && logger.getLevel() != null && logger.getLevel().equals(Level.FINEST)) {
@@ -176,9 +176,9 @@ DynamicTrackedRace, GPSTrackListener {
}
@Override
- public void recordFix(Mark mark, GPSFix fix) {
+ public void recordFix(Mark mark, GPSFix fix, boolean onlyWhenInTrackingTimeInterval) {
final TimePoint fixTimePoint = fix.getTimePoint();
- if (isFixWithinStartAndEndOfTracking(fixTimePoint)) {
+ if (!onlyWhenInTrackingTimeInterval || isFixWithinStartAndEndOfTracking(fixTimePoint)) {
getOrCreateTrack(mark).addGPSFix(fix);
}
}
@@ -308,6 +308,7 @@ DynamicTrackedRace, GPSTrackListener {
if (!Util.equalsWithNull(oldStartOfRace, newStartOfRace)) {
notifyListenersStartOfRaceChanged(oldStartOfRace, newStartOfRace);
}
+ updateStartAndEndOfTracking();
}
@Override
@@ -978,6 +979,13 @@ DynamicTrackedRace, GPSTrackListener {
} catch (IOException e) {
logger.log(Level.INFO, "Exception trying to notify race status change listeners about start time change", e);
}
+ updateStartAndEndOfTracking();
+ }
+
+ @Override
+ public void onFinishedTimeChangedByRaceCommittee(TimePoint newFinishedTime) {
+ logger.info("Finished time of race "+getRace().getName()+" updated by race committee to "+newFinishedTime);
+ updateStartAndEndOfTracking();
}
@Override
@@ -1010,5 +1018,4 @@ DynamicTrackedRace, GPSTrackListener {
public DynamicGPSFixTrack getTrack(Mark mark) {
return (DynamicGPSFixTrack) super.getTrack(mark);
}
-
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java
index 2c27235f626..cc546138856 100755
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/DynamicTrackedRaceLogListener.java
@@ -13,6 +13,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogFinishPositioningConfirmed
import com.sap.sailing.domain.abstractlog.race.RaceLogFixedMarkPassingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogFlagEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogPassChangeEvent;
+import com.sap.sailing.domain.abstractlog.race.RaceLogRaceStatusEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogRevokeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartTimeEvent;
@@ -22,7 +23,6 @@ import com.sap.sailing.domain.abstractlog.race.analyzing.impl.AbortingFlagFinder
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.LastPublishedCourseDesignFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.MarkPassingDataFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
-import com.sap.sailing.domain.abstractlog.race.analyzing.impl.TrackingTimesFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.WindFixesFinder;
import com.sap.sailing.domain.abstractlog.race.impl.BaseRaceLogEventVisitor;
import com.sap.sailing.domain.base.Competitor;
@@ -31,12 +31,12 @@ import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
import com.sap.sailing.domain.common.impl.WindSourceImpl;
+import com.sap.sailing.domain.common.racelog.RaceLogRaceStatus;
import com.sap.sailing.domain.markpassingcalculation.MarkPassingUpdateListener;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.RaceLogWindFixDeclinationHelper;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.TimePoint;
-import com.sap.sse.common.Util.Pair;
import com.sap.sse.common.Util.Triple;
/**
@@ -78,10 +78,6 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
return new LastPublishedCourseDesignFinder(raceLog);
}
- private TrackingTimesFinder createTrackingTimesFinder(RaceLog raceLog) {
- return new TrackingTimesFinder(raceLog);
- }
-
private AbortingFlagFinder createAbortingFlagFinder(RaceLog raceLog) {
return new AbortingFlagFinder(raceLog);
}
@@ -154,7 +150,6 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
private void analyze(RaceLog raceLog) {
analyzeCourseDesign(null);
initializeWindTrack(raceLog);
- analyseTrackingTimes(raceLog);
if (markPassingUpdateListener != null) {
markPassingDataFinder = new MarkPassingDataFinder(raceLog);
analyzeMarkPassings();
@@ -162,14 +157,6 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
trackedRace.updateMarkPassingsAfterRaceLogChanges();
}
- private void analyseTrackingTimes(RaceLog raceLog) {
- Pair times = createTrackingTimesFinder(raceLog).analyze();
- if (times != null) {
- trackedRace.setStartOfTrackingReceived(times.getA());
- trackedRace.setEndOfTrackingReceived(times.getB());
- }
- }
-
private void analyzeCourseDesign(CourseBase courseBaseProvidedByEvent) {
CourseBase courseDesign = null;
for (RaceLog raceLog : raceLogs) {
@@ -319,11 +306,18 @@ public class DynamicTrackedRaceLogListener extends BaseRaceLogEventVisitor {
@Override
public void visit(RaceLogStartOfTrackingEvent event) {
- trackedRace.setStartOfTrackingReceived(event.getLogicalTimePoint());
+ trackedRace.updateStartAndEndOfTracking();
}
@Override
public void visit(RaceLogEndOfTrackingEvent event) {
- trackedRace.setEndOfTrackingReceived(event.getLogicalTimePoint());
+ trackedRace.updateStartAndEndOfTracking();
+ }
+
+ @Override
+ public void visit(RaceLogRaceStatusEvent event) {
+ if (event.getNextStatus().equals(RaceLogRaceStatus.FINISHED)){
+ trackedRace.invalidateEndTime();
+ }
}
}
diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
index 63bb88be797..487c6081ce4 100644
--- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
+++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedRaceImpl.java
@@ -40,6 +40,7 @@ import com.sap.sailing.domain.abstractlog.race.RaceLogEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogGateLineOpeningTimeEvent;
import com.sap.sailing.domain.abstractlog.race.RaceLogStartOfTrackingEvent;
import com.sap.sailing.domain.abstractlog.race.SimpleRaceLogIdentifier;
+import com.sap.sailing.domain.abstractlog.race.analyzing.impl.FinishedTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.RaceLogResolver;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.StartTimeFinder;
import com.sap.sailing.domain.abstractlog.race.analyzing.impl.TrackingTimesFinder;
@@ -83,6 +84,7 @@ import com.sap.sailing.domain.common.TrackedRaceStatusEnum;
import com.sap.sailing.domain.common.Wind;
import com.sap.sailing.domain.common.WindSource;
import com.sap.sailing.domain.common.WindSourceType;
+import com.sap.sailing.domain.common.abstractlog.TimePointSpecificationFoundInLog;
import com.sap.sailing.domain.common.confidence.BearingWithConfidence;
import com.sap.sailing.domain.common.confidence.BearingWithConfidenceCluster;
import com.sap.sailing.domain.common.confidence.HasConfidence;
@@ -156,6 +158,8 @@ import difflib.Patch;
import difflib.PatchFailedException;
public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials implements CourseListener {
+ public static final int TRACKING_BUFFER_IN_MINUTES = 5;
+
private static final long serialVersionUID = -4825546964220003507L;
private static final Logger logger = Logger.getLogger(TrackedRaceImpl.class.getName());
@@ -193,6 +197,13 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
*/
private TimePoint endOfTrackingReceived;
+ /**
+ * The start and end of tracking inferred via RaceLog, the received timepoint (see above) and mapping intervals.
+ * For the precedence order see {@link #updateStartAndEndOfTracking()}.
+ */
+ private TimePoint startOfTracking;
+ private TimePoint endOfTracking;
+
/**
* Race start time as announced by the tracking infrastructure
*/
@@ -728,27 +739,85 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
@Override
public TimePoint getStartOfTracking() {
- return startOfTrackingReceived;
+ return startOfTracking;
+ }
+
+ /**
+ * Updates the start and end of tracking in the following precedence order:
+ *
+ *
+ * - start/end of tracking in Racelog
+ * - manually set start/end of tracking via {@link #setStartOfTrackingReceived(TimePoint, boolean)} and {@link #setEndOfTrackingReceived(TimePoint, boolean)}
+ * - start/end of race in Racelog +/- TRACKING_BUFFER_IN_MINUTES
+ *
+ */
+ public void updateStartAndEndOfTracking() {
+ final Pair trackingTimesFromRaceLog = this.getTrackingTimesFromRaceLogs();
+ final Pair startAndFinishedTimesFromRaceLog = this.getStartAndFinishedTimeFromRaceLogs();
+ TimePoint oldStartOfTracking = getStartOfTracking();
+ TimePoint oldEndOfTracking = getEndOfTracking();
+ boolean startOfTrackingFound = false;
+ boolean endOfTrackingFound = false;
+ // check race log
+ if (trackingTimesFromRaceLog != null){
+ if (trackingTimesFromRaceLog.getA() != null) {
+ startOfTracking = trackingTimesFromRaceLog.getA().getTimePoint();
+ startOfTrackingFound = true;
+ }
+ if (trackingTimesFromRaceLog.getB() != null) {
+ endOfTracking = trackingTimesFromRaceLog.getB().getTimePoint();
+ endOfTrackingFound = true;
+ }
+ }
+ // check "received" variants coming from a connector directly
+ if (!startOfTrackingFound || !endOfTrackingFound) {
+ if (startOfTrackingReceived != null && !startOfTrackingFound) {
+ startOfTrackingFound = true;
+ startOfTracking = startOfTrackingReceived;
+ }
+ if (endOfTrackingReceived != null && !endOfTrackingFound) {
+ endOfTrackingFound = true;
+ endOfTracking = endOfTrackingReceived;
+ }
+ }
+ // check for start/finished times in race log and add a few minutes on the ends
+ if (!startOfTrackingFound || !endOfTrackingFound) {
+ if (startAndFinishedTimesFromRaceLog != null) {
+ if (!startOfTrackingFound && startAndFinishedTimesFromRaceLog.getA() != null){
+ startOfTracking = startAndFinishedTimesFromRaceLog.getA().minus(Duration.ONE_MINUTE.times(TRACKING_BUFFER_IN_MINUTES));
+ startOfTrackingFound = true;
+ }
+ if (!endOfTrackingFound && startAndFinishedTimesFromRaceLog.getB() != null){
+ endOfTracking = startAndFinishedTimesFromRaceLog.getB().plus(Duration.ONE_MINUTE.times(TRACKING_BUFFER_IN_MINUTES));
+ endOfTrackingFound = true;
+ }
+ }
+ }
+ startOfTrackingChanged(oldStartOfTracking, false);
+ endOfTrackingChanged(oldEndOfTracking, false);
}
@Override
public TimePoint getEndOfTracking() {
- return endOfTrackingReceived;
+ return endOfTracking;
}
public void invalidateStartTime() {
startTime = null;
startTimeWithoutInferenceFromStartMarkPassings = null;
+ updateStartAndEndOfTracking();
}
public void invalidateEndTime() {
endTime = null;
+ updateStartAndEndOfTracking();
}
protected void invalidateMarkPassingTimes() {
synchronized (markPassingsTimes) {
markPassingsTimes.clear();
}
+ updateStartAndEndOfTracking();
}
/**
@@ -757,18 +826,34 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
* tracking specification (see bug 3196). This method uses the {@link TrackingTimesFinder} to
* analyze all {@link #attachedRaceLogs race logs attached} to find tracking times specifications.
* If no tracking times specification is found at all, null is returned. Note that
- * even when a valid pair is returned, the components may be null.
+ * even when a valid pair is returned, the components may be null. This may either
+ * indicate that no event for that part of the tracking interval was found, or that an event
+ * was found that explicitly specified {@code null} to force an open interval on that end.
*/
@Override
- public Pair getTrackingTimesFromRaceLogs() {
+ public Pair getTrackingTimesFromRaceLogs() {
for (final RaceLog raceLog : attachedRaceLogs.values()) {
- Pair result = new TrackingTimesFinder(raceLog).analyze();
+ Pair result = new TrackingTimesFinder(raceLog).analyze();
if (result != null) {
return result;
}
}
return null;
}
+
+ @Override
+ public Pair getStartAndFinishedTimeFromRaceLogs() {
+ //Only one of the RaceLogs should have valid data, so one doesn't have to compare all the
+ //start/finished time values in order to find the earliest startTime/latestFinishedTime
+ for (final RaceLog raceLog : attachedRaceLogs.values()) {
+ TimePoint startTime = new StartTimeFinder(raceLogResolver, raceLog).analyze().getStartTime();
+ TimePoint finishedTime = new FinishedTimeFinder(raceLog).analyze();
+ if (startTime != null || finishedTime != null){
+ return new Util.Pair(startTime, finishedTime);
+ }
+ }
+ return null;
+ }
/**
* Calculates the start time of the race from various sources. The highest precedence take the {@link #attachedRaceLogs race logs},
@@ -1842,12 +1927,18 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
}
protected void setStartOfTrackingReceived(final TimePoint startOfTracking, final boolean waitForGPSFixesToLoad) {
- final TimePoint oldStartOfTracking = this.startOfTrackingReceived;
+ final TimePoint oldStartOfTracking = getStartOfTracking();
this.startOfTrackingReceived = startOfTracking;
- if (!Util.equalsWithNull(oldStartOfTracking, startOfTracking) &&
- (startOfTracking == null || (oldStartOfTracking != null && startOfTracking.before(oldStartOfTracking)))) {
- logger.info("Loading fixes after start of tracking for "+getRace()+" has been extended from "+oldStartOfTracking+" to "+startOfTracking);
- loadGPSFixesForExtendedTimeRange(startOfTracking, oldStartOfTracking, waitForGPSFixesToLoad);
+ updateStartAndEndOfTracking();
+ startOfTrackingChanged(oldStartOfTracking, waitForGPSFixesToLoad);
+ }
+
+ private void startOfTrackingChanged(final TimePoint oldStartOfTracking, boolean waitForGPSFixesToLoad){
+ TimePoint inferedStartOfTracking = getStartOfTracking();
+ if (!Util.equalsWithNull(oldStartOfTracking, inferedStartOfTracking) &&
+ (inferedStartOfTracking == null || (oldStartOfTracking != null && inferedStartOfTracking.before(oldStartOfTracking)))) {
+ logger.info("Loading fixes after start of tracking for "+getRace()+" has been extended from "+oldStartOfTracking+" to "+inferedStartOfTracking);
+ loadGPSFixesForExtendedTimeRange(inferedStartOfTracking, oldStartOfTracking, waitForGPSFixesToLoad);
}
}
@@ -1861,11 +1952,19 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
protected void setEndOfTrackingReceived(final TimePoint endOfTracking, final boolean waitForGPSFixesToLoad) {
final TimePoint oldEndOfTracking = this.endOfTrackingReceived;
this.endOfTrackingReceived = endOfTracking;
- if (!Util.equalsWithNull(oldEndOfTracking, endOfTracking) &&
- (endOfTracking == null || (oldEndOfTracking != null && endOfTracking.after(oldEndOfTracking)))) {
- logger.info("Loading fixes after end of tracking for "+getRace()+" has been extended from "+oldEndOfTracking+" to "+endOfTracking);
- loadGPSFixesForExtendedTimeRange(oldEndOfTracking, endOfTracking, waitForGPSFixesToLoad);
+ updateStartAndEndOfTracking();
+ endOfTrackingChanged(oldEndOfTracking, waitForGPSFixesToLoad);
+ }
+
+ private void endOfTrackingChanged(final TimePoint oldEndOfTracking, boolean waitForGPSFixesToLoad){
+ TimePoint inferedEndOfTracking = getEndOfTracking();
+
+ if (!Util.equalsWithNull(oldEndOfTracking, inferedEndOfTracking) &&
+ (inferedEndOfTracking == null || (oldEndOfTracking != null && inferedEndOfTracking.after(oldEndOfTracking)))) {
+ logger.info("Loading fixes after end of tracking for "+getRace()+" has been extended from "+oldEndOfTracking+" to "+inferedEndOfTracking);
+ loadGPSFixesForExtendedTimeRange(oldEndOfTracking, inferedEndOfTracking, waitForGPSFixesToLoad);
}
+
}
/**
@@ -3341,6 +3440,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
synchronized (TrackedRaceImpl.this) {
attachedRaceLogs.put(raceLog.getId(), raceLog);
notifyAll();
+ updateStartAndEndOfTracking();
}
}
@@ -3394,6 +3494,7 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
} catch (InterruptedException e) {
logger.log(Level.WARNING, "Interrupted while waiting for race log being attached", e);
}
+ updateStartAndEndOfTracking();
}
@Override
diff --git a/java/com.sap.sailing.feature/feature.xml b/java/com.sap.sailing.feature/feature.xml
index dd6184140a1..12e6cbee047 100755
--- a/java/com.sap.sailing.feature/feature.xml
+++ b/java/com.sap.sailing.feature/feature.xml
@@ -247,6 +247,13 @@ Computes leaderboard information for sailing races and offers RESTful APIs to al
version="0.0.0"
unpack="false"/>
+
+
boatClassIconsMap;
- private static BoatClassImageResources imageResources = BoatClassImageResources.INSTANCE;
-
- static {
- boatClassIconsMap = new HashMap();
-
- // handicap sailing icons
- boatClassIconsMap.put(BoatClassMasterdata.ORC.getDisplayName(), imageResources.ORCIcon());
- boatClassIconsMap.put(BoatClassMasterdata.ORC_CLUB.getDisplayName(), imageResources.ORCIcon());
- boatClassIconsMap.put(BoatClassMasterdata.ORC_INTERNATIONAL.getDisplayName(), imageResources.ORCIcon());
-
- boatClassIconsMap.put(BoatClassMasterdata._12M.getDisplayName(), imageResources._12mRIcon());
- boatClassIconsMap.put(BoatClassMasterdata._18Footer.getDisplayName(), imageResources._18FooterIcon());
- boatClassIconsMap.put(BoatClassMasterdata._2_4M.getDisplayName(), imageResources._2_4mIcon());
- boatClassIconsMap.put(BoatClassMasterdata._29ER.getDisplayName(), imageResources._29erIcon());
- boatClassIconsMap.put(BoatClassMasterdata._420.getDisplayName(), imageResources._420Icon());
- boatClassIconsMap.put(BoatClassMasterdata._470.getDisplayName(), imageResources._470Icon());
- boatClassIconsMap.put(BoatClassMasterdata._49ERFX.getDisplayName(), imageResources._49erFXIcon());
- boatClassIconsMap.put(BoatClassMasterdata._49ER.getDisplayName(), imageResources._49erIcon());
- boatClassIconsMap.put(BoatClassMasterdata._5O5.getDisplayName(), imageResources._505mIcon());
- boatClassIconsMap.put(BoatClassMasterdata.A_CAT.getDisplayName(), imageResources.A_CatIcon());
- boatClassIconsMap.put(BoatClassMasterdata.ALBIN_EXPRESS.getDisplayName(), imageResources.AlbinExpressIcon());
- boatClassIconsMap.put(BoatClassMasterdata.B_ONE.getDisplayName(), imageResources.BOne());
- boatClassIconsMap.put(BoatClassMasterdata.CANOE_IC.getDisplayName(), imageResources.CanoeICIcon());
- boatClassIconsMap.put(BoatClassMasterdata.CANOE_TAIFUN.getDisplayName(), imageResources.CanoeTaifunIcon());
- boatClassIconsMap.put(BoatClassMasterdata.CONTENDER.getDisplayName(), imageResources.ContenderIcon());
- boatClassIconsMap.put(BoatClassMasterdata.DRAGON_INT.getDisplayName(), imageResources.DragonIcon());
- boatClassIconsMap.put(BoatClassMasterdata.DYAS.getDisplayName(), imageResources.DyasIcon());
- boatClassIconsMap.put(BoatClassMasterdata.EUROPE_INT.getDisplayName(), imageResources.EuropeIcon());
- boatClassIconsMap.put(BoatClassMasterdata.EXTREME_40.getDisplayName(), imageResources.Extreme40Icon());
- boatClassIconsMap.put(BoatClassMasterdata.D_ONE.getDisplayName(), imageResources.DOneIcon());
- boatClassIconsMap.put(BoatClassMasterdata.D_35.getDisplayName(), imageResources.D35Icon());
- boatClassIconsMap.put(BoatClassMasterdata.F_16.getDisplayName(), imageResources.F16Icon());
- boatClassIconsMap.put(BoatClassMasterdata.F_18.getDisplayName(), imageResources.F18Icon());
- boatClassIconsMap.put(BoatClassMasterdata.FARR_30.getDisplayName(), imageResources.Farr30Icon());
- boatClassIconsMap.put(BoatClassMasterdata.FINN.getDisplayName(), imageResources.FinnIcon());
- boatClassIconsMap.put(BoatClassMasterdata.FLYING_DUTCHMAN.getDisplayName(), imageResources.FlyingDutchmanIcon());
- boatClassIconsMap.put(BoatClassMasterdata.FOLKBOAT.getDisplayName(), imageResources.FolkBoatIcon());
- boatClassIconsMap.put(BoatClassMasterdata.FUN.getDisplayName(), imageResources.FUNIcon());
- boatClassIconsMap.put(BoatClassMasterdata.GC_32.getDisplayName(), imageResources.GC32Icon());
- boatClassIconsMap.put(BoatClassMasterdata.H_BOAT.getDisplayName(), imageResources.H_BoatIcon());
- boatClassIconsMap.put(BoatClassMasterdata.HOBIE_16.getDisplayName(), imageResources.HobieIcon());
- boatClassIconsMap.put(BoatClassMasterdata.HOBIE_TIGER.getDisplayName(), imageResources.HobieIcon());
- boatClassIconsMap.put(BoatClassMasterdata.HOBIE_WILD_CAT.getDisplayName(), imageResources.HobieIcon());
- boatClassIconsMap.put(BoatClassMasterdata.INTERNATIONAL_14.getDisplayName(), imageResources.International14Icon());
- boatClassIconsMap.put(BoatClassMasterdata.J22.getDisplayName(), imageResources.J22Icon());
- boatClassIconsMap.put(BoatClassMasterdata.J24.getDisplayName(), imageResources.J24Icon());
- boatClassIconsMap.put(BoatClassMasterdata.J70.getDisplayName(), imageResources.J70Icon());
- boatClassIconsMap.put(BoatClassMasterdata.J80.getDisplayName(), imageResources.J80Icon());
- boatClassIconsMap.put(BoatClassMasterdata.KIELZUGVOGEL.getDisplayName(), imageResources.KielzugvogelIcon());
- boatClassIconsMap.put(BoatClassMasterdata.LASER_2.getDisplayName(), imageResources.Laser2Icon());
- boatClassIconsMap.put(BoatClassMasterdata.LASER_SB3.getDisplayName(), imageResources.LaserSB3Icon());
- boatClassIconsMap.put(BoatClassMasterdata.LASER_INT.getDisplayName(), imageResources.LaserIcon());
- boatClassIconsMap.put(BoatClassMasterdata.LASER_4_7.getDisplayName(), imageResources.LaserIcon());
- boatClassIconsMap.put(BoatClassMasterdata.LASER_RADIAL.getDisplayName(), imageResources.LaserIcon());
- boatClassIconsMap.put(BoatClassMasterdata.LAGO_26.getDisplayName(), imageResources.Lago26Icon());
- boatClassIconsMap.put(BoatClassMasterdata.MELGES_24.getDisplayName(), imageResources.Melges24Icon());
- boatClassIconsMap.put(BoatClassMasterdata.MINI_TRANSAT.getDisplayName(), imageResources.MiniTransatIcon());
- boatClassIconsMap.put(BoatClassMasterdata.MUSTO_SKIFF.getDisplayName(), imageResources.MustoSkiffIcon());
- boatClassIconsMap.put(BoatClassMasterdata.NACRA_17.getDisplayName(), imageResources.Nacra17Icon());
- boatClassIconsMap.put(BoatClassMasterdata.O_JOLLE.getDisplayName(), imageResources.OJolleIcon());
- boatClassIconsMap.put(BoatClassMasterdata.OK.getDisplayName(), imageResources.OKIcon());
- boatClassIconsMap.put(BoatClassMasterdata.OPEN_BIC.getDisplayName(), imageResources.OpenBicIcon());
- boatClassIconsMap.put(BoatClassMasterdata.OPTIMIST.getDisplayName(), imageResources.OptimistIcon());
- boatClassIconsMap.put(BoatClassMasterdata.PIRATE.getDisplayName(), imageResources.PiratIcon());
- boatClassIconsMap.put(BoatClassMasterdata.PLATU_25.getDisplayName(), imageResources.Platu25Icon());
- boatClassIconsMap.put(BoatClassMasterdata.PWA.getDisplayName(), imageResources.PWAIcon());
- boatClassIconsMap.put(BoatClassMasterdata.RS800.getDisplayName(), imageResources.RS800Icon());
- boatClassIconsMap.put(BoatClassMasterdata.RS_X.getDisplayName(), imageResources.RSXIcon());
- boatClassIconsMap.put(BoatClassMasterdata.RS_FEVA.getDisplayName(), imageResources.RSFevaIcon());
- boatClassIconsMap.put(BoatClassMasterdata.SONAR.getDisplayName(), imageResources.SonarIcon());
- boatClassIconsMap.put(BoatClassMasterdata.SOLING.getDisplayName(), imageResources.SolingIcon());
- boatClassIconsMap.put(BoatClassMasterdata.STAR.getDisplayName(), imageResources.StarIcon());
- boatClassIconsMap.put(BoatClassMasterdata.STREAMLINE.getDisplayName(), imageResources.StreamlineIcon());
- boatClassIconsMap.put(BoatClassMasterdata.SWAN_45.getDisplayName(), imageResources.Swan45Icon());
- boatClassIconsMap.put(BoatClassMasterdata.TORNADO.getDisplayName(), imageResources.TornadoIcon());
- boatClassIconsMap.put(BoatClassMasterdata.TRIAS.getDisplayName(), imageResources.TriasIcon());
- boatClassIconsMap.put(BoatClassMasterdata.X_99.getDisplayName(), imageResources.X99Icon());
- }
-
- public static ImageResource getBoatClassIconResource(String displayName) {
- ImageResource imageResource = boatClassIconsMap.get(displayName);
- if(imageResource == null) {
- imageResource = BoatClassImageResources.INSTANCE.genericBoatClass();
- }
- return imageResource;
- }
-}
+package com.sap.sailing.gwt.common.client;
+
+import java.util.HashMap;
+import java.util.Map;
+
+import com.google.gwt.resources.client.ImageResource;
+import com.sap.sailing.domain.common.BoatClassMasterdata;
+
+/**
+ * All images are expected to be 140x140px with transparent background.
+ *
+ * @author Steffen Tobias Wagner, Axel Uhl (D043530)
+ *
+ */
+public class BoatClassImageResolver {
+ private static Map boatClassIconsMap;
+ private static BoatClassImageResources imageResources = BoatClassImageResources.INSTANCE;
+
+ static {
+ boatClassIconsMap = new HashMap();
+
+ // handicap sailing icons
+ boatClassIconsMap.put(BoatClassMasterdata.ORC.getDisplayName(), imageResources.ORCIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.ORC_CLUB.getDisplayName(), imageResources.ORCIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.ORC_INTERNATIONAL.getDisplayName(), imageResources.ORCIcon());
+
+ boatClassIconsMap.put(BoatClassMasterdata.PHRF.getDisplayName(), imageResources.PHRFIcon());
+
+ boatClassIconsMap.put(BoatClassMasterdata._12M.getDisplayName(), imageResources._12mRIcon());
+ boatClassIconsMap.put(BoatClassMasterdata._18Footer.getDisplayName(), imageResources._18FooterIcon());
+ boatClassIconsMap.put(BoatClassMasterdata._2_4M.getDisplayName(), imageResources._2_4mIcon());
+ boatClassIconsMap.put(BoatClassMasterdata._29ER.getDisplayName(), imageResources._29erIcon());
+ boatClassIconsMap.put(BoatClassMasterdata._420.getDisplayName(), imageResources._420Icon());
+ boatClassIconsMap.put(BoatClassMasterdata._470.getDisplayName(), imageResources._470Icon());
+ boatClassIconsMap.put(BoatClassMasterdata._49ERFX.getDisplayName(), imageResources._49erFXIcon());
+ boatClassIconsMap.put(BoatClassMasterdata._49ER.getDisplayName(), imageResources._49erIcon());
+ boatClassIconsMap.put(BoatClassMasterdata._5O5.getDisplayName(), imageResources._505mIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.A_CAT.getDisplayName(), imageResources.A_CatIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.ALBIN_EXPRESS.getDisplayName(), imageResources.AlbinExpressIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.B_ONE.getDisplayName(), imageResources.BOne());
+ boatClassIconsMap.put(BoatClassMasterdata.CANOE_IC.getDisplayName(), imageResources.CanoeICIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.CANOE_TAIFUN.getDisplayName(), imageResources.CanoeTaifunIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.CONTENDER.getDisplayName(), imageResources.ContenderIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.CC_30.getDisplayName(), imageResources.CC30Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.DRAGON_INT.getDisplayName(), imageResources.DragonIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.DYAS.getDisplayName(), imageResources.DyasIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.EUROPE_INT.getDisplayName(), imageResources.EuropeIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.EXTREME_40.getDisplayName(), imageResources.Extreme40Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.D_ONE.getDisplayName(), imageResources.DOneIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.D_35.getDisplayName(), imageResources.D35Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.F_16.getDisplayName(), imageResources.F16Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.F_18.getDisplayName(), imageResources.F18Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.FARR_30.getDisplayName(), imageResources.Farr30Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.FARR_280.getDisplayName(), imageResources.Farr280Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.FINN.getDisplayName(), imageResources.FinnIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.FLYING_DUTCHMAN.getDisplayName(), imageResources.FlyingDutchmanIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.FOLKBOAT.getDisplayName(), imageResources.FolkBoatIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.FUN.getDisplayName(), imageResources.FUNIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.GC_32.getDisplayName(), imageResources.GC32Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.GP_26.getDisplayName(), imageResources.GP26Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.H_BOAT.getDisplayName(), imageResources.H_BoatIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.HOBIE_16.getDisplayName(), imageResources.HobieIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.HOBIE_TIGER.getDisplayName(), imageResources.HobieIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.HOBIE_WILD_CAT.getDisplayName(), imageResources.HobieIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.INTERNATIONAL_14.getDisplayName(), imageResources.International14Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.J22.getDisplayName(), imageResources.J22Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.J24.getDisplayName(), imageResources.J24Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.J70.getDisplayName(), imageResources.J70Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.J80.getDisplayName(), imageResources.J80Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.J88.getDisplayName(), imageResources.J88Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.J105.getDisplayName(), imageResources.J105Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.J111.getDisplayName(), imageResources.J111Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.KIELZUGVOGEL.getDisplayName(), imageResources.KielzugvogelIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.LASER_2.getDisplayName(), imageResources.Laser2Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.LASER_SB3.getDisplayName(), imageResources.LaserSB3Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.LASER_INT.getDisplayName(), imageResources.LaserIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.LASER_4_7.getDisplayName(), imageResources.LaserIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.LASER_RADIAL.getDisplayName(), imageResources.LaserIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.LAGO_26.getDisplayName(), imageResources.Lago26Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.MELGES_20.getDisplayName(), imageResources.Melges20Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.MELGES_24.getDisplayName(), imageResources.Melges24Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.MINI_TRANSAT.getDisplayName(), imageResources.MiniTransatIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.MUSTO_SKIFF.getDisplayName(), imageResources.MustoSkiffIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.NACRA_17.getDisplayName(), imageResources.Nacra17Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.O_JOLLE.getDisplayName(), imageResources.OJolleIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.OK.getDisplayName(), imageResources.OKIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.OPEN_BIC.getDisplayName(), imageResources.OpenBicIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.OPTIMIST.getDisplayName(), imageResources.OptimistIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.PIRATE.getDisplayName(), imageResources.PiratIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.PLATU_25.getDisplayName(), imageResources.Platu25Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.PWA.getDisplayName(), imageResources.PWAIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.RS800.getDisplayName(), imageResources.RS800Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.RS_X.getDisplayName(), imageResources.RSXIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.RS_FEVA.getDisplayName(), imageResources.RSFevaIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.SONAR.getDisplayName(), imageResources.SonarIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.SOLING.getDisplayName(), imageResources.SolingIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.STAR.getDisplayName(), imageResources.StarIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.STREAMLINE.getDisplayName(), imageResources.StreamlineIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.SWAN_45.getDisplayName(), imageResources.Swan45Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.TARTAN_10.getDisplayName(), imageResources.Tartan10Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.TORNADO.getDisplayName(), imageResources.TornadoIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.TRIAS.getDisplayName(), imageResources.TriasIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.VIPER_640.getDisplayName(), imageResources.Viper640Icon());
+ boatClassIconsMap.put(BoatClassMasterdata.VX_ONE.getDisplayName(), imageResources.VXOneIcon());
+ boatClassIconsMap.put(BoatClassMasterdata.X_99.getDisplayName(), imageResources.X99Icon());
+ }
+
+ public static ImageResource getBoatClassIconResource(String displayName) {
+ ImageResource imageResource = boatClassIconsMap.get(displayName);
+ if(imageResource == null) {
+ imageResource = BoatClassImageResources.INSTANCE.genericBoatClass();
+ }
+ return imageResource;
+ }
+}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java
index f11d0e05012..a6e47667273 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/common/client/BoatClassImageResources.java
@@ -1,204 +1,239 @@
-package com.sap.sailing.gwt.common.client;
-
-import com.google.gwt.core.client.GWT;
-import com.google.gwt.resources.client.ClientBundle;
-import com.google.gwt.resources.client.ImageResource;
-
-public interface BoatClassImageResources extends ClientBundle {
- public static final BoatClassImageResources INSTANCE = GWT.create(BoatClassImageResources.class);
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/GENERIC_BOATCLASS.png")
- ImageResource genericBoatClass();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/12MR.png")
- ImageResource _12mRIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/18FOOTER.png")
- ImageResource _18FooterIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/2_4M.png")
- ImageResource _2_4mIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/29ER.png")
- ImageResource _29erIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/420.png")
- ImageResource _420Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/470.png")
- ImageResource _470Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER_FX.png")
- ImageResource _49erFXIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER.png")
- ImageResource _49erIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/505.png")
- ImageResource _505mIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/A_CAT.png")
- ImageResource A_CatIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/ALBIN_EXPRESS.png")
- ImageResource AlbinExpressIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/B_ONE.png")
- ImageResource BOne();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/CANOE_IC.png")
- ImageResource CanoeICIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/CANOE_TAIFUN.png")
- ImageResource CanoeTaifunIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/CONTENDER.png")
- ImageResource ContenderIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/DRAGON.png")
- ImageResource DragonIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/EUROPE.png")
- ImageResource EuropeIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/EXTREME40.png")
- ImageResource Extreme40Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/D35.png")
- ImageResource D35Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/F16.png")
- ImageResource F16Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/F18.png")
- ImageResource F18Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FARR_30.png")
- ImageResource Farr30Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FINN.png")
- ImageResource FinnIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FLYING_DUTCHMAN.png")
- ImageResource FlyingDutchmanIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FOLKBOAT.png")
- ImageResource FolkBoatIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FUN.png")
- ImageResource FUNIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/H_BOAT.png")
- ImageResource H_BoatIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/HOBIE.png")
- ImageResource HobieIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J22.png")
- ImageResource J22Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J24.png")
- ImageResource J24Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J70.png")
- ImageResource J70Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J80.png")
- ImageResource J80Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/LASER2.png")
- ImageResource Laser2Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/LASER_SB3.png")
- ImageResource LaserSB3Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/LASER.png")
- ImageResource LaserIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/LAGO_26.png")
- ImageResource Lago26Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/MELGES_24.png")
- ImageResource Melges24Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/MINI_TRANSAT.png")
- ImageResource MiniTransatIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/MUSTO_SKIFF.png")
- ImageResource MustoSkiffIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/NACRA_17.png")
- ImageResource Nacra17Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/OK.png")
- ImageResource OKIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/OPTIMIST.png")
- ImageResource OptimistIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/PIRAT.png")
- ImageResource PiratIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/PLATU_25.png")
- ImageResource Platu25Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/RS_X.png")
- ImageResource RSXIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/RS_FEVA.png")
- ImageResource RSFevaIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/SONAR.png")
- ImageResource SonarIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/SOLING.png")
- ImageResource SolingIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/STAR.png")
- ImageResource StarIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/STREAMLINE.png")
- ImageResource StreamlineIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/SWAN_45.png")
- ImageResource Swan45Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/TORNADO.png")
- ImageResource TornadoIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/X_99.png")
- ImageResource X99Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/DYAS.png")
- ImageResource DyasIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/INTERNATIONAL_14.png")
- ImageResource International14Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/OPENBIC.png")
- ImageResource OpenBicIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/RS800.png")
- ImageResource RS800Icon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/ORC.png")
- ImageResource ORCIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/KIELZUGVOGEL.png")
- ImageResource KielzugvogelIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/TRIAS.png")
- ImageResource TriasIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/O_JOLLE.png")
- ImageResource OJolleIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/D_ONE.png")
- ImageResource DOneIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/PWA.png")
- ImageResource PWAIcon();
-
- @Source("com/sap/sailing/gwt/ui/client/images/boatclass/GC_32.png")
- ImageResource GC32Icon();
-}
+package com.sap.sailing.gwt.common.client;
+
+import com.google.gwt.core.client.GWT;
+import com.google.gwt.resources.client.ClientBundle;
+import com.google.gwt.resources.client.ImageResource;
+
+public interface BoatClassImageResources extends ClientBundle {
+ public static final BoatClassImageResources INSTANCE = GWT.create(BoatClassImageResources.class);
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/GENERIC_BOATCLASS.png")
+ ImageResource genericBoatClass();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/12MR.png")
+ ImageResource _12mRIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/18FOOTER.png")
+ ImageResource _18FooterIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/2_4M.png")
+ ImageResource _2_4mIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/29ER.png")
+ ImageResource _29erIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/420.png")
+ ImageResource _420Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/470.png")
+ ImageResource _470Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER_FX.png")
+ ImageResource _49erFXIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/49ER.png")
+ ImageResource _49erIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/505.png")
+ ImageResource _505mIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/A_CAT.png")
+ ImageResource A_CatIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/ALBIN_EXPRESS.png")
+ ImageResource AlbinExpressIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/B_ONE.png")
+ ImageResource BOne();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/CANOE_IC.png")
+ ImageResource CanoeICIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/CANOE_TAIFUN.png")
+ ImageResource CanoeTaifunIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/CONTENDER.png")
+ ImageResource ContenderIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/DRAGON.png")
+ ImageResource DragonIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/EUROPE.png")
+ ImageResource EuropeIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/EXTREME40.png")
+ ImageResource Extreme40Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/D35.png")
+ ImageResource D35Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/F16.png")
+ ImageResource F16Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/F18.png")
+ ImageResource F18Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FARR_30.png")
+ ImageResource Farr30Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FARR_280.png")
+ ImageResource Farr280Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FINN.png")
+ ImageResource FinnIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FLYING_DUTCHMAN.png")
+ ImageResource FlyingDutchmanIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FOLKBOAT.png")
+ ImageResource FolkBoatIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/FUN.png")
+ ImageResource FUNIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/H_BOAT.png")
+ ImageResource H_BoatIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/HOBIE.png")
+ ImageResource HobieIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J22.png")
+ ImageResource J22Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J24.png")
+ ImageResource J24Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J70.png")
+ ImageResource J70Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J80.png")
+ ImageResource J80Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J88.png")
+ ImageResource J88Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J105.png")
+ ImageResource J105Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/J111.png")
+ ImageResource J111Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/LASER2.png")
+ ImageResource Laser2Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/LASER_SB3.png")
+ ImageResource LaserSB3Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/LASER.png")
+ ImageResource LaserIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/LAGO_26.png")
+ ImageResource Lago26Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/MELGES_20.png")
+ ImageResource Melges20Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/MELGES_24.png")
+ ImageResource Melges24Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/MINI_TRANSAT.png")
+ ImageResource MiniTransatIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/MUSTO_SKIFF.png")
+ ImageResource MustoSkiffIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/NACRA_17.png")
+ ImageResource Nacra17Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/OK.png")
+ ImageResource OKIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/OPTIMIST.png")
+ ImageResource OptimistIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/PIRAT.png")
+ ImageResource PiratIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/PLATU_25.png")
+ ImageResource Platu25Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/RS_X.png")
+ ImageResource RSXIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/RS_FEVA.png")
+ ImageResource RSFevaIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/SONAR.png")
+ ImageResource SonarIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/SOLING.png")
+ ImageResource SolingIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/STAR.png")
+ ImageResource StarIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/STREAMLINE.png")
+ ImageResource StreamlineIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/SWAN_45.png")
+ ImageResource Swan45Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/TORNADO.png")
+ ImageResource TornadoIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/X_99.png")
+ ImageResource X99Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/DYAS.png")
+ ImageResource DyasIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/INTERNATIONAL_14.png")
+ ImageResource International14Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/OPENBIC.png")
+ ImageResource OpenBicIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/RS800.png")
+ ImageResource RS800Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/ORC.png")
+ ImageResource ORCIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/PHRF.png")
+ ImageResource PHRFIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/KIELZUGVOGEL.png")
+ ImageResource KielzugvogelIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/TRIAS.png")
+ ImageResource TriasIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/O_JOLLE.png")
+ ImageResource OJolleIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/D_ONE.png")
+ ImageResource DOneIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/PWA.png")
+ ImageResource PWAIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/GC_32.png")
+ ImageResource GC32Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/CC_30.png")
+ ImageResource CC30Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/GP_26.png")
+ ImageResource GP26Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/VX_ONE.png")
+ ImageResource VXOneIcon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/TARTAN_10.png")
+ ImageResource Tartan10Icon();
+
+ @Source("com/sap/sailing/gwt/ui/client/images/boatclass/VIPER_640.png")
+ ImageResource Viper640Icon();
+
+
+}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.java
index 44c10e1f1f9..d6170b2f01f 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.java
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.java
@@ -15,6 +15,7 @@ import com.google.gwt.uibinder.client.UiField;
import com.google.gwt.user.client.Event;
import com.google.gwt.user.client.ui.Composite;
import com.google.gwt.user.client.ui.FlowPanel;
+import com.google.gwt.user.client.ui.UIObject;
import com.google.gwt.user.client.ui.Widget;
import com.sap.sailing.gwt.common.client.i18n.TextMessages;
import com.sap.sailing.gwt.home.communication.eventview.EventViewDTO;
@@ -157,10 +158,12 @@ public class EventHeader extends Composite {
if(!presenter.needsSelectionInHeader()) {
eventName.setInnerText(nameToShow);
LabelTypeUtil.renderLabelType(eventState, event.getState().getStateMarker());
+ UIObject.ensureDebugId(eventState, "EventStateLabelDiv");
hide(dropdownTitle);
} else {
dropdownEventName.setInnerText(nameToShow);
LabelTypeUtil.renderLabelType(dropdownEventState, presenter.showRegattaMetadata() ? presenter.getRegattaMetadata().getState().getStateMarker() : event.getState().getStateMarker());
+ UIObject.ensureDebugId(dropdownEventState, "EventStateLabelDiv");
hide(staticTitle);
initDropdown();
}
diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.ui.xml b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.ui.xml
index 6f98e5881ac..bf98f60fd23 100644
--- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.ui.xml
+++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/partials/eventheader/EventHeader.ui.xml
@@ -4,7 +4,7 @@
-
+