bug3378: fixing display by marking the second of two split calls as overlap=true, avoiding the incorrect removal of fixes/tails for the second update

Change-Id: Ib1cdeb7b58455745a572c85fab954cfd0f14c31e
This commit is contained in:
Axel Uhl committed 2015-12-15 11:57:58 +01:00
1 parent a2076240a3
commit a292d85224
3 files changed
+91 -55

No files matched your search

@@ -174,28 +174,37 @@ public class FixesAndTails {
}
/**
* Adds the fixes received in <code>result</code> to {@link #fixes} and ensures they are still contiguous for each
* competitor. If <code>overlapsWithKnownFixes</code> indicates that the fixes received in <code>result</code>
* overlap with those already known, the fixes are merged into the list of already known fixes for the competitor.
* Otherwise, the fixes received in <code>result</code> replace those known so far for the respective competitor.
* The {@link #tails} affected by these fixes are updated accordingly when modifications fall inside the interval
* shown by the tail, as defined by {@link #firstShownFix} and {@link #lastShownFix}. The tails are, however, not
* trimmed according to the specification for the tail length. This has to happen elsewhere (see also
* {@link #updateTail}).
* Adds the fixes received in <code>fixesForCompetitors</code> to {@link #fixes} and ensures they are still
* contiguous for each competitor. If <code>overlapsWithKnownFixes</code> indicates that the fixes received in
* <code>result</code> overlap with those already known, the fixes are merged into the list of already known fixes
* for the competitor. Otherwise, the fixes received in <code>result</code> replace those known so far for the
* respective competitor. The {@link #tails} affected by these fixes are updated accordingly when modifications fall
* inside the interval shown by the tail, as defined by {@link #firstShownFix} and {@link #lastShownFix}. The tails
* are, however, not trimmed according to the specification for the tail length. This has to happen elsewhere (see
* also {@link #updateTail}).
*
* @param fixesForCompetitors
* For each list the invariant must hold that an {@link GPSFixDTO#extrapolated extrapolated} fix must be
* the last one in the list
*
* @return the competitors for which new fixes have been added; this will not include competitors appearing as keys
* in <code>fixesForCompetitor</code> whose fixes list is <code>null</code> or empty
* @param overlapsWithKnownFixes
* if for a competitor whose fixes are provided in <code>fixesForCompetitors</code> this holds
* <code>false</code>, any fixes previously stored for that competitor are removed, and the tail is
* deleted from the map (see {@link #removeTail(CompetitorDTO)}); the new fixes are then added to the
* {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). If
* this map holds <code>true</code>, {@link #mergeFixes(CompetitorDTO, List, long)} is used to merge the
* new fixes from <code>fixesForCompetitors</code> into the {@link #fixes} collection, and the tail is
* left unchanged. <b>NOTE:</b> When a non-overlapping set of fixes is updated (<code>false</code>), this
* map's record for the competitor is <b>UPDATED</b> to <code>true</code> after the tail deletion and
* {@link #fixes} replacement has taken place. This helps in cases where this update is only one of two
* into which an original request was split (one quick update of the tail's head and another one for the
* 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.
*/
protected Iterable<CompetitorDTO> updateFixes(Map<CompetitorDTO, List<GPSFixDTO>> fixesForCompetitors,
protected void updateFixes(Map<CompetitorDTO, List<GPSFixDTO>> fixesForCompetitors,
Map<CompetitorDTO, Boolean> overlapsWithKnownFixes, TailFactory tailFactory, long timeForPositionTransitionMillis) {
final Set<CompetitorDTO> competitorsWithNewFixes = new HashSet<>();
for (Map.Entry<CompetitorDTO, List<GPSFixDTO>> e : fixesForCompetitors.entrySet()) {
if (e.getValue() != null && !e.getValue().isEmpty()) {
competitorsWithNewFixes.add(e.getKey());
List<GPSFixDTO> fixesForCompetitor = fixes.get(e.getKey());
if (fixesForCompetitor == null) {
fixesForCompetitor = new ArrayList<GPSFixDTO>();
@@ -206,19 +215,14 @@ 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
if (tails.containsKey(e.getKey())) {
Polyline removedTail = tails.remove(e.getKey());
removedTail.setMap(null);
}
firstShownFix.remove(e.getKey());
lastShownFix.remove(e.getKey());
removeTail(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);
}
}
}
return competitorsWithNewFixes;
}
/**
@@ -483,7 +487,7 @@ public class FixesAndTails {
toDate = upTo;
}
// only request something for the competitor if we're missing information at all
if (fromDate.before(toDate) || fromDate.equals(toDate)) {
if (!fromDate.after(toDate)) {
from.put(competitor, fromDate);
to.put(competitor, toDate);
overlapWithKnownFixes.put(competitor, overlap);
@@ -23,7 +23,11 @@ public class Hoverline {
this.options.setMap(polyline.getMap());
this.options.setPath(polyline.getPath());
this.options.setStrokeColor(polylineOptions.getStrokeColor());
this.options.setZindex(polylineOptions.getZindex());
try {
this.options.setZindex(polylineOptions.getZindex());
} catch (Exception e) {
// the Z-index of polylineOptions most likely was undefined and therefore cannot be copied (GWT DevMode problem, mostly)
}
this.hoverline = Polyline.newInstance(this.options);
this.hoverline.setVisible(false);
polyline.addMouseOverHandler(new MouseOverMapHandler() {
@@ -773,14 +773,12 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
if (selectedRaces != null && !selectedRaces.isEmpty()) {
RegattaAndRaceIdentifier race = selectedRaces.get(selectedRaces.size() - 1);
final Iterable<CompetitorDTO> competitorsToShow = getCompetitorsToShow();
if (race != null) {
final long transitionTimeInMillis = calculateTimeForPositionTransitionInMillis(newTime, oldTime);
final com.sap.sse.common.Util.Triple<Map<CompetitorDTO, Date>, Map<CompetitorDTO, Date>, Map<CompetitorDTO, Boolean>> fromAndToAndOverlap =
fixesAndTails.computeFromAndTo(newTime, competitorsToShow, settings.getEffectiveTailLengthInMilliseconds());
// Request map data update, possibly in two calls; see method details
callGetRaceMapDataForAllOverlappingAndTipsOfNonOverlappingAndGetBoatPositionsForAllOthers(fromAndToAndOverlap, race, newTime, transitionTimeInMillis, competitorsToShow);
// draw the wind into the map, get the combined wind
List<String> windSourceTypeNames = new ArrayList<String>();
windSourceTypeNames.add(WindSourceType.EXPEDITION.name());
@@ -879,25 +877,37 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
competitorsByIdAsString.put(competitor.getIdAsString(), competitor);
}
// only update the tails for these competitors
// Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to updateBoatPositions happening inside
// the callback provided by getRaceMapDataCallback(...) for those
// entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for
// all its entries so that the other response received for GetBoatPositionsAction will consider this an
// overlap if it happens after this update.
asyncActionsExecutor.execute(new GetRaceMapDataAction(sailingService, competitorsByIdAsString,
race, useNullAsTimePoint() ? null : newTime, fromTimesForQuickCall, toTimesForQuickCall, /* extrapolate */true,
(settings.isShowSimulationOverlay() ? simulationOverlay.getLegIdentifier() : null)), GET_RACE_MAP_DATA_CATEGORY,
getRaceMapDataCallback(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, ++boatPositionRequestIDCounter));
// next, if necessary, do the full thing; the two calls have different action classes, so throttling should not drop one for the other
asyncActionsExecutor.execute(new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, toTimesForNonOverlappingTailsCall,
/* extrapolate */ true), GET_RACE_MAP_DATA_CATEGORY,
new MarkedAsyncCallback<>(new AsyncCallback<CompactBoatPositionsDTO>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), true /*silentMode */);
}
@Override
public void onSuccess(CompactBoatPositionsDTO result) {
updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow, result.getBoatPositionsForCompetitors(competitorsByIdAsString),
/* updateTailsOnly */ true);
}
}));
if (!fromTimesForNonOverlappingTailsCall.keySet().isEmpty()) {
asyncActionsExecutor.execute(new GetBoatPositionsAction(sailingService, race, fromTimesForNonOverlappingTailsCall, toTimesForNonOverlappingTailsCall,
/* extrapolate */ true), GET_RACE_MAP_DATA_CATEGORY,
new MarkedAsyncCallback<>(new AsyncCallback<CompactBoatPositionsDTO>() {
@Override
public void onFailure(Throwable t) {
errorReporter.reportError("Error obtaining racemap data: " + t.getMessage(), true /*silentMode */);
}
@Override
public void onSuccess(CompactBoatPositionsDTO result) {
// Note: the fromAndToAndOverlap.getC() map will be UPDATED by the call to updateBoatPositions for those
// entries that are considered not overlapping; subsequently, fromAndToOverlap.getC() will contain true for
// all its entries so that the other response received for GetRaceMapDataAction will consider this an
// overlap if it happens after this update.
updateBoatPositions(newTime, transitionTimeInMillis, fromAndToAndOverlap.getC(), competitorsToShow,
result.getBoatPositionsForCompetitors(competitorsByIdAsString), /* updateTailsOnly */ true);
}
}));
}
}
private AsyncCallback<RaceMapDataDTO> getRaceMapDataCallback(
@@ -973,6 +983,20 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
}
/**
* @param hasTailOverlapForCompetitor
* if for a competitor whose fixes are provided in <code>fixesForCompetitors</code> this holds
* <code>false</code>, any fixes previously stored for that competitor are removed, and the tail is
* deleted from the map (see {@link #removeTail(CompetitorDTO)}); the new fixes are then added to the
* {@link #fixes} map, and a new tail will have to be constructed as needed (does not happen here). If
* this map holds <code>true</code>, {@link #mergeFixes(CompetitorDTO, List, long)} is used to merge the
* new fixes from <code>fixesForCompetitors</code> into the {@link #fixes} collection, and the tail is
* left unchanged. <b>NOTE:</b> When a non-overlapping set of fixes is updated (<code>false</code>), this
* map's record for the competitor is <b>UPDATED</b> to <code>true</code> after the tail deletion and
* {@link #fixes} replacement has taken place. This helps in cases where this update is only one of two
* into which an original request was split (one quick update of the tail's head and another one for the
* 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. See also {@link FixesAndTails#updateFixes(Map, Map, TailFactory, long)}.
* @param updateTailsOnly
* if <code>true</code>, only the tails are updated according to <code>boatData</code> and
* <code>hasTailOverlapForCompetitor</code>, but the advantage line is not updated, and neither are the
@@ -985,16 +1009,12 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
final Map<CompetitorDTO, Boolean> hasTailOverlapForCompetitor,
final Iterable<CompetitorDTO> competitorsToShow, Map<CompetitorDTO, List<GPSFixDTO>> boatData, boolean updateTailsOnly) {
fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis);
Iterable<CompetitorDTO> competitorsForWhichNewFixesWereReveiced =
fixesAndTails.updateFixes(boatData, hasTailOverlapForCompetitor, RaceMap.this, transitionTimeInMillis);
if (!Util.isEmpty(competitorsForWhichNewFixesWereReveiced)) {
showBoatsOnMap(newTime, transitionTimeInMillis, competitorsForWhichNewFixesWereReveiced, updateTailsOnly);
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
// adjust to new boat positions
showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis);
}
showBoatsOnMap(newTime, transitionTimeInMillis, competitorsToShow, updateTailsOnly);
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
// adjust to new boat positions
showAdvantageLine(competitorsToShow, newTime, transitionTimeInMillis);
}
}
@@ -1206,8 +1226,12 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
if (map != null) {
Date tailsFromTime = new Date(newTime.getTime() - settings.getEffectiveTailLengthInMilliseconds());
Date tailsToTime = newTime;
Set<CompetitorDTO> competitorDTOsOfUnusedTails = new HashSet<CompetitorDTO>(fixesAndTails.getCompetitorsWithTails());
Set<CompetitorDTO> competitorDTOsOfUnusedBoatCanvases = new HashSet<CompetitorDTO>(boatOverlays.keySet());
Set<CompetitorDTO> competitorDTOsOfUnusedTails = new HashSet<CompetitorDTO>();
Set<CompetitorDTO> competitorDTOsOfUnusedBoatCanvases = new HashSet<CompetitorDTO>();
if (!updateTailsOnly) {
competitorDTOsOfUnusedTails.addAll(fixesAndTails.getCompetitorsWithTails());
competitorDTOsOfUnusedBoatCanvases.addAll(boatOverlays.keySet());
}
for (CompetitorDTO competitorDTO : competitorsToShow) {
if (fixesAndTails.hasFixesFor(competitorDTO)) {
Polyline tail = fixesAndTails.getTail(competitorDTO);
@@ -1216,12 +1240,14 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
} else {
fixesAndTails.updateTail(tail, competitorDTO, tailsFromTime, tailsToTime,
(int) (timeForPositionTransitionMillis==-1?-1:timeForPositionTransitionMillis/2));
competitorDTOsOfUnusedTails.remove(competitorDTO);
if (!updateTailsOnly) {
competitorDTOsOfUnusedTails.remove(competitorDTO);
}
PolylineOptions newOptions = createTailStyle(competitorDTO, displayHighlighted(competitorDTO));
tail.setOptions(newOptions);
}
boolean usedExistingBoatCanvas = updateBoatCanvasForCompetitor(competitorDTO, newTime, timeForPositionTransitionMillis);
if (usedExistingBoatCanvas) {
if (usedExistingBoatCanvas && !updateTailsOnly) {
competitorDTOsOfUnusedBoatCanvases.remove(competitorDTO);
}
}
@@ -1379,8 +1405,10 @@ public class RaceMap extends AbsolutePanel implements TimeListener, CompetitorSe
nextPath.push(advantageLinePos1);
nextPath.push(advantageLinePos2);
advantageTimer.setNextPositionAndTransitionMillis(nextPath, timeForPositionTransitionMillis);
advantageLineMouseOverHandler.setTrueWindBearing(bearingOfCombinedWindInDeg);
advantageLineMouseOverHandler.setDate(new Date(windFix.measureTimepoint));
if (advantageLineMouseOverHandler != null) {
advantageLineMouseOverHandler.setTrueWindBearing(bearingOfCombinedWindInDeg);
advantageLineMouseOverHandler.setDate(new Date(windFix.measureTimepoint));
}
}
drawAdvantageLine = true;
}