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 java.util.Set;
|
||||||
|
|
||||||
import com.google.gwt.maps.client.base.LatLng;
|
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.maps.client.overlays.Polyline;
|
||||||
import com.google.gwt.user.client.Timer;
|
import com.google.gwt.user.client.Timer;
|
||||||
import com.google.gwt.user.client.rpc.AsyncCallback;
|
import com.google.gwt.user.client.rpc.AsyncCallback;
|
||||||
@@ -47,22 +48,23 @@ public class FixesAndTails {
|
|||||||
private final Map<CompetitorDTO, List<GPSFixDTO>> fixes;
|
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;
|
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
|
* 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;
|
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
|
* {@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;
|
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
|
* 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)
|
* {@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
|
* 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) {
|
protected Polyline createTailAndUpdateIndices(final CompetitorDTO competitorDTO, Date from, Date to, TailFactory tailFactory) {
|
||||||
List<LatLng> points = new ArrayList<LatLng>();
|
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
|
* 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
|
* having an overlap now, not leading to a replacement of the previous update originating from the same
|
||||||
* request.
|
* 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) {
|
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()) {
|
if (e.getValue() != null && !e.getValue().isEmpty()) {
|
||||||
List<GPSFixDTO> fixesForCompetitor = fixes.get(e.getKey());
|
List<GPSFixDTO> fixesForCompetitor = fixes.get(e.getKey());
|
||||||
if (fixesForCompetitor == null) {
|
if (fixesForCompetitor == null) {
|
||||||
@@ -214,14 +227,18 @@ public class FixesAndTails {
|
|||||||
fixesForCompetitor.clear();
|
fixesForCompetitor.clear();
|
||||||
// to re-establish the invariants for tails, firstShownFix and lastShownFix, we now need to remove
|
// 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
|
// 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());
|
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(e.getKey(), true); // In case this was only one part of a split request, the next request *does* have an overlap
|
||||||
} else {
|
} else {
|
||||||
mergeFixes(e.getKey(), e.getValue(), timeForPositionTransitionMillis);
|
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 {
|
} else {
|
||||||
intoThis.add(intoThisIndex, mergeThisFix);
|
intoThis.add(intoThisIndex, mergeThisFix);
|
||||||
if (tail != null && intoThisIndex >= indexOfFirstShownFix && intoThisIndex <= indexOfLastShownFix) {
|
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));
|
tail.getPath().insertAt(intoThisIndex - indexOfFirstShownFix, coordinateSystem.toLatLng(mergeThisFix.position));
|
||||||
}
|
}
|
||||||
if (intoThisIndex < indexOfFirstShownFix) {
|
if (intoThisIndex < indexOfFirstShownFix) {
|
||||||
@@ -364,12 +382,19 @@ public class FixesAndTails {
|
|||||||
* @param delayForTailChangeInMillis
|
* @param delayForTailChangeInMillis
|
||||||
* the time in milliseconds after which to actually draw the tail update, or <code>-1</code> to perform
|
* the time in milliseconds after which to actually draw the tail update, or <code>-1</code> to perform
|
||||||
* the update immediately
|
* 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,
|
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() {
|
Timer delayedOrImmediateExecutor = new Timer() {
|
||||||
@Override
|
@Override
|
||||||
public void run() {
|
public void run() {
|
||||||
|
if (tailPreparer != null) {
|
||||||
|
tailPreparer.run();
|
||||||
|
}
|
||||||
int vertexCount = tail.getPath().getLength();
|
int vertexCount = tail.getPath().getLength();
|
||||||
final List<GPSFixDTO> fixesForCompetitor = getFixes(competitorDTO);
|
final List<GPSFixDTO> fixesForCompetitor = getFixes(competitorDTO);
|
||||||
int indexOfFirstShownFix = firstShownFix.get(competitorDTO) == null ? -1 : firstShownFix
|
int indexOfFirstShownFix = firstShownFix.get(competitorDTO) == null ? -1 : firstShownFix
|
||||||
@@ -391,7 +416,7 @@ public class FixesAndTails {
|
|||||||
tail.getPath().insertAt(0, coordinateSystem.toLatLng(fix.position));
|
tail.getPath().insertAt(0, coordinateSystem.toLatLng(fix.position));
|
||||||
vertexCount++;
|
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
|
int indexOfLastShownFix = lastShownFix.get(competitorDTO) == null ? -1 : lastShownFix
|
||||||
.get(competitorDTO);
|
.get(competitorDTO);
|
||||||
while (indexOfLastShownFix != -1 && vertexCount > 0
|
while (indexOfLastShownFix != -1 && vertexCount > 0
|
||||||
@@ -410,6 +435,9 @@ public class FixesAndTails {
|
|||||||
indexOfLastShownFix++;
|
indexOfLastShownFix++;
|
||||||
GPSFixDTO fix = fixesForCompetitor.get(indexOfLastShownFix);
|
GPSFixDTO fix = fixesForCompetitor.get(indexOfLastShownFix);
|
||||||
tail.getPath().insertAt(vertexCount++, coordinateSystem.toLatLng(fix.position));
|
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);
|
firstShownFix.put(competitorDTO, indexOfFirstShownFix);
|
||||||
lastShownFix.put(competitorDTO, indexOfLastShownFix);
|
lastShownFix.put(competitorDTO, indexOfLastShownFix);
|
||||||
@@ -438,6 +466,19 @@ public class FixesAndTails {
|
|||||||
firstShownFix.remove(competitor);
|
firstShownFix.remove(competitor);
|
||||||
lastShownFix.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
|
* 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,
|
private void updateBoatPositions(final Date newTime, final long transitionTimeInMillis,
|
||||||
final Map<CompetitorDTO, Boolean> hasTailOverlapForCompetitor,
|
final Map<CompetitorDTO, Boolean> hasTailOverlapForCompetitor,
|
||||||
final Iterable<CompetitorDTO> competitorsToShow, Map<CompetitorDTO, List<GPSFixDTO>> boatData, boolean updateTailsOnly) {
|
final Iterable<CompetitorDTO> competitorsToShow, Map<CompetitorDTO, List<GPSFixDTO>> boatData, boolean updateTailsOnly) {
|
||||||
fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis);
|
final Map<CompetitorDTO, Runnable> tailPreparersPerCompetitor =
|
||||||
showBoatsOnMap(newTime, transitionTimeInMillis, competitorsToShow, updateTailsOnly);
|
fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis);
|
||||||
|
showBoatsOnMap(newTime, transitionTimeInMillis, competitorsToShow, updateTailsOnly, tailPreparersPerCompetitor);
|
||||||
if (!updateTailsOnly) {
|
if (!updateTailsOnly) {
|
||||||
showCompetitorInfoOnMap(newTime, transitionTimeInMillis, competitorSelection.getSelectedFilteredCompetitors());
|
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
|
// 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) {
|
if (map != null) {
|
||||||
Date tailsFromTime = new Date(newTime.getTime() - settings.getEffectiveTailLengthInMilliseconds());
|
Date tailsFromTime = new Date(newTime.getTime() - settings.getEffectiveTailLengthInMilliseconds());
|
||||||
Date tailsToTime = newTime;
|
Date tailsToTime = newTime;
|
||||||
@@ -1233,13 +1240,15 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
|
|||||||
competitorDTOsOfUnusedBoatCanvases.addAll(boatOverlays.keySet());
|
competitorDTOsOfUnusedBoatCanvases.addAll(boatOverlays.keySet());
|
||||||
}
|
}
|
||||||
for (CompetitorDTO competitorDTO : competitorsToShow) {
|
for (CompetitorDTO competitorDTO : competitorsToShow) {
|
||||||
|
Runnable tailPreparer = tailPreparersPerCompetitor.get(competitorDTO);
|
||||||
if (fixesAndTails.hasFixesFor(competitorDTO)) {
|
if (fixesAndTails.hasFixesFor(competitorDTO)) {
|
||||||
Polyline tail = fixesAndTails.getTail(competitorDTO);
|
Polyline tail = fixesAndTails.getTail(competitorDTO);
|
||||||
if (tail == null) {
|
if (tail == null) {
|
||||||
|
tailPreparer.run(); // presumably a no-op, but you never know...
|
||||||
tail = fixesAndTails.createTailAndUpdateIndices(competitorDTO, tailsFromTime, tailsToTime, this);
|
tail = fixesAndTails.createTailAndUpdateIndices(competitorDTO, tailsFromTime, tailsToTime, this);
|
||||||
} else {
|
} else {
|
||||||
fixesAndTails.updateTail(tail, competitorDTO, tailsFromTime, tailsToTime,
|
fixesAndTails.updateTail(tail, competitorDTO, tailsFromTime, tailsToTime,
|
||||||
(int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2));
|
(int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2), tailPreparer);
|
||||||
if (!updateTailsOnly) {
|
if (!updateTailsOnly) {
|
||||||
competitorDTOsOfUnusedTails.remove(competitorDTO);
|
competitorDTOsOfUnusedTails.remove(competitorDTO);
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in new issue
Block a user