Merge remote-tracking branch 'origin/master'

This commit is contained in:
Simon Pamies committed 2015-03-14 23:32:04 +04:00
commit 861ff55778
14 files changed
+164 -73

No files matched your search

@@ -40,7 +40,9 @@ public enum MaxPointsReason {
/** Discretionary Penalty Imposed by the race committee */
DPI(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ false),
/** Retired */
RET(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true);
RET(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true),
/** Uniform Flag Disqualification */
UFD(/* discardable */ true, /* advanceCompetitorsTrackedWorse */ true);
private final boolean discardable;
@@ -63,4 +65,4 @@ public enum MaxPointsReason {
public boolean isAdvanceCompetitorsTrackedWorse() {
return advanceCompetitorsTrackedWorse;
}
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.domain.tracking;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
@@ -39,4 +40,23 @@ public interface WindTrack extends DynamicTrack<Wind> {
void setMillisecondsOverWhichToAverage(long millisecondsOverWhichToAverage);
long getMillisecondsOverWhichToAverageWind();
/**
* The {@link #getAveragedWindWithConfidence(Position, TimePoint)} method will usually try to produce a result even
* if this track has only fixes that are spatially or time-wise not close to the position and time point requested,
* having a lower confidence as an effect.
* <p>
*
* However, some wind tracks may choose to return
* <code>null<code> from their {@link #getAveragedWindWithConfidence(Position, TimePoint)}
* implementation if no fix can be produced or found within a certain range around the time point requested. If
* such a time tick resolution exists, this method will tell the duration between two time ticks. Clients can
* use this to determine how they should treat fixes in order to achieve far-reaching semantic consistency
* with the server side.<p>
*
* For example, if a client-side wind track records fixes coming from this track at a certain frequency,
* the client can determine based on the result of this method whether the server had a value in the area
* requested or not.
*/
Duration getResolutionOutsideOfWhichNoFixWillBeReturned();
}
@@ -1033,8 +1033,8 @@ public class GPSFixTrackImpl<ItemType, FixType extends GPSFix> extends TrackImpl
while (!result && next.compareTo(end) <= 0) {
SpeedWithBearing estimatedSpeedAtNext = getEstimatedSpeed(next);
if (estimatedSpeedAtNext != null) {
Bearing bearingAtEnd = estimatedSpeedAtNext.getBearing();
result = Math.abs(bearingAtStart.getDifferenceTo(bearingAtEnd).getDegrees()) > minimumDegreeDifference;
Bearing bearingAtNext = estimatedSpeedAtNext.getBearing();
result = Math.abs(bearingAtStart.getDifferenceTo(bearingAtNext).getDegrees()) > minimumDegreeDifference;
}
next = new MillisecondsTimePoint(next.asMillis()
+ Math.max(1000l, getMillisecondsOverWhichToAverageSpeed() / 2));
@@ -33,6 +33,7 @@ import com.sap.sailing.domain.tracking.TrackedRaceStatus;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
@@ -77,6 +78,8 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl {
private static final long serialVersionUID = -4397496421917807499L;
private static final SpeedWithBearing defaultSpeedWithBearing = new KnotSpeedWithBearingImpl(0, new DegreeBearingImpl(0));
private static Duration RESOLUTION = Duration.ONE_SECOND;
private final EstimatedWindFixesAsNavigableSet virtualInternalRawFixes;
@@ -788,7 +791,7 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl {
*/
private EstimatedWindFixesAsNavigableSet(TrackedRace trackedRace,
TimePoint from, TimePoint to) {
super(TrackBasedEstimationWindTrackImpl.this, trackedRace, from, to, /* resolution in milliseconds */ 1000l);
super(TrackBasedEstimationWindTrackImpl.this, trackedRace, from, to, RESOLUTION.asMillis());
}
@Override
@@ -815,4 +818,10 @@ public class TrackBasedEstimationWindTrackImpl extends VirtualWindTrackImpl {
public void windDataReceived(WindImpl wind, WindSource realWindSource) {
listener.windDataReceived(wind, realWindSource);
}
@Override
public Duration getResolutionOutsideOfWhichNoFixWillBeReturned() {
return RESOLUTION;
}
}
@@ -25,6 +25,7 @@ import com.sap.sailing.domain.tracking.Wind;
import com.sap.sailing.domain.tracking.WindListener;
import com.sap.sailing.domain.tracking.WindTrack;
import com.sap.sailing.domain.tracking.WindWithConfidence;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
import com.sap.sse.common.Util;
import com.sap.sse.util.impl.ArrayListNavigableSet;
@@ -79,6 +80,15 @@ public class WindTrackImpl extends TrackImpl<Wind> implements WindTrack {
listeners = new HashSet<WindListener>();
}
/**
* By default, wind tracks respond with a valid result to {@link #getAveragedWindWithConfidence(Position, TimePoint)},
* so <code>null</code> is the result of this default implementation.
*/
@Override
public Duration getResolutionOutsideOfWhichNoFixWillBeReturned() {
return null;
}
@Override
protected Wind getDummyFix(TimePoint timePoint) {
return new DummyWind(timePoint);
+2 -2
View File
@@ -477,11 +477,11 @@ img.openColumn {
}
.TimePanel-ShowExtended-Button-Open {
background: white url(images/arrows_top_black.png) center no-repeat;
background: white url(images/arrows_bottom_black.png) center no-repeat;
}
.TimePanel-ShowExtended-Button-Closed {
background: white url(images/arrows_bottom_black.png) center no-repeat;
background: white url(images/arrows_top_black.png) center no-repeat;
}
.TimePanel-ShowExtended-Button:hover {
@@ -58,11 +58,11 @@ public class MediaSingleSelectionControl extends AbstractMediaSelectionControl i
private Button createMediaEntry(final MediaTrack mediaTrack) {
Button mediaSelectButton = new Button(mediaTrack.title);
mediaSelectButton.setStyleName("Media-Select-Button");
if (mediaPlayerManager.getPlayingVideoTracks().contains(mediaTrack)) {
if (mediaTrack.equals(mediaPlayerManager.getPlayingAudioTrack()) || mediaPlayerManager.getPlayingVideoTracks().contains(mediaTrack)) {
mediaSelectButton.setTitle(stringMessages.mediaHideVideoTooltip());
mediaSelectButton.addStyleName("Media-Select-Button-playing");
mediaSelectButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (mediaPlayerManager.getPlayingAudioTrack() == mediaTrack) {
@@ -75,19 +75,14 @@ public class MediaSingleSelectionControl extends AbstractMediaSelectionControl i
} else {
mediaSelectButton.setTitle(stringMessages.mediaShowVideoTooltip(mediaTrack.title));
mediaSelectButton.addClickHandler(new ClickHandler() {
@Override
public void onClick(ClickEvent event) {
if (event.isControlKeyDown() && mediaTrack.mimeType.mediaType == MediaTrack.MediaType.video) {
if (mediaTrack.mimeType.mediaType == MediaTrack.MediaType.video) {
mediaPlayerManager.playFloatingVideo(mediaTrack);
} else if (mediaPlayerManager.getPlayingAudioTrack() != mediaTrack) {
mediaPlayerManager.stopAll();
if (mediaTrack.mimeType.mediaType == MediaTrack.MediaType.video) {
mediaPlayerManager.playFloatingVideo(mediaTrack);
mediaPlayerManager.playAudio(mediaTrack);
} else if (mediaTrack.mimeType.mediaType == MediaTrack.MediaType.audio) {
mediaPlayerManager.playAudio(mediaTrack);
}
}
if (mediaTrack.mimeType.mediaType == MediaTrack.MediaType.audio || mediaPlayerManager.getPlayingAudioTrack() == null) {
mediaPlayerManager.playAudio(mediaTrack);
}
hide();
};
@@ -1060,6 +1060,7 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
boolean isVisibleLeaderInfoComplete = false;
boolean isLegTypeKnown = false;
WindTrackInfoDTO windDataForLegMiddle = null;
LegInfoDTO legInfoDTO = null;
if (visibleLeaderInfo != null
&& visibleLeaderInfo.getA() > 0
&& visibleLeaderInfo.getA() <= lastRaceTimesInfo.getLegInfos().size()
@@ -1068,14 +1069,13 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
.getCombinedWindOnLegMiddle(visibleLeaderInfo.getA() - 1)) != null
&& !windDataForLegMiddle.windFixes.isEmpty()) {
isVisibleLeaderInfoComplete = true;
LegInfoDTO legInfoDTO = lastRaceTimesInfo.getLegInfos().get(visibleLeaderInfo.getA() - 1);
legInfoDTO = lastRaceTimesInfo.getLegInfos().get(visibleLeaderInfo.getA() - 1);
if (legInfoDTO.legType != null) {
isLegTypeKnown = true;
}
lastBoatFix = getBoatFix(visibleLeaderInfo.getB(), date);
}
if (isVisibleLeaderInfoComplete && isLegTypeKnown && lastBoatFix != null && lastBoatFix.speedWithBearing != null) {
LegInfoDTO legInfoDTO = lastRaceTimesInfo.getLegInfos().get(visibleLeaderInfo.getA() - 1);
double advantageLineLengthInKm = 1.0; // TODO this should probably rather scale with the visible
// area of the map; bug 616
double distanceFromBoatPositionInKm = visibleLeaderInfo.getB().getBoatClass()
@@ -1133,11 +1133,12 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
windSourcesToDeliver.add(new WindSourceImpl(WindSourceType.WEB));
}
for (WindSource windSource : windSourcesToDeliver) {
if(windSource.getType() == WindSourceType.WEB) {
if (windSource.getType() == WindSourceType.WEB) {
WindTrackInfoDTO windTrackInfoDTO = new WindTrackInfoDTO();
windTrackInfoDTO.windFixes = new ArrayList<WindDTO>();
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
windTrackInfoDTO.resolutionOutsideOfWhichNoFixWillBeReturned = windTrack
.getResolutionOutsideOfWhichNoFixWillBeReturned();
windTrack.lockForRead();
try {
Iterator<Wind> windIter = windTrack.getRawFixes().iterator();
@@ -1151,7 +1152,6 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
} finally {
windTrack.unlockAfterRead();
}
windTrackInfoDTOs.put(windSource, windTrackInfoDTO);
}
}
@@ -1238,6 +1238,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
windTrackInfoDTO.windFixes = new ArrayList<WindDTO>();
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
windTrackInfoDTOs.put(windSource, windTrackInfoDTO);
windTrackInfoDTO.resolutionOutsideOfWhichNoFixWillBeReturned = windTrack
.getResolutionOutsideOfWhichNoFixWillBeReturned();
windTrackInfoDTO.dampeningIntervalInMilliseconds = windTrack
.getMillisecondsOverWhichToAverageWind();
TimePoint timePoint = fromTimePoint;
@@ -1352,6 +1354,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
PositionAtTimeProvider positionProvider) {
WindTrack windTrack = trackedRace.getOrCreateWindTrack(windSource);
WindTrackInfoDTO windTrackInfoDTO = new WindTrackInfoDTO();
windTrackInfoDTO.resolutionOutsideOfWhichNoFixWillBeReturned = windTrack.getResolutionOutsideOfWhichNoFixWillBeReturned();
windTrackInfoDTO.windFixes = new ArrayList<WindDTO>();
windTrackInfoDTO.dampeningIntervalInMilliseconds = windTrack.getMillisecondsOverWhichToAverageWind();
TimePoint timePoint = from;
@@ -3,6 +3,9 @@ package com.sap.sailing.gwt.ui.shared;
import java.util.List;
import com.google.gwt.user.client.rpc.IsSerializable;
import com.sap.sailing.domain.common.Position;
import com.sap.sse.common.Duration;
import com.sap.sse.common.TimePoint;
public class WindTrackInfoDTO implements IsSerializable {
public List<WindDTO> windFixes;
@@ -10,5 +13,24 @@ public class WindTrackInfoDTO implements IsSerializable {
public double minWindConfidence;
public double maxWindConfidence;
/**
* The {@link #getAveragedWindWithConfidence(Position, TimePoint)} method will usually try to produce a result even
* if this track has only fixes that are spatially or time-wise not close to the position and time point requested,
* having a lower confidence as an effect.
* <p>
*
* However, some wind tracks may choose to return
* <code>null<code> from their {@link #getAveragedWindWithConfidence(Position, TimePoint)}
* implementation if no fix can be produced or found within a certain range around the time point requested. If
* such a time tick resolution exists, this method will tell the duration between two time ticks. Clients can
* use this to determine how they should treat fixes in order to achieve far-reaching semantic consistency
* with the server side.<p>
*
* For example, if a client-side wind track records fixes coming from this track at a certain frequency,
* the client can determine based on the result of this method whether the server had a value in the area
* requested or not.
*/
public Duration resolutionOutsideOfWhichNoFixWillBeReturned;
public WindTrackInfoDTO() {}
}
@@ -190,16 +190,18 @@ public class WindStreamletsRaceboardOverlay extends MovingCanvasOverlay {
// confidences
for (Entry<WindSource, WindTrackInfoDTO> e : result.windTrackInfoByWindSource.entrySet()) {
WindTrackInfoDTO windTrackForSource = windInfoForRace.windTrackInfoByWindSource.get(e.getKey());
final WindTrackInfoDTO resultWindTrackInfoDTO = result.windTrackInfoByWindSource.get(e.getKey());
windTrackForSource.resolutionOutsideOfWhichNoFixWillBeReturned = resultWindTrackInfoDTO.resolutionOutsideOfWhichNoFixWillBeReturned;
if (windTrackForSource.windFixes == null) {
windTrackForSource.windFixes = result.windTrackInfoByWindSource.get(e.getKey()).windFixes;
windTrackForSource.windFixes = resultWindTrackInfoDTO.windFixes;
} else {
windTrackForSource.windFixes.addAll(result.windTrackInfoByWindSource.get(e.getKey()).windFixes);
windTrackForSource.windFixes.addAll(resultWindTrackInfoDTO.windFixes);
}
if (result.windTrackInfoByWindSource.get(e.getKey()).maxWindConfidence > windTrackForSource.maxWindConfidence) {
windTrackForSource.maxWindConfidence = result.windTrackInfoByWindSource.get(e.getKey()).maxWindConfidence;
if (resultWindTrackInfoDTO.maxWindConfidence > windTrackForSource.maxWindConfidence) {
windTrackForSource.maxWindConfidence = resultWindTrackInfoDTO.maxWindConfidence;
}
if (result.windTrackInfoByWindSource.get(e.getKey()).minWindConfidence < windTrackForSource.minWindConfidence) {
windTrackForSource.minWindConfidence = result.windTrackInfoByWindSource.get(e.getKey()).minWindConfidence;
if (resultWindTrackInfoDTO.minWindConfidence < windTrackForSource.minWindConfidence) {
windTrackForSource.minWindConfidence = resultWindTrackInfoDTO.minWindConfidence;
}
}
}
@@ -6,7 +6,6 @@ import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.confidence.Weigher;
import com.sap.sailing.domain.common.dto.PositionDTO;
import com.sap.sse.common.Util;
/**
* A weigher that uses a {@link PositionDTO} and a {@link Date} to compute a confidence based on space and time
@@ -24,9 +23,8 @@ import com.sap.sse.common.Util;
* @author Axel Uhl (d043530)
*
*/
public class PositionDTOAndDateWeigher implements Weigher<Util.Pair<PositionDTO, Date>> {
public class PositionDTOWeigher implements Weigher<PositionDTO> {
private static final long serialVersionUID = -262428237738496818L;
private final long halfConfidenceAfterMilliseconds;
private final double halfConfidenceDistanceNauticalMiles;
private final AverageLatitudeProvider averageLatitudeDegProvider;
@@ -35,9 +33,7 @@ public class PositionDTOAndDateWeigher implements Weigher<Util.Pair<PositionDTO,
double getCosineOfAverageLatitude();
}
public PositionDTOAndDateWeigher(long halfConfidenceAfterMilliseconds, Distance halfConfidenceDistance,
AverageLatitudeProvider averageLatitudeDegProvider) {
this.halfConfidenceAfterMilliseconds = halfConfidenceAfterMilliseconds;
public PositionDTOWeigher(Distance halfConfidenceDistance, AverageLatitudeProvider averageLatitudeDegProvider) {
this.halfConfidenceDistanceNauticalMiles = halfConfidenceDistance.getNauticalMiles();
this.averageLatitudeDegProvider = averageLatitudeDegProvider;
}
@@ -47,17 +43,10 @@ public class PositionDTOAndDateWeigher implements Weigher<Util.Pair<PositionDTO,
}
@Override
public double getConfidence(Util.Pair<PositionDTO, Date> fix, Util.Pair<PositionDTO, Date> request) {
final double timeConfidence;
{
double x = Math.abs(fix.getB().getTime() - request.getB().getTime());
double c = halfConfidenceAfterMilliseconds;
double y = halfConfidenceAfterMilliseconds;
timeConfidence = c / (x + y);
}
public double getConfidence(PositionDTO fix, PositionDTO request) {
final double distanceConfidence;
if (fix.getA() != null && request.getA() != null) {
double x = getApproximateNauticalMileDistance(fix.getA(), request.getA());
if (fix != null && request != null) {
double x = getApproximateNauticalMileDistance(fix, request);
double c = halfConfidenceDistanceNauticalMiles;
double y = c;
distanceConfidence = c / (x + y);
@@ -65,7 +54,7 @@ public class PositionDTOAndDateWeigher implements Weigher<Util.Pair<PositionDTO,
distanceConfidence = 0.0001; // if we have no information about the position, let's not distort an
// otherwise spatially-resolved field
}
return distanceConfidence * timeConfidence;
return distanceConfidence;
}
private double getApproximateNauticalMileDistance(PositionDTO p1, PositionDTO p2) {
@@ -21,9 +21,8 @@ import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.gwt.ui.shared.WindDTO;
import com.sap.sailing.gwt.ui.shared.WindInfoForRaceDTO;
import com.sap.sailing.gwt.ui.shared.WindTrackInfoDTO;
import com.sap.sailing.gwt.ui.simulator.streamlets.PositionDTOAndDateWeigher.AverageLatitudeProvider;
import com.sap.sailing.gwt.ui.simulator.streamlets.PositionDTOWeigher.AverageLatitudeProvider;
import com.sap.sse.common.Util;
import com.sap.sse.common.Util.Pair;
/**
* Implements the {@link VectorField} interface by providing real wind data from a <code>TrackedRace</code> which has
@@ -45,15 +44,15 @@ import com.sap.sse.common.Util.Pair;
public class WindInfoForRaceVectorField implements VectorField, AverageLatitudeProvider {
private static final double MAX_WIND_SPEED_IN_KNOTS = 40;
private final Bounds infiniteBounds = new BoundsImpl(new DegreePosition(-90, -180), new DegreePosition(90, 180));
private final Weigher<Pair<PositionDTO, Date>> weigher;
private final Weigher<PositionDTO> weigher;
private double averageLatitudeDeg;
private double averageLatitudeCosine;
private double knotsInDegreePerFrame;
private final Comparator<WindDTO> windByMeasureTimePointComparator = new Comparator<WindDTO>() {
private final Comparator<WindDTO> windByRequestTimePointComparator = new Comparator<WindDTO>() {
@Override
public int compare(WindDTO o1, WindDTO o2) {
return o1.measureTimepoint > o2.measureTimepoint ? 1 : o1.measureTimepoint == o2.measureTimepoint ? 0 : -1;
return o1.requestTimepoint > o2.requestTimepoint ? 1 : o1.requestTimepoint == o2.requestTimepoint ? 0 : -1;
}
};
private final WindInfoForRaceDTO windInfoForRace;
@@ -61,8 +60,7 @@ public class WindInfoForRaceVectorField implements VectorField, AverageLatitudeP
public WindInfoForRaceVectorField(WindInfoForRaceDTO windInfoForRace, double framesPerSecond) {
this.windInfoForRace = windInfoForRace;
this.knotsInDegreePerFrame = 1.0 / (60*3600) / framesPerSecond; // 1kn = 1/60 deg/h = 1/(60*3600) deg/s
weigher = new PositionDTOAndDateWeigher(/* half confidence after milliseconds */3000,
/* halfConfidenceDistance */new MeterDistance(100), this);
weigher = new PositionDTOWeigher(/* halfConfidenceDistance */new MeterDistance(100), this);
}
/**
@@ -90,32 +88,36 @@ public class WindInfoForRaceVectorField implements VectorField, AverageLatitudeP
return true;
}
private final static Position where = new DegreePosition(-33.85741596360764,151.22181591282387);
@Override
public Vector getVector(Position p, Date at) {
final Pair<PositionDTO, Date> request = new Pair<>(new PositionDTO(p.getLatDeg(), p.getLngDeg()), at);
if (where.getDistance(p).getMeters() < 50) {
System.out.println("Here we are");
}
final PositionDTO request = new PositionDTO(p.getLatDeg(), p.getLngDeg());
double speedConfidenceSum = 0;
double knotSpeedSumScaledByConfidence = 0;
final BearingWithConfidenceCluster<Pair<PositionDTO, Date>> bearingCluster = new BearingWithConfidenceCluster<>(weigher);
final BearingWithConfidenceCluster<PositionDTO> bearingCluster = new BearingWithConfidenceCluster<>(weigher);
for (final Entry<WindSource, WindTrackInfoDTO> windSourceAndWindTrack : windInfoForRace.windTrackInfoByWindSource.entrySet()) {
/*if ((windSourceAndWindTrack.getKey().name().equals("RACECOMMITTEE"))||(windSourceAndWindTrack.getKey().name().equals("COURSE_BASED"))) {
continue;
}*/
if (!Util.contains(windInfoForRace.windSourcesToExclude, windSourceAndWindTrack.getKey())) {
WindDTO timewiseClosestFixForWindSource = getTimewiseClosestFix(windSourceAndWindTrack.getValue().windFixes, at);
WindDTO timewiseClosestFixForWindSource = getTimewiseClosestFix(windSourceAndWindTrack.getValue(), at);
if (timewiseClosestFixForWindSource != null) {
Pair<PositionDTO, Date> fix = new Pair<>(timewiseClosestFixForWindSource.position, new Date(
timewiseClosestFixForWindSource.measureTimepoint));
final double confidence = weigher.getConfidence(fix, request);
final double confidence = (timewiseClosestFixForWindSource.confidence == null ? 1 : timewiseClosestFixForWindSource.confidence) *
weigher.getConfidence(timewiseClosestFixForWindSource.position, request);
if (windSourceAndWindTrack.getKey().getType().useSpeed()) {
speedConfidenceSum += confidence;
knotSpeedSumScaledByConfidence += confidence * timewiseClosestFixForWindSource.dampenedTrueWindSpeedInKnots;
}
bearingCluster.add(new BearingWithConfidenceImpl<Util.Pair<PositionDTO, Date>>(
new DegreeBearingImpl(timewiseClosestFixForWindSource.dampenedTrueWindBearingDeg), confidence, fix));
bearingCluster.add(new BearingWithConfidenceImpl<PositionDTO>(
new DegreeBearingImpl(timewiseClosestFixForWindSource.dampenedTrueWindBearingDeg), confidence, timewiseClosestFixForWindSource.position));
}
}
}
final BearingWithConfidence<Pair<PositionDTO, Date>> bearing = bearingCluster.getAverage(request);
final BearingWithConfidence<PositionDTO> bearing = bearingCluster.getAverage(request);
final Vector result;
if (bearing != null && bearing.getObject() != null) {
final double bearingRad = bearing.getObject().getRadians();
@@ -127,30 +129,56 @@ public class WindInfoForRaceVectorField implements VectorField, AverageLatitudeP
return result;
}
private WindDTO getTimewiseClosestFix(List<WindDTO> windFixes, Date at) {
final WindDTO result;
/**
* Regular server-side wind tracks will deliver fixes for queries with a time that is far from any
* of the fixes' time point. A respectively reduced confidence will be the result. However, some wind
* track types such as that for the wind estimation may not deliver a fix, not even with low confidence,
* if at the requested time point (give or take some rounding to a time tick resolution) no fix can be
* provided. In this case, the server will not use any value from such a track when computing an average
* including fixes from several tracks.<p>
*
* To ensure consistent behavior on the client, this method will check if the wind track is marked
* accordingly and will return <code>null</code> if the track cannot provide a fix within its resolution
* around the requested time point.
*/
private WindDTO getTimewiseClosestFix(WindTrackInfoDTO windTrackInfo, Date at) {
List<WindDTO> windFixes = windTrackInfo.windFixes;
final WindDTO preResult;
if (windFixes == null || windFixes.isEmpty()) {
result = null;
preResult = null;
} else {
final WindDTO atDummy = new WindDTO();
atDummy.measureTimepoint = at.getTime();
int pos = Collections.binarySearch(windFixes, atDummy, windByMeasureTimePointComparator);
atDummy.requestTimepoint = at.getTime();
int pos = Collections.binarySearch(windFixes, atDummy, windByRequestTimePointComparator);
if (pos < 0) {
pos = (-pos) - 1; // now pos points at the insertion point
if (pos == 0
|| (pos < windFixes.size() &&
Math.abs(windFixes.get(pos).measureTimepoint - at.getTime()) < Math.abs(windFixes
.get(pos - 1).measureTimepoint - at.getTime()))) {
result = windFixes.get(pos);
Math.abs(windFixes.get(pos).requestTimepoint - at.getTime()) < Math.abs(windFixes
.get(pos - 1).requestTimepoint - at.getTime()))) {
preResult = windFixes.get(pos);
} else {
// pos doesn't point to the first element, nor is the element at pos time-wise closer than the element at pos-1
// or pos points beyond the end of the list
result = windFixes.get(pos-1);
preResult = windFixes.get(pos-1);
}
} else {
result = windFixes.get(pos);
preResult = windFixes.get(pos);
}
}
final WindDTO result;
// don't return wind fixes if according to the wind track there is a resolution to which times are rounded
// and where null is returned when there is no fix at the rounded time point; mimic this behavior here. See
// also bug 2689 comment #13.
if (windTrackInfo.resolutionOutsideOfWhichNoFixWillBeReturned != null) {
if (Math.abs(preResult.requestTimepoint-at.getTime()) > windTrackInfo.resolutionOutsideOfWhichNoFixWillBeReturned.asMillis()) {
result = null;
} else {
result = preResult;
}
} else {
result = preResult;
}
return result;
}
@@ -4,6 +4,17 @@
<div id="mainContent">
<h4 class="articleHeadline">What's New - SAP Sailing Analytics</h4>
<div class="innerContent">
<h5 class="articleSubheadline">March 2015</h5>
<ul class="bulletList">
<li>The weights of the wind measurement units, the wind estimation and manual wind inputs by the
race committee are now used properly for the wind streamlet visualization. This fixes issues
with exaggerated weights for an estimated wind direction of very low confidence.
</li>
<li>The expand/collapse toggle button in the race viewer's time panel has correct arrows now.
</li>
</ul>
<h5 class="articleSubheadline">February 2015</h5>
<ul class="bulletList">