don't insert extrapolated fixes anywhere except at the end; use real fixes if extrapolation is requested and we have a real fix for the "to" time point

Change-Id: I83fcb72a3d09b97922f918234263ba167f0e7645
This commit is contained in:
Axel Uhl committed 2016-11-28 18:12:43 +01:00
1 parent 6746b39028
commit a0302d97b9
2 files changed
+72 -49

No files matched your search

@@ -227,31 +227,44 @@ public class FixesAndTails {
Map<CompetitorDTO, Boolean> overlapsWithKnownFixes, TailFactory tailFactory, long timeForPositionTransitionMillis) {
for (final Map.Entry<CompetitorDTO, List<GPSFixDTOWithSpeedWindTackAndLegType>> e : fixesForCompetitors.entrySet()) {
if (e.getValue() != null && !e.getValue().isEmpty()) {
List<GPSFixDTOWithSpeedWindTackAndLegType> fixesForCompetitor = fixes.get(e.getKey());
final CompetitorDTO competitor = e.getKey();
List<GPSFixDTOWithSpeedWindTackAndLegType> fixesForCompetitor = fixes.get(competitor);
if (fixesForCompetitor == null) {
fixesForCompetitor = new ArrayList<GPSFixDTOWithSpeedWindTackAndLegType>();
fixes.put(e.getKey(), fixesForCompetitor);
fixes.put(competitor, fixesForCompetitor);
}
if (!overlapsWithKnownFixes.get(e.getKey())) {
if (!overlapsWithKnownFixes.get(competitor)) {
// clearing and then re-populating establishes the invariant that an extrapolated fix must be the last
fixesForCompetitor.clear();
// to re-establish the invariants for tails, firstShownFix and lastShownFix, we now need to remove
// all points from the competitor's polyline and clear the entries in firstShownFix and lastShownFix
final Triggerable triggerable = new Triggerable(new Runnable() { @Override public void run() { clearTail(e.getKey()); } });
final Trigger<Polyline> tailTrigger = tails.get(e.getKey());
if (tailTrigger != null) {
tailTrigger.register(triggerable);
}
registerIndexTrigger(triggerable, firstShownFix, e.getKey());
registerIndexTrigger(triggerable, lastShownFix, e.getKey());
final Triggerable triggerable = new Triggerable(new Runnable() { @Override public void run() { clearTail(competitor); } });
registerTriggerable(competitor, triggerable);
fixesForCompetitor.addAll(e.getValue());
overlapsWithKnownFixes.put(e.getKey(), true); // In case this was only one part of a split request, the next request *does* have an overlap
overlapsWithKnownFixes.put(competitor, true); // In case this was only one part of a split request, the next request *does* have an overlap
} else {
mergeFixes(e.getKey(), e.getValue(), timeForPositionTransitionMillis);
mergeFixes(competitor, e.getValue(), timeForPositionTransitionMillis);
}
}
}
}
/**
* A {@link Triggerable} that is usually a {@link TriggerableTimer} and is or will be scheduled for delayed execution
* will be registered with the {@link #tails}, {@link #firstShownFix} and {@link #lastShownFix} structures such that
* when any of them is accessed for the {@code competitor} passed as parameter then the {@code triggerable} will be
* triggered to ensure consistency and establish all invariants. This way, invariants may temporarily be left unmet
* which gives the UI a better chance of delaying particularly the tail updates. However, in case of read access the
* invariants all must be established, hence the trigger pattern.
*/
private void registerTriggerable(final CompetitorDTO competitor, final Triggerable triggerable) {
final Trigger<Polyline> tailTrigger = tails.get(competitor);
if (tailTrigger != null) {
tailTrigger.register(triggerable);
}
registerIndexTrigger(triggerable, firstShownFix, competitor);
registerIndexTrigger(triggerable, lastShownFix, competitor);
}
private void registerIndexTrigger(Triggerable triggerable, Map<CompetitorDTO, Trigger<Integer>> indexMap, CompetitorDTO competitor) {
Trigger<Integer> firstShownFixTrigger = indexMap.get(competitor);
@@ -325,6 +338,7 @@ public class FixesAndTails {
tail.getPath().removeAt(finalIntoThisIndex - finalIndexOfFirstShownFix);
}
});
registerTriggerable(competitorDTO, triggerable);
Timer timer = new TriggerableTimer(triggerable);
runDelayedOrImmediately(timer, (int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2));
}
@@ -337,40 +351,47 @@ public class FixesAndTails {
intoThisIndex--;
}
} else {
intoThis.add(intoThisIndex, mergeThisFix);
if (tail != null && intoThisIndex >= indexOfFirstShownFix && intoThisIndex <= indexOfLastShownFix) {
// fix inserted at a position currently visualized by tail
tail.getPath().insertAt(intoThisIndex - indexOfFirstShownFix, coordinateSystem.toLatLng(mergeThisFix.position));
}
if (intoThisIndex < indexOfFirstShownFix) {
indexOfFirstShownFix++;
}
if (intoThisIndex <= indexOfLastShownFix) {
indexOfLastShownFix++;
}
// If there is a fix prior to the one added and that prior fix was obtained by extrapolation, remove it now because
// extrapolated fixes can only be the last in the list
if (intoThisIndex > 0 && intoThis.get(intoThisIndex-1).extrapolated) {
intoThis.remove(intoThisIndex-1);
if (tail != null && intoThisIndex-1 >= indexOfFirstShownFix && intoThisIndex-1 <= indexOfLastShownFix) {
final int finalIntoThisIndex = intoThisIndex;
final int finalIndexOfFirstShownFix = indexOfFirstShownFix;
Triggerable triggerable = new Triggerable(new Runnable() {
@Override
public void run() {
tail.getPath().removeAt(finalIntoThisIndex-1 - finalIndexOfFirstShownFix);
}
});
Timer timer = new TriggerableTimer(triggerable);
runDelayedOrImmediately(timer, (int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2));
// insert the fix only if it is not extrapolated or if the extrapolated fix is to be appended to the end (including
// being the only fix)
if (!mergeThisFix.extrapolated || intoThisIndex == intoThis.size()) {
intoThis.add(intoThisIndex, mergeThisFix);
if (tail != null && intoThisIndex >= indexOfFirstShownFix && intoThisIndex <= indexOfLastShownFix) {
// fix inserted at a position currently visualized by tail
tail.getPath().insertAt(intoThisIndex - indexOfFirstShownFix, coordinateSystem.toLatLng(mergeThisFix.position));
}
if (intoThisIndex-1 < indexOfFirstShownFix) {
indexOfFirstShownFix--;
if (intoThisIndex < indexOfFirstShownFix) {
indexOfFirstShownFix++;
}
if (intoThisIndex-1 <= indexOfLastShownFix) {
indexOfLastShownFix--;
if (intoThisIndex <= indexOfLastShownFix) {
indexOfLastShownFix++;
}
intoThisIndex--;
// If there is a fix prior to the one added and that prior fix was obtained by extrapolation, remove it now because
// extrapolated fixes can only be the last in the list
if (intoThisIndex > 0 && intoThis.get(intoThisIndex-1).extrapolated) {
intoThis.remove(intoThisIndex-1);
if (tail != null && intoThisIndex-1 >= indexOfFirstShownFix && intoThisIndex-1 <= indexOfLastShownFix) {
final int finalIntoThisIndex = intoThisIndex;
final int finalIndexOfFirstShownFix = indexOfFirstShownFix;
Triggerable triggerable = new Triggerable(new Runnable() {
@Override
public void run() {
tail.getPath().removeAt(finalIntoThisIndex-1 - finalIndexOfFirstShownFix);
}
});
registerTriggerable(competitorDTO, triggerable);
Timer timer = new TriggerableTimer(triggerable);
runDelayedOrImmediately(timer, (int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2));
}
if (intoThisIndex-1 < indexOfFirstShownFix) {
indexOfFirstShownFix--;
}
if (intoThisIndex-1 <= indexOfLastShownFix) {
indexOfLastShownFix--;
}
intoThisIndex--;
}
} else {
intoThisIndex--; // to compensate for the following ++
}
}
intoThisIndex++;
@@ -1717,12 +1717,13 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
* date provided as value
* @param to
* for the list of competitors provided as keys (expected to be equal to the set of competitors used as
* keys in the <code>from</code> parameter, requests the GPS fixes up to but excluding the date provided
* as value
* keys in the <code>from</code> parameter, requests the GPS fixes up to but excluding (except
* {@code extrapolate} is {@code true}) the date provided as value
* @param extrapolate
* if <code>true</code> and no (exact or interpolated) position is known for <code>date</code>, the last
* if <code>true</code> and no (exact or interpolated) position is known for <code>to</code>, the last
* entry returned in the list of GPS fixes will be obtained by extrapolating from the competitors last
* known position before <code>date</code> and the estimated speed.
* known position at <code>to</code> and the estimated speed. With this, the {@code to} time point is no
* longer exclusive.
* @return a map where for each competitor participating in the race the list of GPS fixes in increasing
* chronological order is provided. The last one is the last position at or before <code>date</code>.
*/
@@ -1750,7 +1751,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
Iterator<GPSFixMoving> fixIter = track.getFixesIterator(fromTimePoint, /* inclusive */true);
while (fixIter.hasNext()) {
GPSFixMoving fix = fixIter.next();
if (fix.getTimePoint().before(toTimePointExcluding)) {
if (fix.getTimePoint().before(toTimePointExcluding) ||
(extrapolate && fix.getTimePoint().equals(toTimePointExcluding))) {
if (logger.isLoggable(Level.FINEST)) {
logger.finest(""+competitor.getName()+": " + fix);
}
@@ -1814,7 +1816,7 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S
if (fixIter.hasNext()) {
fix = fixIter.next();
} else {
// check if fix was at date and if extrapolation is requested
// check if fix was at date and if extrapolation is requested;
if (!fix.getTimePoint().equals(toTimePointExcluding) && extrapolate) {
Position position = track.getEstimatedPosition(toTimePointExcluding, extrapolate);
Wind wind2 = trackedRace.getWind(position, toTimePointExcluding);