From a0302d97b975a83e6bfe0e624d48de53e0475fd7 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 28 Nov 2016 18:12:43 +0100 Subject: [PATCH] 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 --- .../client/shared/racemap/FixesAndTails.java | 107 +++++++++++------- .../gwt/ui/server/SailingServiceImpl.java | 14 ++- 2 files changed, 72 insertions(+), 49 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/FixesAndTails.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/FixesAndTails.java index d9cd681fc01..f25dda270c9 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/FixesAndTails.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/FixesAndTails.java @@ -227,31 +227,44 @@ public class FixesAndTails { Map overlapsWithKnownFixes, TailFactory tailFactory, long timeForPositionTransitionMillis) { for (final Map.Entry> e : fixesForCompetitors.entrySet()) { if (e.getValue() != null && !e.getValue().isEmpty()) { - List fixesForCompetitor = fixes.get(e.getKey()); + final CompetitorDTO competitor = e.getKey(); + List fixesForCompetitor = fixes.get(competitor); if (fixesForCompetitor == null) { fixesForCompetitor = new ArrayList(); - 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 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 tailTrigger = tails.get(competitor); + if (tailTrigger != null) { + tailTrigger.register(triggerable); + } + registerIndexTrigger(triggerable, firstShownFix, competitor); + registerIndexTrigger(triggerable, lastShownFix, competitor); + } private void registerIndexTrigger(Triggerable triggerable, Map> indexMap, CompetitorDTO competitor) { Trigger 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++; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java index c9af96ba985..2f0640d008d 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/server/SailingServiceImpl.java @@ -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 from parameter, requests the GPS fixes up to but excluding the date provided - * as value + * keys in the from parameter, requests the GPS fixes up to but excluding (except + * {@code extrapolate} is {@code true}) the date provided as value * @param extrapolate - * if true and no (exact or interpolated) position is known for date, the last + * if true and no (exact or interpolated) position is known for to, the last * entry returned in the list of GPS fixes will be obtained by extrapolating from the competitors last - * known position before date and the estimated speed. + * known position at to 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 date. */ @@ -1750,7 +1751,8 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S Iterator 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);