mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-11-06 19:12:23 +00:00
Merge remote-tracking branch 'origin/master'
This commit is contained in:
commit
861ff55778
14 files changed
+164
-73
No files matched your search
+4
-2
@@ -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();
|
||||
}
|
||||
+2
-2
@@ -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));
|
||||
|
||||
+10
-1
@@ -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;
|
||||
}
|
||||
|
||||
}
|
||||
+10
@@ -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);
|
||||
|
||||
@@ -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 {
|
||||
|
||||
+7
-12
@@ -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();
|
||||
};
|
||||
|
||||
+2
-2
@@ -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()
|
||||
|
||||
+6
-3
@@ -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;
|
||||
|
||||
+22
@@ -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() {}
|
||||
}
|
||||
+8
-6
@@ -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
-17
@@ -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) {
|
||||
+54
-26
@@ -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;
|
||||
}
|
||||
|
||||
|
||||
+11
@@ -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">
|
||||
|
||||
Reference in new issue
Block a user