mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-28 14:57:12 +00:00
fixed the flickering issue with tail update, bug 3378
Change-Id: Ib52197191537d1dc8de1c704b6b23d9b2f3e1ff3
This commit is contained in:
1 parent
dceee2da70
commit
bb176b6790
2 files changed
+67
-17
No files matched your search
+53
-12
@@ -12,6 +12,7 @@ import java.util.Map;
|
||||
import java.util.Set;
|
||||
|
||||
import com.google.gwt.maps.client.base.LatLng;
|
||||
import com.google.gwt.maps.client.mvc.MVCArray;
|
||||
import com.google.gwt.maps.client.overlays.Polyline;
|
||||
import com.google.gwt.user.client.Timer;
|
||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||
@@ -47,22 +48,23 @@ public class FixesAndTails {
|
||||
private final Map<CompetitorDTO, List<GPSFixDTO>> fixes;
|
||||
|
||||
/**
|
||||
* Tails of competitors currently displayed as overlays on the map.
|
||||
* Tails of competitors currently displayed as overlays on the map. A tail may have an {@link MVCArray#getLength()
|
||||
* empty} {@link Polyline#getPath()}. In this case, {@link #firstShownFix} and {@link #lastShownFix} will hold
|
||||
* <code>-1</code> for that competitor key.
|
||||
*/
|
||||
private final Map<CompetitorDTO, Polyline> tails;
|
||||
|
||||
|
||||
/**
|
||||
* Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the first fix shown
|
||||
* Key set is equal to that of {@link #tails} and tells what the index in {@link #fixes} of the first fix shown
|
||||
* in {@link #tails} is. If a key is contained in this map, it is also contained in {@link #lastShownFix} and vice
|
||||
* versa.
|
||||
* versa. If a tail is present but has an empty path, this map contains <code>-1</code> for that competitor.
|
||||
*/
|
||||
private final Map<CompetitorDTO, Integer> firstShownFix;
|
||||
|
||||
/**
|
||||
* Key set is equal to that of {@link #tails} and tells what the index in in {@link #fixes} of the last fix shown in
|
||||
* Key set is equal to that of {@link #tails} and tells what the index in {@link #fixes} of the last fix shown in
|
||||
* {@link #tails} is. If a key is contained in this map, it is also contained in {@link #firstShownFix} and vice
|
||||
* versa.
|
||||
* versa. If a tail is present but has an empty path, this map contains <code>-1</code> for that competitor.
|
||||
*/
|
||||
private final Map<CompetitorDTO, Integer> lastShownFix;
|
||||
|
||||
@@ -129,7 +131,9 @@ public class FixesAndTails {
|
||||
* Creates a polyline for the competitor represented by <code>competitorDTO</code>, taking the fixes from
|
||||
* {@link #fixes fixes.get(competitorDTO)} and using the fixes starting at time point <code>from</code> (inclusive)
|
||||
* up to the last fix with time point before <code>to</code>. The polyline is returned. Updates are applied to
|
||||
* {@link #lastShownFix}, {@link #firstShownFix} and {@link #tails}.
|
||||
* {@link #lastShownFix}, {@link #firstShownFix} and {@link #tails}.<p>
|
||||
*
|
||||
* Precondition: <code>tails.containsKey(competitorDTO) == false</code>
|
||||
*/
|
||||
protected Polyline createTailAndUpdateIndices(final CompetitorDTO competitorDTO, Date from, Date to, TailFactory tailFactory) {
|
||||
List<LatLng> points = new ArrayList<LatLng>();
|
||||
@@ -199,10 +203,19 @@ public class FixesAndTails {
|
||||
* longer tail itself), such that the second request that uses the <em>same</em> map will be considered
|
||||
* having an overlap now, not leading to a replacement of the previous update originating from the same
|
||||
* request.
|
||||
* @return
|
||||
*
|
||||
* @return a {@link Runnable} for each competitor that the caller must invoke before starting to create or update
|
||||
* the key competitor's tail. The object returned may be a no-op, but it may also clear an existing tail if
|
||||
* necessary. The reason why this is not executed immediately is that there may be a transition-based delay
|
||||
* with which the tails are updated. In this case we would be producing severe flicker. By delaying the
|
||||
* command to the point when the update is applied should avoid such flicker.
|
||||
*/
|
||||
protected void updateFixes(Map<CompetitorDTO, List<GPSFixDTO>> fixesForCompetitors,
|
||||
protected Map<CompetitorDTO, Runnable> updateFixes(Map<CompetitorDTO, List<GPSFixDTO>> fixesForCompetitors,
|
||||
Map<CompetitorDTO, Boolean> overlapsWithKnownFixes, TailFactory tailFactory, long timeForPositionTransitionMillis) {
|
||||
for (Map.Entry<CompetitorDTO, List<GPSFixDTO>> e : fixesForCompetitors.entrySet()) {
|
||||
final Map<CompetitorDTO, Runnable> result = new HashMap<>();
|
||||
final Runnable noOp = new Runnable() { @Override public void run() {} };
|
||||
for (final Map.Entry<CompetitorDTO, List<GPSFixDTO>> e : fixesForCompetitors.entrySet()) {
|
||||
if (e.getValue() != null && !e.getValue().isEmpty()) {
|
||||
List<GPSFixDTO> fixesForCompetitor = fixes.get(e.getKey());
|
||||
if (fixesForCompetitor == null) {
|
||||
@@ -214,14 +227,18 @@ public class FixesAndTails {
|
||||
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
|
||||
removeTail(e.getKey());
|
||||
result.put(e.getKey(), new Runnable() { @Override public void run() { clearTail(e.getKey()); } });
|
||||
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
|
||||
} else {
|
||||
mergeFixes(e.getKey(), e.getValue(), timeForPositionTransitionMillis);
|
||||
result.put(e.getKey(), noOp);
|
||||
}
|
||||
} else {
|
||||
result.put(e.getKey(), noOp);
|
||||
}
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
/**
|
||||
@@ -299,6 +316,7 @@ public class FixesAndTails {
|
||||
} 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) {
|
||||
@@ -364,12 +382,19 @@ public class FixesAndTails {
|
||||
* @param delayForTailChangeInMillis
|
||||
* the time in milliseconds after which to actually draw the tail update, or <code>-1</code> to perform
|
||||
* the update immediately
|
||||
* @param tailPreparer
|
||||
* will be {@link Runnable#run} just before the updates are applied to the <code>tail</code>. In
|
||||
* particular, if a transition is active, the effect of running this preparer will be held back until the
|
||||
* transition applies. May be <code>null</code> in which case simply no preparer is run.
|
||||
*/
|
||||
protected void updateTail(final Polyline tail, final CompetitorDTO competitorDTO,
|
||||
final Date from, final Date to, final int delayForTailChangeInMillis) {
|
||||
final Date from, final Date to, final int delayForTailChangeInMillis, final Runnable tailPreparer) {
|
||||
Timer delayedOrImmediateExecutor = new Timer() {
|
||||
@Override
|
||||
public void run() {
|
||||
if (tailPreparer != null) {
|
||||
tailPreparer.run();
|
||||
}
|
||||
int vertexCount = tail.getPath().getLength();
|
||||
final List<GPSFixDTO> fixesForCompetitor = getFixes(competitorDTO);
|
||||
int indexOfFirstShownFix = firstShownFix.get(competitorDTO) == null ? -1 : firstShownFix
|
||||
@@ -391,7 +416,7 @@ public class FixesAndTails {
|
||||
tail.getPath().insertAt(0, coordinateSystem.toLatLng(fix.position));
|
||||
vertexCount++;
|
||||
}
|
||||
// now adjust the polylines tail: remove excess vertices that are after "to"
|
||||
// now adjust the polyline's tail: remove excess vertices that are after "to"
|
||||
int indexOfLastShownFix = lastShownFix.get(competitorDTO) == null ? -1 : lastShownFix
|
||||
.get(competitorDTO);
|
||||
while (indexOfLastShownFix != -1 && vertexCount > 0
|
||||
@@ -410,6 +435,9 @@ public class FixesAndTails {
|
||||
indexOfLastShownFix++;
|
||||
GPSFixDTO fix = fixesForCompetitor.get(indexOfLastShownFix);
|
||||
tail.getPath().insertAt(vertexCount++, coordinateSystem.toLatLng(fix.position));
|
||||
if (indexOfFirstShownFix < 0) { // empty tail before?
|
||||
indexOfFirstShownFix = indexOfLastShownFix; // set to the first vertex inserted into tail
|
||||
}
|
||||
}
|
||||
firstShownFix.put(competitorDTO, indexOfFirstShownFix);
|
||||
lastShownFix.put(competitorDTO, indexOfLastShownFix);
|
||||
@@ -438,6 +466,19 @@ public class FixesAndTails {
|
||||
firstShownFix.remove(competitor);
|
||||
lastShownFix.remove(competitor);
|
||||
}
|
||||
|
||||
/**
|
||||
* Leaves the tail on the map and empties its path {@link Polyline#getPath()}. Correspondingly, the
|
||||
* {@link #firstShownFix} and {@link #lastShownFix} entries for <code>competitor</code> are set to <code>-1</code>.
|
||||
*/
|
||||
private void clearTail(CompetitorDTO competitor) {
|
||||
Polyline tail = tails.get(competitor);
|
||||
if (tail != null) {
|
||||
tail.getPath().clear();
|
||||
firstShownFix.put(competitor, -1);
|
||||
lastShownFix.put(competitor, -1);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* From {@link #fixes} as well as the selection of {@link #getCompetitorsToShow competitors to show}, computes the
|
||||
|
||||
+14
-5
@@ -1008,8 +1008,9 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
private void updateBoatPositions(final Date newTime, final long transitionTimeInMillis,
|
||||
final Map<CompetitorDTO, Boolean> hasTailOverlapForCompetitor,
|
||||
final Iterable<CompetitorDTO> competitorsToShow, Map<CompetitorDTO, List<GPSFixDTO>> boatData, boolean updateTailsOnly) {
|
||||
fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis);
|
||||
showBoatsOnMap(newTime, transitionTimeInMillis, competitorsToShow, updateTailsOnly);
|
||||
final Map<CompetitorDTO, Runnable> tailPreparersPerCompetitor =
|
||||
fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis);
|
||||
showBoatsOnMap(newTime, transitionTimeInMillis, competitorsToShow, updateTailsOnly, tailPreparersPerCompetitor);
|
||||
if (!updateTailsOnly) {
|
||||
showCompetitorInfoOnMap(newTime, transitionTimeInMillis, competitorSelection.getSelectedFilteredCompetitors());
|
||||
// even though the wind data is retrieved by a separate call, re-draw the advantage line because it needs to
|
||||
@@ -1220,9 +1221,15 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
}
|
||||
|
||||
/**
|
||||
* @param updateTailsOnly if <code>false</code>, tails of competitors not in <code>competitorsToShow</code> are removed from the map
|
||||
* @param updateTailsOnly
|
||||
* if <code>false</code>, tails of competitors not in <code>competitorsToShow</code> are removed from the
|
||||
* map
|
||||
* @param tailPreparersPerCompetitor
|
||||
* executed before creating or updating the respective competitor's tail
|
||||
*/
|
||||
private void showBoatsOnMap(final Date newTime, final long timeForPositionTransitionMillis, final Iterable<CompetitorDTO> competitorsToShow, boolean updateTailsOnly) {
|
||||
private void showBoatsOnMap(final Date newTime, final long timeForPositionTransitionMillis,
|
||||
final Iterable<CompetitorDTO> competitorsToShow, boolean updateTailsOnly,
|
||||
Map<CompetitorDTO, Runnable> tailPreparersPerCompetitor) {
|
||||
if (map != null) {
|
||||
Date tailsFromTime = new Date(newTime.getTime() - settings.getEffectiveTailLengthInMilliseconds());
|
||||
Date tailsToTime = newTime;
|
||||
@@ -1233,13 +1240,15 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
||||
competitorDTOsOfUnusedBoatCanvases.addAll(boatOverlays.keySet());
|
||||
}
|
||||
for (CompetitorDTO competitorDTO : competitorsToShow) {
|
||||
Runnable tailPreparer = tailPreparersPerCompetitor.get(competitorDTO);
|
||||
if (fixesAndTails.hasFixesFor(competitorDTO)) {
|
||||
Polyline tail = fixesAndTails.getTail(competitorDTO);
|
||||
if (tail == null) {
|
||||
tailPreparer.run(); // presumably a no-op, but you never know...
|
||||
tail = fixesAndTails.createTailAndUpdateIndices(competitorDTO, tailsFromTime, tailsToTime, this);
|
||||
} else {
|
||||
fixesAndTails.updateTail(tail, competitorDTO, tailsFromTime, tailsToTime,
|
||||
(int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2));
|
||||
(int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2), tailPreparer);
|
||||
if (!updateTailsOnly) {
|
||||
competitorDTOsOfUnusedTails.remove(competitorDTO);
|
||||
}
|
||||
|
||||
Reference in new issue
Block a user