mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-09-28 08:26:44 +00:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
+13
-6
@@ -12,39 +12,46 @@ public enum WindSourceType {
|
||||
/**
|
||||
* Manually entered via a web form or received through a REST service call, e.g., from BeTomorrow's estimation
|
||||
*/
|
||||
WEB(true),
|
||||
WEB(true, 0.9),
|
||||
|
||||
/**
|
||||
* Measured using wind sensors
|
||||
*/
|
||||
EXPEDITION(true),
|
||||
EXPEDITION(true, 0.9),
|
||||
|
||||
/**
|
||||
* Estimates wind conditions by analyzing the boat tracks; may not have results for all time points, e.g.,
|
||||
* because at a given time point all boats may sail on the same tack and hence no averaging between the
|
||||
* two tacks is possible. This is the more likely to happen the smaller the fleet tracked is.
|
||||
*/
|
||||
TRACK_BASED_ESTIMATION(false),
|
||||
TRACK_BASED_ESTIMATION(false, 0.5),
|
||||
|
||||
/**
|
||||
* Inferred from the race course layout if the course is known to have its first leg be an upwind leg
|
||||
*/
|
||||
COURSE_BASED(false),
|
||||
COURSE_BASED(false, 0.3),
|
||||
|
||||
/**
|
||||
* Wind estimation combined from all other wind sources, using <code>TrackedRace.getWind(...)</code>, based on
|
||||
* confidences
|
||||
*/
|
||||
COMBINED(false);
|
||||
COMBINED(false, 0.9);
|
||||
|
||||
private final boolean canBeStored;
|
||||
|
||||
private WindSourceType(boolean canBeStored) {
|
||||
private final double baseConfidence;
|
||||
|
||||
private WindSourceType(boolean canBeStored, double baseConfidence) {
|
||||
this.canBeStored = canBeStored;
|
||||
this.baseConfidence = baseConfidence;
|
||||
}
|
||||
|
||||
public boolean canBeStored() {
|
||||
return canBeStored;
|
||||
}
|
||||
|
||||
public double getBaseConfidence() {
|
||||
return baseConfidence;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -93,7 +93,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
|
||||
@Override
|
||||
public WindTrack loadWindTrack(Event event, RaceDefinition race, WindSource windSource, long millisecondsOverWhichToAverage) {
|
||||
WindTrack result = new WindTrackImpl(millisecondsOverWhichToAverage);
|
||||
WindTrack result = new WindTrackImpl(millisecondsOverWhichToAverage, windSource.getType().getBaseConfidence());
|
||||
try {
|
||||
BasicDBObject query = new BasicDBObject();
|
||||
query.put(FieldNames.EVENT_NAME.name(), event.getName());
|
||||
@@ -289,7 +289,7 @@ public class DomainObjectFactoryImpl implements DomainObjectFactory {
|
||||
}
|
||||
WindTrack track = result.get(windSource);
|
||||
if (track == null) {
|
||||
track = new WindTrackImpl(millisecondsOverWhichToAverageWind);
|
||||
track = new WindTrackImpl(millisecondsOverWhichToAverageWind, windSource.getType().getBaseConfidence());
|
||||
result.put(windSource, track);
|
||||
}
|
||||
track.add(wind);
|
||||
|
||||
+3
-3
@@ -109,7 +109,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
|
||||
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(/* position */ null, now);
|
||||
// less precision because downwind estimation has less confidence
|
||||
assertEquals(180., estimatedWindDirection.getBearing().getDegrees(), 0.2);
|
||||
CombinedWindTrackImpl combinedTrack = new CombinedWindTrackImpl(getTrackedRace());
|
||||
CombinedWindTrackImpl combinedTrack = new CombinedWindTrackImpl(getTrackedRace(), WindSourceType.COMBINED.getBaseConfidence());
|
||||
Wind combinedWindDirection = combinedTrack.getAveragedWind(/* position */ null, now);
|
||||
assertEquals(180., combinedWindDirection.getBearing().getDegrees(), 0.2); // a bit less precise as course-based wind isn't exactly 180deg
|
||||
}
|
||||
@@ -132,7 +132,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
|
||||
assertEquals(LegType.UPWIND, firstLeg.getLegType(new MillisecondsTimePoint(MillisecondsTimePoint.now().asMillis())));
|
||||
final Map<TimePoint, Wind> cachedFixes = new HashMap<TimePoint, Wind>();
|
||||
TrackBasedEstimationWindTrackImpl track = new TrackBasedEstimationWindTrackImpl(
|
||||
getTrackedRace(), /* millisecondsOverWhichToAverage */ 30000) {
|
||||
getTrackedRace(), /* millisecondsOverWhichToAverage */ 30000, WindSourceType.TRACK_BASED_ESTIMATION.getBaseConfidence()) {
|
||||
@Override
|
||||
protected void cache(TimePoint timePoint, Wind fix) {
|
||||
super.cache(timePoint, fix);
|
||||
@@ -172,7 +172,7 @@ public class WindEstimationOnConstructedTracksTest extends StoredTrackBasedTest
|
||||
setBearingForCompetitor(competitors.get(1), checkTime, 50);
|
||||
final Map<TimePoint, Wind> cachedFixes = new HashMap<TimePoint, Wind>();
|
||||
TrackBasedEstimationWindTrackImpl track = new TrackBasedEstimationWindTrackImpl(
|
||||
getTrackedRace(), /* millisecondsOverWhichToAverage */ 30000) {
|
||||
getTrackedRace(), /* millisecondsOverWhichToAverage */ 30000, WindSourceType.TRACK_BASED_ESTIMATION.getBaseConfidence()) {
|
||||
@Override
|
||||
protected void cache(TimePoint timePoint, Wind fix) {
|
||||
super.cache(timePoint, fix);
|
||||
|
||||
+1
-1
@@ -58,7 +58,7 @@ public class WindEstimationOnKielerWoche505Race2DataTest extends OnlineTracTracB
|
||||
// hence have a direction change.
|
||||
TimePoint middle = new MillisecondsTimePoint(1308839492322l);
|
||||
TrackBasedEstimationWindTrackImpl estimatedWindTrack = new TrackBasedEstimationWindTrackImpl(getTrackedRace(),
|
||||
WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND);
|
||||
WindTrack.DEFAULT_MILLISECONDS_OVER_WHICH_TO_AVERAGE_WIND, WindSourceType.TRACK_BASED_ESTIMATION.getBaseConfidence());
|
||||
Wind estimatedWindDirection = getTrackedRace().getEstimatedWindDirection(/* position */ null, middle);
|
||||
assertNotNull(estimatedWindDirection);
|
||||
Wind estimationBasedOnTrack = estimatedWindTrack.getAveragedWind(null, middle);
|
||||
|
||||
+14
@@ -2,11 +2,13 @@ package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import java.util.NavigableSet;
|
||||
|
||||
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.Position;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
import com.sap.sailing.domain.tracking.WindTrack;
|
||||
import com.sap.sailing.domain.tracking.impl.WindTrackImpl.DummyWind;
|
||||
|
||||
/**
|
||||
* Delivers what {@link TrackedRace#getWind(Position, TimePoint)} delivers, as a navigable set.
|
||||
@@ -35,4 +37,16 @@ public class CombinedWindAsNavigableSet extends VirtualWindFixesAsNavigableSet {
|
||||
return new CombinedWindAsNavigableSet(track, trackedRace, from, to, getResolutionInMilliseconds());
|
||||
}
|
||||
|
||||
/**
|
||||
* Time point up to and including which the GPS fixes are considered in the race's tracks. Returns the value of
|
||||
* {@link #to} unless it is <code>null</code>. In this case, the time point of the
|
||||
* {@link TrackedRace#getAssumedEnd() assumed end of race},
|
||||
* {@link #ceilingToResolution(Wind) ceiled to the resolution of this set} will be returned instead. If no valid
|
||||
* time of a newest event can be obtained from the race, <code>MillisecondsTimePoint(1)</code> is returned instead.
|
||||
*/
|
||||
protected TimePoint getTo() {
|
||||
return getToInternal() == null ? getTrackedRace().getAssumedEnd() == null ? new MillisecondsTimePoint(1)
|
||||
: ceilingToResolution(new DummyWind(getTrackedRace().getAssumedEnd())) : getToInternal();
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
+2
-2
@@ -14,8 +14,8 @@ import com.sap.sailing.domain.tracking.impl.TrackBasedEstimationWindTrackImpl.Es
|
||||
public class CombinedWindTrackImpl extends VirtualWindTrackImpl {
|
||||
private CombinedWindAsNavigableSet virtualInternalRawFixes;
|
||||
|
||||
public CombinedWindTrackImpl(TrackedRace trackedRace) {
|
||||
super(trackedRace, /* millisecondsOverWhichToAverage not used, see overridden method */ -1);
|
||||
public CombinedWindTrackImpl(TrackedRace trackedRace, double baseConfidence) {
|
||||
super(trackedRace, /* millisecondsOverWhichToAverage not used, see overridden method */ -1, baseConfidence);
|
||||
virtualInternalRawFixes = new CombinedWindAsNavigableSet(this, trackedRace, /* resolutionInMilliseconds */ 10000l);
|
||||
}
|
||||
|
||||
|
||||
+16
-6
@@ -2,6 +2,7 @@ package com.sap.sailing.domain.tracking.impl;
|
||||
|
||||
import java.util.NavigableSet;
|
||||
|
||||
import com.sap.sailing.domain.base.impl.MillisecondsTimePoint;
|
||||
import com.sap.sailing.domain.common.TimePoint;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.Wind;
|
||||
@@ -19,11 +20,17 @@ import com.sap.sailing.util.impl.UnmodifiableNavigableSet;
|
||||
*
|
||||
*/
|
||||
public class CourseBasedWindTrackImpl extends WindTrackImpl {
|
||||
/**
|
||||
* The first leg's direction will be measured this many milliseconds before the estimated race start time,
|
||||
* at estimated race start time and this many milliseconds after estimated race start time.
|
||||
*/
|
||||
private final long MILLISECONDS_AROUND_START_TO_TRACK = 30000l;
|
||||
|
||||
private final TrackedRace trackedRace;
|
||||
private static final NavigableSet<Wind> empty = new UnmodifiableNavigableSet<Wind>(new ArrayListNavigableSet<Wind>(WindComparator.INSTANCE));
|
||||
|
||||
public CourseBasedWindTrackImpl(TrackedRace trackedRace, long millisecondsOverWhichToAverage) {
|
||||
super(millisecondsOverWhichToAverage);
|
||||
public CourseBasedWindTrackImpl(TrackedRace trackedRace, long millisecondsOverWhichToAverage, double baseConfidence) {
|
||||
super(millisecondsOverWhichToAverage, baseConfidence);
|
||||
this.trackedRace = trackedRace;
|
||||
}
|
||||
|
||||
@@ -33,10 +40,13 @@ public class CourseBasedWindTrackImpl extends WindTrackImpl {
|
||||
if (trackedRace.raceIsKnownToStartUpwind()) {
|
||||
TimePoint startTime = trackedRace.getStart();
|
||||
if (startTime != null) {
|
||||
result = new ArrayListNavigableSet<Wind>(1, WindComparator.INSTANCE);
|
||||
final Wind directionFromStartToNextMark = trackedRace.getDirectionFromStartToNextMark(startTime);
|
||||
if (directionFromStartToNextMark != null) {
|
||||
result.add(directionFromStartToNextMark);
|
||||
result = new ArrayListNavigableSet<Wind>(3, WindComparator.INSTANCE);
|
||||
for (long t = startTime.asMillis() - MILLISECONDS_AROUND_START_TO_TRACK; t <= startTime.asMillis()
|
||||
+ MILLISECONDS_AROUND_START_TO_TRACK; t += MILLISECONDS_AROUND_START_TO_TRACK) {
|
||||
final Wind directionFromStartToNextMark = trackedRace.getDirectionFromStartToNextMark(new MillisecondsTimePoint(t));
|
||||
if (directionFromStartToNextMark != null) {
|
||||
result.add(directionFromStartToNextMark);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
result = empty;
|
||||
|
||||
+4
-2
@@ -4,6 +4,7 @@ import java.util.Collections;
|
||||
import java.util.Map;
|
||||
|
||||
import com.sap.sailing.domain.common.WindSource;
|
||||
import com.sap.sailing.domain.common.WindSourceType;
|
||||
import com.sap.sailing.domain.tracking.TrackedEvent;
|
||||
import com.sap.sailing.domain.tracking.TrackedRace;
|
||||
import com.sap.sailing.domain.tracking.WindStore;
|
||||
@@ -17,9 +18,10 @@ public class EmptyWindStore implements WindStore {
|
||||
long millisecondsOverWhichToAverage) {
|
||||
switch (windSource.getType()) {
|
||||
case COURSE_BASED:
|
||||
return new CourseBasedWindTrackImpl(trackedRace, millisecondsOverWhichToAverage);
|
||||
return new CourseBasedWindTrackImpl(trackedRace, millisecondsOverWhichToAverage, WindSourceType.COURSE_BASED.getBaseConfidence());
|
||||
case TRACK_BASED_ESTIMATION:
|
||||
return new TrackBasedEstimationWindTrackImpl(trackedRace, millisecondsOverWhichToAverage);
|
||||
return new TrackBasedEstimationWindTrackImpl(trackedRace, millisecondsOverWhichToAverage,
|
||||
WindSourceType.TRACK_BASED_ESTIMATION.getBaseConfidence());
|
||||
default:
|
||||
return new WindTrackImpl(millisecondsOverWhichToAverage);
|
||||
}
|
||||
|
||||
+2
-2
@@ -61,8 +61,8 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl impl
|
||||
*/
|
||||
private final HashSet<TimePoint> timePointsWithCachedNullResultFastContains;
|
||||
|
||||
public TrackBasedEstimationWindTrackImpl(TrackedRace trackedRace, long millisecondsOverWhichToAverage) {
|
||||
super(trackedRace, millisecondsOverWhichToAverage);
|
||||
public TrackBasedEstimationWindTrackImpl(TrackedRace trackedRace, long millisecondsOverWhichToAverage, double baseConfidence) {
|
||||
super(trackedRace, millisecondsOverWhichToAverage, baseConfidence);
|
||||
virtualInternalRawFixes = new EstimatedWindFixesAsNavigableSet(trackedRace);
|
||||
trackedRace.addListener(this);
|
||||
this.timePointsWithCachedNullResult = new ArrayListNavigableSet<TimePoint>(AbstractTimePoint.TIMEPOINT_COMPARATOR);
|
||||
|
||||
+10
-8
@@ -161,20 +161,22 @@ public class TrackedLegImpl implements TrackedLeg, RaceChangeListener {
|
||||
}
|
||||
|
||||
private Wind getWindOnLeg(TimePoint at) {
|
||||
Wind wind;
|
||||
Position approximateLegStartPosition = getTrackedRace().getOrCreateTrack(
|
||||
getLeg().getFrom().getBuoys().iterator().next()).getEstimatedPosition(at, false);
|
||||
Position approximateLegEndPosition = getTrackedRace().getOrCreateTrack(
|
||||
getLeg().getTo().getBuoys().iterator().next()).getEstimatedPosition(at, false);
|
||||
if (approximateLegStartPosition == null || approximateLegEndPosition == null) {
|
||||
throw new RuntimeException("No mark positions received yet for leg "+getLeg()+
|
||||
". Can't determine wind direction since position is not known.");
|
||||
wind = null;
|
||||
} else {
|
||||
// exclude track-based estimation; it is itself based on the leg type which is based on the getWindOnLeg
|
||||
// result which
|
||||
// would therefore lead to an endless recursion without further tricks being applied
|
||||
wind = getWind(approximateLegStartPosition.translateGreatCircle(
|
||||
approximateLegStartPosition.getBearingGreatCircle(approximateLegEndPosition),
|
||||
approximateLegStartPosition.getDistance(approximateLegEndPosition).scale(0.5)), at,
|
||||
getTrackedRace().getWindSources(WindSourceType.TRACK_BASED_ESTIMATION));
|
||||
}
|
||||
// exclude track-based estimation; it is itself based on the leg type which is based on the getWindOnLeg result which
|
||||
// would therefore lead to an endless recursion without further tricks being applied
|
||||
Wind wind = getWind(
|
||||
approximateLegStartPosition.translateGreatCircle(approximateLegStartPosition.getBearingGreatCircle(approximateLegEndPosition),
|
||||
approximateLegStartPosition.getDistance(approximateLegEndPosition).scale(0.5)), at,
|
||||
getTrackedRace().getWindSources(WindSourceType.TRACK_BASED_ESTIMATION));
|
||||
return wind;
|
||||
}
|
||||
|
||||
|
||||
+27
-17
@@ -134,7 +134,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
*/
|
||||
private final Map<WindSource, WindTrack> windTracks;
|
||||
|
||||
private Wind directionFromStartToNextMarkCache;
|
||||
private final Map<TimePoint, Wind> directionFromStartToNextMarkCache;
|
||||
|
||||
private final Map<Buoy, GPSFixTrack<Buoy, GPSFix>> buoyTracks;
|
||||
|
||||
@@ -152,6 +152,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
this.updateCount = 0;
|
||||
this.race = race;
|
||||
this.windStore = windStore;
|
||||
this.directionFromStartToNextMarkCache = new HashMap<TimePoint, Wind>();
|
||||
this.millisecondsOverWhichToAverageSpeed = millisecondsOverWhichToAverageSpeed;
|
||||
this.millisecondsOverWhichToAverageWind = millisecondsOverWhichToAverageWind;
|
||||
this.startToNextMarkCacheInvalidationListeners = new HashMap<Buoy, TrackedRaceImpl.StartToNextMarkCacheInvalidationListener>();
|
||||
@@ -523,7 +524,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
public WindTrack getOrCreateWindTrack(WindSource windSource) {
|
||||
WindTrack result;
|
||||
if (windSource.getType() == WindSourceType.COMBINED) {
|
||||
result = new CombinedWindTrackImpl(this);
|
||||
result = new CombinedWindTrackImpl(this, WindSourceType.COMBINED.getBaseConfidence());
|
||||
} else {
|
||||
synchronized (windTracks) {
|
||||
result = windTracks.get(windSource);
|
||||
@@ -591,18 +592,21 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
|
||||
@Override
|
||||
public Wind getDirectionFromStartToNextMark(TimePoint at) {
|
||||
Wind result = directionFromStartToNextMarkCache;
|
||||
if (result == null) {
|
||||
Leg firstLeg = getRace().getCourse().getLegs().iterator().next();
|
||||
Position firstLegEnd = getApproximatePosition(firstLeg.getTo(), at);
|
||||
Position firstLegStart = getApproximatePosition(firstLeg.getFrom(), at);
|
||||
if (firstLegStart != null && firstLegEnd != null) {
|
||||
result = new WindImpl(firstLegStart, at, new KnotSpeedWithBearingImpl(1.0,
|
||||
firstLegEnd.getBearingGreatCircle(firstLegStart)));
|
||||
final Wind finalResult = result;
|
||||
directionFromStartToNextMarkCache = finalResult;
|
||||
} else {
|
||||
result = null;
|
||||
Wind result;
|
||||
synchronized (directionFromStartToNextMarkCache) {
|
||||
result = directionFromStartToNextMarkCache.get(at);
|
||||
if (result == null) {
|
||||
Leg firstLeg = getRace().getCourse().getLegs().iterator().next();
|
||||
Position firstLegEnd = getApproximatePosition(firstLeg.getTo(), at);
|
||||
Position firstLegStart = getApproximatePosition(firstLeg.getFrom(), at);
|
||||
if (firstLegStart != null && firstLegEnd != null) {
|
||||
result = new WindImpl(firstLegStart, at, new KnotSpeedWithBearingImpl(1.0,
|
||||
firstLegEnd.getBearingGreatCircle(firstLegStart)));
|
||||
final Wind finalResult = result;
|
||||
directionFromStartToNextMarkCache.put(at, finalResult);
|
||||
} else {
|
||||
result = null;
|
||||
}
|
||||
}
|
||||
}
|
||||
return result;
|
||||
@@ -686,7 +690,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
if (zeroBasedIndex < 2) {
|
||||
// the observing listener on any previous buoy will be GCed; we need to ensure
|
||||
// that the cache is recomputed
|
||||
directionFromStartToNextMarkCache = null;
|
||||
clearDirectionFromStartToNextMarkCache();
|
||||
Iterator<Waypoint> waypointsIter = getRace().getCourse().getWaypoints().iterator();
|
||||
waypointsIter.next(); // skip first
|
||||
if (waypointsIter.hasNext()) {
|
||||
@@ -700,6 +704,12 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
addStartToNextMarkCacheInvalidationListener(waypointThatGotAdded);
|
||||
}
|
||||
|
||||
private void clearDirectionFromStartToNextMarkCache() {
|
||||
synchronized (directionFromStartToNextMarkCache) {
|
||||
directionFromStartToNextMarkCache.clear();
|
||||
}
|
||||
}
|
||||
|
||||
private void addStartToNextMarkCacheInvalidationListener(Waypoint waypoint) {
|
||||
for (Buoy buoy : waypoint.getBuoys()) {
|
||||
addStartToNextMarkCacheInvalidationListener(buoy);
|
||||
@@ -756,7 +766,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
if (zeroBasedIndex < 2) {
|
||||
// the observing listener on any previous buoy will be GCed; we need to ensure
|
||||
// that the cache is recomputed
|
||||
directionFromStartToNextMarkCache = null;
|
||||
clearDirectionFromStartToNextMarkCache();
|
||||
stopAndRemoveStartToNextMarkCacheInvalidationListener(waypointThatGotRemoved);
|
||||
Iterator<Waypoint> waypointsIter = getRace().getCourse().getWaypoints().iterator();
|
||||
waypointsIter.next(); // skip first
|
||||
@@ -1253,7 +1263,7 @@ public abstract class TrackedRaceImpl implements TrackedRace, CourseListener {
|
||||
|
||||
@Override
|
||||
public void gpsFixReceived(GPSFix fix, Buoy buoy) {
|
||||
directionFromStartToNextMarkCache = null;
|
||||
clearDirectionFromStartToNextMarkCache();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
+17
-13
@@ -74,7 +74,7 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
|
||||
protected TimePoint lowerToResolution(Wind w) {
|
||||
TimePoint result;
|
||||
final TimePoint timePointOfLastEvent = trackedRace.getTimePointOfLastEvent();
|
||||
final TimePoint timePointOfLastEvent = getTrackedRace().getTimePointOfLastEvent();
|
||||
if (timePointOfLastEvent == null) {
|
||||
// nothing received yet; "lowering" to end of time
|
||||
result = new MillisecondsTimePoint((Long.MAX_VALUE - 1) / getResolutionInMilliseconds()
|
||||
@@ -90,7 +90,7 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
|
||||
protected TimePoint floorToResolution(Wind w) {
|
||||
TimePoint result;
|
||||
final TimePoint timePointOfLastEvent = trackedRace.getTimePointOfLastEvent();
|
||||
final TimePoint timePointOfLastEvent = getTrackedRace().getTimePointOfLastEvent();
|
||||
if (timePointOfLastEvent == null) {
|
||||
// nothing received yet; "lowering" to end of time
|
||||
result = new MillisecondsTimePoint((Long.MAX_VALUE - 1) / getResolutionInMilliseconds()
|
||||
@@ -106,7 +106,7 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
|
||||
protected TimePoint ceilingToResolution(Wind w) {
|
||||
TimePoint result;
|
||||
final TimePoint startOfTracking = trackedRace.getStartOfTracking();
|
||||
final TimePoint startOfTracking = getTrackedRace().getStartOfTracking();
|
||||
if (startOfTracking == null) {
|
||||
// no start of tracking yet; "ceiling" to beginning of time
|
||||
result = new MillisecondsTimePoint(0);
|
||||
@@ -121,7 +121,7 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
|
||||
protected TimePoint higherToResolution(Wind w) {
|
||||
TimePoint result;
|
||||
final TimePoint startOfTracking = trackedRace.getStartOfTracking();
|
||||
final TimePoint startOfTracking = getTrackedRace().getStartOfTracking();
|
||||
if (startOfTracking == null) {
|
||||
// no start of tracking yet; "ceiling" to beginning of time
|
||||
result = new MillisecondsTimePoint(0);
|
||||
@@ -141,9 +141,9 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
* will be returned instead. If no valid start time can be obtained from the race, <code>Long.MAX_VALUE</code> is
|
||||
* returned instead.
|
||||
*/
|
||||
private TimePoint getFrom() {
|
||||
return from == null ? trackedRace.getStart() == null ? new MillisecondsTimePoint(Long.MAX_VALUE)
|
||||
: floorToResolution(new DummyWind(trackedRace.getStart())) : from;
|
||||
protected TimePoint getFrom() {
|
||||
return from == null ? getTrackedRace().getStart() == null ? new MillisecondsTimePoint(Long.MAX_VALUE)
|
||||
: floorToResolution(new DummyWind(getTrackedRace().getStart())) : from;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -153,9 +153,9 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
* {@link #ceilingToResolution(Wind) ceiled to the resolution of this set} will be returned instead. If no valid
|
||||
* time of a newest event can be obtained from the race, <code>MillisecondsTimePoint(1)</code> is returned instead.
|
||||
*/
|
||||
private TimePoint getTo() {
|
||||
return to == null ? trackedRace.getTimePointOfNewestEvent() == null ? new MillisecondsTimePoint(1)
|
||||
: ceilingToResolution(new DummyWind(trackedRace.getTimePointOfNewestEvent())) : to;
|
||||
protected TimePoint getTo() {
|
||||
return getToInternal() == null ? getTrackedRace().getTimePointOfNewestEvent() == null ? new MillisecondsTimePoint(1)
|
||||
: ceilingToResolution(new DummyWind(getTrackedRace().getTimePointOfNewestEvent())) : getToInternal();
|
||||
}
|
||||
|
||||
@Override
|
||||
@@ -242,7 +242,7 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
|
||||
@Override
|
||||
public NavigableSet<Wind> subSet(Wind fromElement, boolean fromInclusive, Wind toElement, boolean toInclusive) {
|
||||
return createSubset(track, trackedRace, fromInclusive ? ceilingToResolution(fromElement)
|
||||
return createSubset(track, getTrackedRace(), fromInclusive ? ceilingToResolution(fromElement)
|
||||
: higherToResolution(fromElement), toInclusive ? floorToResolution(toElement)
|
||||
: lowerToResolution(toElement));
|
||||
}
|
||||
@@ -254,13 +254,13 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
|
||||
@Override
|
||||
public NavigableSet<Wind> headSet(Wind toElement, boolean inclusive) {
|
||||
return createSubset(track, trackedRace, /* from */ null,
|
||||
return createSubset(track, getTrackedRace(), /* from */ null,
|
||||
inclusive ? ceilingToResolution(toElement) : lowerToResolution(toElement));
|
||||
}
|
||||
|
||||
@Override
|
||||
public NavigableSet<Wind> tailSet(Wind fromElement, boolean inclusive) {
|
||||
return createSubset(track, trackedRace, inclusive ? floorToResolution(fromElement)
|
||||
return createSubset(track, getTrackedRace(), inclusive ? floorToResolution(fromElement)
|
||||
: higherToResolution(fromElement),
|
||||
/* to */ null);
|
||||
}
|
||||
@@ -335,5 +335,9 @@ public abstract class VirtualWindFixesAsNavigableSet extends AbstractUnmodifiabl
|
||||
T[] tResult = (T[]) result;
|
||||
return tResult;
|
||||
}
|
||||
|
||||
protected TimePoint getToInternal() {
|
||||
return to;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -20,8 +20,8 @@ import com.sap.sailing.domain.tracking.WindWithConfidence;
|
||||
public abstract class VirtualWindTrackImpl extends WindTrackImpl {
|
||||
private final TrackedRace trackedRace;
|
||||
|
||||
protected VirtualWindTrackImpl(TrackedRace trackedRace, long millisecondsOverWhichToAverage) {
|
||||
super(millisecondsOverWhichToAverage);
|
||||
protected VirtualWindTrackImpl(TrackedRace trackedRace, long millisecondsOverWhichToAverage, double baseConfidence) {
|
||||
super(millisecondsOverWhichToAverage, baseConfidence);
|
||||
this.trackedRace = trackedRace;
|
||||
}
|
||||
|
||||
|
||||
+14
-1
@@ -37,11 +37,24 @@ import com.sap.sailing.util.impl.ArrayListNavigableSet;
|
||||
public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
||||
private final static Logger logger = Logger.getLogger(WindTrackImpl.class.getName());
|
||||
|
||||
private final static double DEFAULT_BASE_CONFIDENCE = 0.9;
|
||||
|
||||
private final double baseConfidence;
|
||||
|
||||
private long millisecondsOverWhichToAverage;
|
||||
private final Set<WindListener> listeners;
|
||||
|
||||
public WindTrackImpl(long millisecondsOverWhichToAverage) {
|
||||
this(millisecondsOverWhichToAverage, DEFAULT_BASE_CONFIDENCE);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param baseConfidence
|
||||
* the confidence to attribute to the raw wind fixes in this track
|
||||
*/
|
||||
public WindTrackImpl(long millisecondsOverWhichToAverage, double baseConfidence) {
|
||||
super(new ArrayListNavigableSet<Timed>(WindComparator.INSTANCE));
|
||||
this.baseConfidence = baseConfidence;
|
||||
this.millisecondsOverWhichToAverage = millisecondsOverWhichToAverage;
|
||||
listeners = new HashSet<WindListener>();
|
||||
}
|
||||
@@ -209,7 +222,7 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
|
||||
* represent <em>the truth</em>. 0.0 means "no relevance at all."
|
||||
*/
|
||||
private double getBaseConfidence() {
|
||||
return /* confidence */ 0.9;
|
||||
return baseConfidence;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
||||
@@ -129,3 +129,6 @@ body, table td, select, button {
|
||||
margin-top: 7px;
|
||||
}
|
||||
|
||||
.errorLabel {
|
||||
color: #FF0000;
|
||||
}
|
||||
|
||||
@@ -247,4 +247,8 @@ input.magnifier {
|
||||
|
||||
.breadcrumbPanel-NextArrow {
|
||||
display: none;
|
||||
}
|
||||
}
|
||||
|
||||
.errorLabel {
|
||||
color: #FF0000;
|
||||
}
|
||||
|
||||
@@ -343,3 +343,7 @@ input.magnifier {
|
||||
background: #c0bebe;
|
||||
font-family: 'UbuntuRegular', Arial, Verdana, sans-serif
|
||||
}
|
||||
|
||||
.errorLabel {
|
||||
color: #FF0000;
|
||||
}
|
||||
|
||||
+22
-1
@@ -20,6 +20,8 @@ import org.moxieapps.gwt.highcharts.client.ToolTipData;
|
||||
import org.moxieapps.gwt.highcharts.client.ToolTipFormatter;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartClickEvent;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartClickEventHandler;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEvent;
|
||||
import org.moxieapps.gwt.highcharts.client.events.ChartSelectionEventHandler;
|
||||
import org.moxieapps.gwt.highcharts.client.plotOptions.LinePlotOptions;
|
||||
import org.moxieapps.gwt.highcharts.client.plotOptions.Marker;
|
||||
|
||||
@@ -61,6 +63,7 @@ public class WindChart extends SimplePanel implements Component<WindChartSetting
|
||||
private final ErrorReporter errorReporter;
|
||||
private final SailingServiceAsync sailingService;
|
||||
private final Chart chart;
|
||||
private boolean ignoreClickOnce;
|
||||
private final Timer timer;
|
||||
private final DateTimeFormat dateFormat = DateTimeFormat.getFormat("HH:mm:ss");
|
||||
|
||||
@@ -114,10 +117,28 @@ public class WindChart extends SimplePanel implements Component<WindChartSetting
|
||||
chart.setClickEventHandler(new ChartClickEventHandler() {
|
||||
@Override
|
||||
public boolean onClick(ChartClickEvent chartClickEvent) {
|
||||
WindChart.this.timer.setTime(chartClickEvent.getXAxisValueAsLong());
|
||||
if (ignoreClickOnce) {
|
||||
ignoreClickOnce = false;
|
||||
} else {
|
||||
WindChart.this.timer.setTime(chartClickEvent.getXAxisValueAsLong());
|
||||
}
|
||||
return true;
|
||||
}
|
||||
});
|
||||
chart.setSelectionEventHandler(new ChartSelectionEventHandler() {
|
||||
@Override
|
||||
public boolean onSelection(ChartSelectionEvent chartSelectionEvent) {
|
||||
try {
|
||||
chartSelectionEvent.getXAxisMaxAsLong();
|
||||
chartSelectionEvent.getXAxisMinAsLong();
|
||||
ignoreClickOnce = true;
|
||||
return true;
|
||||
} catch (Throwable t) {
|
||||
chart.redraw();
|
||||
return true;
|
||||
}
|
||||
}
|
||||
});
|
||||
chart.getXAxis().setType(Axis.Type.DATE_TIME).setMaxZoom(10000) // ten seconds
|
||||
.setAxisTitleText(stringMessages.time());
|
||||
chart.getYAxis(0).setAxisTitleText(stringMessages.fromDeg()).setStartOnTick(false).setShowFirstLabel(false);
|
||||
|
||||
Reference in New Issue
Block a user