From 9065df6235d82bcdc2d352629fde1fedb40b6d03 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 19 Jun 2017 09:53:40 +0200 Subject: [PATCH 1/7] compilable version of competitor info update (bug 4175) Change-Id: Ia20077ef7b6b561b7b9ea24c5dd4b790471a5a43 --- .../racemap/CompetitorInfoOverlays.java | 151 ++++++++++++++++++ .../racemap/DefaultQuickRanksDTOProvider.java | 7 +- .../shared/racemap/QuickRanksDTOProvider.java | 8 + .../gwt/ui/client/shared/racemap/RaceMap.java | 67 ++++---- .../AbstractQuickRanksDTOProvider.java | 31 ++++ ...ickRanksDTOFromLeaderboardDTOProvider.java | 5 +- 6 files changed, 225 insertions(+), 44 deletions(-) create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java create mode 100755 java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractQuickRanksDTOProvider.java diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java new file mode 100755 index 00000000000..d306df87b6e --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java @@ -0,0 +1,151 @@ +package com.sap.sailing.gwt.ui.client.shared.racemap; + +import java.util.Collections; +import java.util.HashMap; +import java.util.Iterator; +import java.util.Map; +import java.util.Map.Entry; + +import com.sap.sailing.domain.common.dto.CompetitorDTO; +import com.sap.sailing.gwt.ui.client.NumberFormatterFactory; +import com.sap.sailing.gwt.ui.client.StringMessages; +import com.sap.sailing.gwt.ui.client.shared.racemap.QuickRanksDTOProvider.QuickRanksListener; +import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType; +import com.sap.sailing.gwt.ui.shared.QuickRankDTO; + +/** + * Manages the collection of all {@link CompetitorInfoOverlay} objects shown in the {@link RaceMap}. + * In particular, it keeps track of the information used for the text in the overlays and allows for + * incremental updates, e.g., if a competitor's rank has been updated. + * + * @author Axel Uhl (d043530) + * + */ +public class CompetitorInfoOverlays implements QuickRanksListener { + private final RaceMap raceMap; + + /** + * html5 canvases used for competitor info display on the map; keys are the competitor IDs as strings + */ + private final Map competitorInfoOverlays; + + /** + * keys are the competitor IDs as strings + */ + private final Map ranks; + + /** + * keys are the competitor IDs as strings + */ + private final Map lastPositions; + + private final StringMessages stringMessages; + + public CompetitorInfoOverlays(RaceMap raceMap, final StringMessages stringMessages) { + competitorInfoOverlays = new HashMap<>(); + ranks = new HashMap<>(); + lastPositions = new HashMap<>(); + this.stringMessages = stringMessages; + this.raceMap = raceMap; + } + + public CompetitorInfoOverlay createCompetitorInfoOverlay(int zIndex, final CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType gpsFixDTO, Integer rank, long timeForPositionTransitionMillis) { + updatePosition(competitorDTO, gpsFixDTO); + updateRank(competitorDTO, rank); + CompetitorInfoOverlay result = new CompetitorInfoOverlay(raceMap.getMap(), zIndex, + raceMap.getCompetitorSelection().getColor(competitorDTO, raceMap.getRaceIdentifier()), + createInfoText(competitorDTO), raceMap.getCoordinateSystem()); + result.setPosition(gpsFixDTO.position, timeForPositionTransitionMillis); + competitorInfoOverlays.put(competitorDTO.getIdAsString(), result); + return result; + } + + /** + * Removes the {@link CompetitorInfoOverlay} for the competitor specified as {@code competitorDTO} from the map and + * from this structure. + */ + public void remove(final CompetitorDTO competitorDTO) { + remove(competitorDTO.getIdAsString()); + } + + public void remove(final String competitorIdAsString) { + CompetitorInfoOverlay overlay = competitorInfoOverlays.remove(competitorIdAsString); + if (overlay != null) { + overlay.removeFromMap(); + } + ranks.remove(competitorIdAsString); + lastPositions.remove(competitorIdAsString); + } + + public CompetitorInfoOverlay get(final CompetitorDTO competitorDTO) { + return competitorInfoOverlays.get(competitorDTO.getIdAsString()); + } + + public Iterable getCompetitorInfoOverlays() { + return competitorInfoOverlays.values(); + } + + public void updateRank(CompetitorDTO competitorDTO, int rank) { + CompetitorInfoOverlay overlay = competitorInfoOverlays.get(competitorDTO.getIdAsString()); + if (overlay != null) { + ranks.put(competitorDTO.getIdAsString(), rank); + overlay.setInfoText(createInfoText(competitorDTO)); + overlay.draw(); + } + } + + private GPSFixDTOWithSpeedWindTackAndLegType getLastPosition(CompetitorDTO competitorDTO) { + return lastPositions.get(competitorDTO.getIdAsString()); + } + + public void removeTransitions() { + for (CompetitorInfoOverlay infoOverlay : getCompetitorInfoOverlays()) { + infoOverlay.removeCanvasPositionAndRotationTransition(); + } + } + + public void updatePosition(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType gpsFixDTO) { + lastPositions.put(competitorDTO.getIdAsString(), gpsFixDTO); + } + + /** + * Produces a text for the competitor info window for competitor {@code competitorDTO} based on the contents + * of {@link #ranks} and {@link #lastPositions}. + */ + private String createInfoText(CompetitorDTO competitorDTO) { + StringBuilder infoText = new StringBuilder(); + infoText.append(competitorDTO.getSailID()).append("\n"); + infoText.append(NumberFormatterFactory.getDecimalFormat(1).format(getLastPosition(competitorDTO).speedWithBearing.speedInKnots)) + .append(" ").append(stringMessages.knotsUnit()).append("\n"); + final Integer rank = ranks.get(competitorDTO.getIdAsString()); + if (rank != null) { + infoText.append(stringMessages.rank()).append(" : ").append(rank); + } + return infoText.toString(); + } + + public Iterable getCompetitorIdsAsStrings() { + return Collections.unmodifiableCollection(competitorInfoOverlays.keySet()); + } + + /** + * Removes all overlays managed by this structure from the map and from this structure + */ + public void clear() { + for (final Iterator> i=competitorInfoOverlays.entrySet().iterator(); i.hasNext(); ) { + final Entry e = i.next(); + e.getValue().removeFromMap(); + i.remove(); + } + lastPositions.clear(); + ranks.clear(); + } + + @Override + public void rankChanged(String competitorIdAsString, QuickRankDTO quickRanks) { + if (competitorInfoOverlays.containsKey(competitorIdAsString)) { + ranks.put(competitorIdAsString, quickRanks.rank); + competitorInfoOverlays.get(competitorIdAsString).draw(); + } + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/DefaultQuickRanksDTOProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/DefaultQuickRanksDTOProvider.java index 3b150295988..09658aabd33 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/DefaultQuickRanksDTOProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/DefaultQuickRanksDTOProvider.java @@ -1,15 +1,20 @@ package com.sap.sailing.gwt.ui.client.shared.racemap; import java.util.LinkedHashMap; +import java.util.Map.Entry; +import com.sap.sailing.gwt.ui.raceboard.AbstractQuickRanksDTOProvider; import com.sap.sailing.gwt.ui.shared.QuickRankDTO; -public class DefaultQuickRanksDTOProvider implements QuickRanksDTOProvider { +public class DefaultQuickRanksDTOProvider extends AbstractQuickRanksDTOProvider { private LinkedHashMap quickRanksFromServer; @Override public void quickRanksReceivedFromServer(LinkedHashMap quickRanksFromServer) { this.quickRanksFromServer = quickRanksFromServer; + for (final Entry e : quickRanksFromServer.entrySet()) { + notifyListeners(e.getKey(), e.getValue()); + } } @Override diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/QuickRanksDTOProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/QuickRanksDTOProvider.java index 9cab5e77b5f..f7158988b1f 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/QuickRanksDTOProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/QuickRanksDTOProvider.java @@ -23,6 +23,14 @@ import com.sap.sailing.gwt.ui.shared.RaceMapDataDTO; * */ public interface QuickRanksDTOProvider { + public interface QuickRanksListener { + void rankChanged(String competitorIdAsString, QuickRankDTO quickRanks); + } + + void addQuickRanksListener(QuickRanksListener listener); + + void removeQuickRanksListener(QuickRanksListener listener); + /** * The strategy may or may not use this quick ranks information provided directly from the server. This information * may have been provided with some delay and may be slightly inconsistent with a {@link LeaderboardDTO leaderboard} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index 7d815d67aa8..b27456d1b94 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -269,7 +269,7 @@ public class RaceMap extends AbstractCompositeComponent impleme /** * html5 canvases used for competitor info display on the map */ - private final Map competitorInfoOverlays; + private final CompetitorInfoOverlays competitorInfoOverlays; private SmallTransparentInfoOverlay countDownOverlay; @@ -431,7 +431,8 @@ public class RaceMap extends AbstractCompositeComponent impleme courseMiddleLines = new HashMap<>(); infoOverlaysForLinesForCourseGeometry = new HashMap<>(); boatOverlays = new HashMap(); - competitorInfoOverlays = new HashMap(); + competitorInfoOverlays = new CompetitorInfoOverlays(this, stringMessages); + quickRanksDTOProvider.addQuickRanksListener(competitorInfoOverlays); windSensorOverlays = new HashMap(); courseMarkOverlays = new HashMap(); courseMarkClickHandlers = new HashMap(); @@ -741,9 +742,7 @@ public class RaceMap extends AbstractCompositeComponent impleme boatOverlay.removeCanvasPositionAndRotationTransition(); } // remove the canvas animations for the info overlays of the selected boats - for (CompetitorInfoOverlay infoOverlay : competitorInfoOverlays.values()) { - infoOverlay.removeCanvasPositionAndRotationTransition(); - } + competitorInfoOverlays.removeTransitions(); for (CourseMarkOverlay markOverlay : courseMarkOverlays.values()) { markOverlay.removeCanvasPositionAndRotationTransition(); } @@ -761,6 +760,18 @@ public class RaceMap extends AbstractCompositeComponent impleme return Collections.unmodifiableMap(boatOverlays); } + protected CompetitorSelectionProvider getCompetitorSelection() { + return competitorSelection; + } + + protected Timer getTimer() { + return timer; + } + + protected RegattaAndRaceIdentifier getRaceIdentifier() { + return raceIdentifier; + } + public MapWidget getMap() { return map; } @@ -1212,38 +1223,32 @@ public class RaceMap extends AbstractCompositeComponent impleme private void showCompetitorInfoOnMap(final Date newTime, final long timeForPositionTransitionMillis, final Iterable competitorsToShow) { if (map != null) { if (settings.isShowSelectedCompetitorsInfo()) { - Set toRemoveCompetorInfoOverlays = new HashSet( - competitorInfoOverlays.keySet()); + Set toRemoveCompetorInfoOverlaysIdsAsStrings = new HashSet<>(); + Util.addAll(competitorInfoOverlays.getCompetitorIdsAsStrings(), toRemoveCompetorInfoOverlaysIdsAsStrings); for (CompetitorDTO competitorDTO : competitorsToShow) { if (fixesAndTails.hasFixesFor(competitorDTO)) { GPSFixDTOWithSpeedWindTackAndLegType lastBoatFix = getBoatFix(competitorDTO, newTime); if (lastBoatFix != null) { CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(competitorDTO); + final Integer rank = getRank(competitorDTO); if (competitorInfoOverlay == null) { - competitorInfoOverlay = createCompetitorInfoOverlay(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO); - competitorInfoOverlays.put(competitorDTO, competitorInfoOverlay); - competitorInfoOverlay.setInfoText(createInfoText(competitorDTO, lastBoatFix)); - competitorInfoOverlay.setPosition(lastBoatFix.position, timeForPositionTransitionMillis); + competitorInfoOverlay = competitorInfoOverlays.createCompetitorInfoOverlay(RaceMapOverlaysZIndexes.INFO_OVERLAY_ZINDEX, competitorDTO, + lastBoatFix, rank, timeForPositionTransitionMillis); competitorInfoOverlay.addToMap(); } else { - competitorInfoOverlay.setInfoText(createInfoText(competitorDTO, lastBoatFix)); - competitorInfoOverlay.setPosition(lastBoatFix.position, timeForPositionTransitionMillis); - competitorInfoOverlay.draw(); + competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix); + competitorInfoOverlays.updateRank(competitorDTO, rank); + competitorInfoOverlays.get(competitorDTO).draw(); } - toRemoveCompetorInfoOverlays.remove(competitorDTO); + toRemoveCompetorInfoOverlaysIdsAsStrings.remove(competitorDTO.getIdAsString()); } } } - for (CompetitorDTO toRemoveCompetorDTO : toRemoveCompetorInfoOverlays) { - CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(toRemoveCompetorDTO); - competitorInfoOverlay.removeFromMap(); - competitorInfoOverlays.remove(toRemoveCompetorDTO); + for (String toRemoveCompetitorOverlayIdAsString : toRemoveCompetorInfoOverlaysIdsAsStrings) { + competitorInfoOverlays.remove(toRemoveCompetitorOverlayIdAsString); } } else { // remove all overlays - for (CompetitorInfoOverlay competitorInfoOverlay : competitorInfoOverlays.values()) { - competitorInfoOverlay.removeFromMap(); - } competitorInfoOverlays.clear(); } } @@ -1960,23 +1965,6 @@ public class RaceMap extends AbstractCompositeComponent impleme } } - private CompetitorInfoOverlay createCompetitorInfoOverlay(int zIndex, final CompetitorDTO competitorDTO) { - GPSFixDTOWithSpeedWindTackAndLegType gpsFixDTO = getBoatFix(competitorDTO, timer.getTime()); - return new CompetitorInfoOverlay(map, zIndex, competitorSelection.getColor(competitorDTO, raceIdentifier), createInfoText(competitorDTO, gpsFixDTO), coordinateSystem); - } - - private String createInfoText(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType gpsFixDTO) { - StringBuilder infoText = new StringBuilder(); - infoText.append(competitorDTO.getSailID()).append("\n"); - infoText.append(NumberFormatterFactory.getDecimalFormat(1).format(gpsFixDTO.speedWithBearing.speedInKnots)) - .append(" ").append(stringMessages.knotsUnit()).append("\n"); - final Integer rank = getRank(competitorDTO); - if (rank != null) { - infoText.append(stringMessages.rank()).append(" : ").append(rank); - } - return infoText.toString(); - } - private BoatOverlay createBoatOverlay(int zIndex, final CompetitorDTO competitorDTO, boolean highlighted) { final BoatOverlay boatCanvas = new BoatOverlay(map, zIndex, competitorDTO, competitorSelection.getColor(competitorDTO, raceIdentifier), coordinateSystem); boatCanvas.setSelected(highlighted); @@ -2773,7 +2761,6 @@ public class RaceMap extends AbstractCompositeComponent impleme @Override public Integer getRank(CompetitorDTO competitor) { - // TODO bug4175: obtain from raceBoardPanel.leaderboardPanel.leaderboard instead of QuickRanks final Integer result; QuickRankDTO quickRank = quickRanksDTOProvider.getQuickRanks().get(competitor.getIdAsString()); if (quickRank != null) { diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractQuickRanksDTOProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractQuickRanksDTOProvider.java new file mode 100755 index 00000000000..cbe93d1f0da --- /dev/null +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/AbstractQuickRanksDTOProvider.java @@ -0,0 +1,31 @@ +package com.sap.sailing.gwt.ui.raceboard; + +import java.util.HashSet; +import java.util.Set; + +import com.sap.sailing.gwt.ui.client.shared.racemap.QuickRanksDTOProvider; +import com.sap.sailing.gwt.ui.shared.QuickRankDTO; + +public abstract class AbstractQuickRanksDTOProvider implements QuickRanksDTOProvider { + private final Set listeners; + + public AbstractQuickRanksDTOProvider() { + listeners = new HashSet<>(); + } + + @Override + public void addQuickRanksListener(QuickRanksListener listener) { + listeners.add(listener); + } + + @Override + public void removeQuickRanksListener(QuickRanksListener listener) { + listeners.remove(listener); + } + + protected void notifyListeners(String competitorIdAsString, QuickRankDTO quickRank) { + for (QuickRanksListener listener : listeners) { + listener.rankChanged(competitorIdAsString, quickRank); + } + } +} diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/QuickRanksDTOFromLeaderboardDTOProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/QuickRanksDTOFromLeaderboardDTOProvider.java index e1a821bd33f..52dc3b54a64 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/QuickRanksDTOFromLeaderboardDTOProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/QuickRanksDTOFromLeaderboardDTOProvider.java @@ -10,7 +10,6 @@ import com.sap.sailing.domain.common.dto.LeaderboardDTO; import com.sap.sailing.domain.common.dto.LeaderboardEntryDTO; import com.sap.sailing.domain.common.dto.LeaderboardRowDTO; import com.sap.sailing.domain.common.dto.RaceColumnDTO; -import com.sap.sailing.gwt.ui.client.shared.racemap.QuickRanksDTOProvider; import com.sap.sailing.gwt.ui.client.shared.racemap.RaceCompetitorSet; import com.sap.sailing.gwt.ui.shared.QuickRankDTO; import com.sap.sse.common.Util; @@ -25,7 +24,7 @@ import com.sap.sse.common.Util; * @author Axel Uhl (d043530) * */ -public class QuickRanksDTOFromLeaderboardDTOProvider implements QuickRanksDTOProvider { +public class QuickRanksDTOFromLeaderboardDTOProvider extends AbstractQuickRanksDTOProvider { private LinkedHashMap quickRanks; private final RaceCompetitorSet raceCompetitorSet; private final RaceIdentifier selectedRace; @@ -43,6 +42,7 @@ public class QuickRanksDTOFromLeaderboardDTOProvider implements QuickRanksDTOPro quickRanks = new LinkedHashMap<>(); for (final Entry e : quickRanksFromServer.entrySet()) { quickRanks.put(e.getKey(), e.getValue()); + notifyListeners(e.getKey(), e.getValue()); } } else if (!lastLeaderboardProvidedLegNumbers) { // extract at least the leg numbers and update existing quick ranks accordingly in place @@ -110,5 +110,4 @@ public class QuickRanksDTOFromLeaderboardDTOProvider implements QuickRanksDTOPro public LinkedHashMap getQuickRanks() { return quickRanks; } - } From 8c5af7dd532d794a1c5ee6e3831db45b5b3ec935 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 19 Jun 2017 16:46:53 +0200 Subject: [PATCH 2/7] added null check in case mark does not have valid position (bug 4187) Change-Id: Ib8b775de0a10847f36ec535a9ae889fe93849b67 --- .../domain/tracking/impl/TrackedLegOfCompetitorImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java index f53598fd656..c55005f065d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java @@ -453,7 +453,12 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { } else { Distance distanceToNextMark = getTrackedRace().getTrackedLeg(leg) .getAbsoluteWindwardDistance(currentPosition, nextMarkPosition, timePoint, windPositionMode, cache); - result = new MeterDistance(result.getMeters() + distanceToNextMark.getMeters()); + if (distanceToNextMark != null) { + result = new MeterDistance(result.getMeters() + distanceToNextMark.getMeters()); + } else { + result = null; + break; + } } currentPosition = nextMarkPosition; } else { From 0a7140b5b60f3051b1e33ba9b9e2038ef182cd24 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 19 Jun 2017 16:46:53 +0200 Subject: [PATCH 3/7] added null check in case mark does not have valid position (bug 4187) Change-Id: Ib8b775de0a10847f36ec535a9ae889fe93849b67 --- .../domain/tracking/impl/TrackedLegOfCompetitorImpl.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java index f53598fd656..c55005f065d 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/tracking/impl/TrackedLegOfCompetitorImpl.java @@ -453,7 +453,12 @@ public class TrackedLegOfCompetitorImpl implements TrackedLegOfCompetitor { } else { Distance distanceToNextMark = getTrackedRace().getTrackedLeg(leg) .getAbsoluteWindwardDistance(currentPosition, nextMarkPosition, timePoint, windPositionMode, cache); - result = new MeterDistance(result.getMeters() + distanceToNextMark.getMeters()); + if (distanceToNextMark != null) { + result = new MeterDistance(result.getMeters() + distanceToNextMark.getMeters()); + } else { + result = null; + break; + } } currentPosition = nextMarkPosition; } else { From c98398c139324db08e429b040f362538d1baffd9 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 19 Jun 2017 17:51:34 +0200 Subject: [PATCH 4/7] fixed bug 4188: HOBIE_16 gets a catamaran visualization Change-Id: I331786097c2d9ffacfcee58a31898065a70b0cde --- .../client/shared/racemap/BoatClassVectorGraphicsResolver.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java index cb885b4e6b5..d1a744a2c8f 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/BoatClassVectorGraphicsResolver.java @@ -38,7 +38,7 @@ public class BoatClassVectorGraphicsResolver { BoatClassMasterdata.D_35, BoatClassMasterdata.SKUD_18, BoatClassMasterdata.WETA); BoatClassVectorGraphics gc32 = new GC32VectorGraphics(BoatClassMasterdata.GC_32, BoatClassMasterdata.M32); BoatClassVectorGraphics smallMultihull = new SmallMultihullVectorGraphics(BoatClassMasterdata.NACRA_17, - BoatClassMasterdata.F_16, BoatClassMasterdata.F_18, BoatClassMasterdata.HOBIE_WILD_CAT, + BoatClassMasterdata.F_16, BoatClassMasterdata.F_18, BoatClassMasterdata.HOBIE_WILD_CAT, BoatClassMasterdata.HOBIE_16, BoatClassMasterdata.HOBIE_TIGER, BoatClassMasterdata.A_CAT, BoatClassMasterdata.TORNADO, BoatClassMasterdata.FLYING_PHANTOM); BoatClassVectorGraphics keelBoatWithGennaker = new KeelBoatWithGennakerVectorGraphics(BoatClassMasterdata.J70, From 6453c9c288d890b0cf496a1e7023db187e996096 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 20 Jun 2017 01:01:49 +0200 Subject: [PATCH 5/7] bug4175: quick ranks cache updates and visualization now work well in local test Change-Id: Ia23800cbce68b248b750116dc09c0e4b729fc3ed --- .../racemap/CompetitorInfoOverlays.java | 55 ++++++++++++++----- .../gwt/ui/client/shared/racemap/RaceMap.java | 6 +- ...ickRanksDTOFromLeaderboardDTOProvider.java | 55 +++++++++++-------- .../gwt/ui/server/SailingServiceImpl.java | 6 +- .../gwt/ui/shared/CompactQuickRankDTO.java | 2 +- .../gwt/ui/shared/CompactRaceMapDataDTO.java | 4 +- .../sailing/gwt/ui/shared/QuickRankDTO.java | 5 +- 7 files changed, 85 insertions(+), 48 deletions(-) diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java index d306df87b6e..e514ed98daf 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/CompetitorInfoOverlays.java @@ -14,9 +14,16 @@ import com.sap.sailing.gwt.ui.shared.GPSFixDTOWithSpeedWindTackAndLegType; import com.sap.sailing.gwt.ui.shared.QuickRankDTO; /** - * Manages the collection of all {@link CompetitorInfoOverlay} objects shown in the {@link RaceMap}. - * In particular, it keeps track of the information used for the text in the overlays and allows for - * incremental updates, e.g., if a competitor's rank has been updated. + * Manages the collection of all {@link CompetitorInfoOverlay} objects shown in the {@link RaceMap}. In particular, it + * keeps track of the information used for the text in the overlays and allows for incremental updates, e.g., if a + * competitor's rank has been updated. It can act as a {@link QuickRanksListener} and as such be subscribed for rank + * changes, e.g., with a {@link QuickRanksDTOProvider}. It will track all rank updates for any competitor whose info + * overlay is managed by this instance. + *

+ * + * Competitor positions are expected to be explicitly announced by calling + * {@link #updatePosition(CompetitorDTO, GPSFixDTOWithSpeedWindTackAndLegType, long)} in case new position data is + * known. * * @author Axel Uhl (d043530) * @@ -50,13 +57,13 @@ public class CompetitorInfoOverlays implements QuickRanksListener { } public CompetitorInfoOverlay createCompetitorInfoOverlay(int zIndex, final CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType gpsFixDTO, Integer rank, long timeForPositionTransitionMillis) { - updatePosition(competitorDTO, gpsFixDTO); - updateRank(competitorDTO, rank); CompetitorInfoOverlay result = new CompetitorInfoOverlay(raceMap.getMap(), zIndex, raceMap.getCompetitorSelection().getColor(competitorDTO, raceMap.getRaceIdentifier()), - createInfoText(competitorDTO), raceMap.getCoordinateSystem()); - result.setPosition(gpsFixDTO.position, timeForPositionTransitionMillis); + /* info text */ "", raceMap.getCoordinateSystem()); competitorInfoOverlays.put(competitorDTO.getIdAsString(), result); + updatePosition(competitorDTO, gpsFixDTO, timeForPositionTransitionMillis); + result.setInfoText(createInfoText(competitorDTO)); + updateRank(competitorDTO, rank); return result; } @@ -85,7 +92,10 @@ public class CompetitorInfoOverlays implements QuickRanksListener { return competitorInfoOverlays.values(); } - public void updateRank(CompetitorDTO competitorDTO, int rank) { + /** + * Updates {@link #ranks} and re-draws the corresponding overlay + */ + public void updateRank(CompetitorDTO competitorDTO, Integer rank) { CompetitorInfoOverlay overlay = competitorInfoOverlays.get(competitorDTO.getIdAsString()); if (overlay != null) { ranks.put(competitorDTO.getIdAsString(), rank); @@ -104,13 +114,24 @@ public class CompetitorInfoOverlays implements QuickRanksListener { } } - public void updatePosition(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType gpsFixDTO) { + /** + * Updates the competitor's position data and and, if the competitor has an overlay managed by this instance, + * adjusts the overlay's position on the map using the transition timeout specified by + * {@code timeForPositionTransitionMillis}. + */ + public void updatePosition(CompetitorDTO competitorDTO, GPSFixDTOWithSpeedWindTackAndLegType gpsFixDTO, long timeForPositionTransitionMillis) { lastPositions.put(competitorDTO.getIdAsString(), gpsFixDTO); + CompetitorInfoOverlay overlay = competitorInfoOverlays.get(competitorDTO.getIdAsString()); + if (overlay != null) { + overlay.setPosition(gpsFixDTO.position, timeForPositionTransitionMillis); + overlay.draw(); + } } /** * Produces a text for the competitor info window for competitor {@code competitorDTO} based on the contents - * of {@link #ranks} and {@link #lastPositions}. + * of {@link #ranks} and {@link #lastPositions}. If no {@link #ranks} entry exists, no rank is shown. However, + * a {@link #lastPosition} is required in order to produce the speed/course information. */ private String createInfoText(CompetitorDTO competitorDTO) { StringBuilder infoText = new StringBuilder(); @@ -118,7 +139,7 @@ public class CompetitorInfoOverlays implements QuickRanksListener { infoText.append(NumberFormatterFactory.getDecimalFormat(1).format(getLastPosition(competitorDTO).speedWithBearing.speedInKnots)) .append(" ").append(stringMessages.knotsUnit()).append("\n"); final Integer rank = ranks.get(competitorDTO.getIdAsString()); - if (rank != null) { + if (rank != null && rank != 0) { infoText.append(stringMessages.rank()).append(" : ").append(rank); } return infoText.toString(); @@ -141,11 +162,17 @@ public class CompetitorInfoOverlays implements QuickRanksListener { ranks.clear(); } + /** + * @param quickRanks + * uses the {@link QuickRankDTO#oneBasedRank} and {@link QuickRankDTO#competitor} fields + */ @Override public void rankChanged(String competitorIdAsString, QuickRankDTO quickRanks) { - if (competitorInfoOverlays.containsKey(competitorIdAsString)) { - ranks.put(competitorIdAsString, quickRanks.rank); - competitorInfoOverlays.get(competitorIdAsString).draw(); + final CompetitorInfoOverlay competitorInfoOverlay = competitorInfoOverlays.get(competitorIdAsString); + ranks.put(competitorIdAsString, quickRanks.oneBasedRank); + if (competitorInfoOverlay != null) { + competitorInfoOverlay.setInfoText(createInfoText(quickRanks.competitor)); + competitorInfoOverlay.draw(); } } } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java index b27456d1b94..75716cd4fa2 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/client/shared/racemap/RaceMap.java @@ -1236,9 +1236,7 @@ public class RaceMap extends AbstractCompositeComponent impleme lastBoatFix, rank, timeForPositionTransitionMillis); competitorInfoOverlay.addToMap(); } else { - competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix); - competitorInfoOverlays.updateRank(competitorDTO, rank); - competitorInfoOverlays.get(competitorDTO).draw(); + competitorInfoOverlays.updatePosition(competitorDTO, lastBoatFix, timeForPositionTransitionMillis); } toRemoveCompetorInfoOverlaysIdsAsStrings.remove(competitorDTO.getIdAsString()); } @@ -2764,7 +2762,7 @@ public class RaceMap extends AbstractCompositeComponent impleme final Integer result; QuickRankDTO quickRank = quickRanksDTOProvider.getQuickRanks().get(competitor.getIdAsString()); if (quickRank != null) { - result = quickRank.rank; + result = quickRank.oneBasedRank; } else { result = null; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/QuickRanksDTOFromLeaderboardDTOProvider.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/QuickRanksDTOFromLeaderboardDTOProvider.java index 52dc3b54a64..8e45209eae7 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/QuickRanksDTOFromLeaderboardDTOProvider.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/raceboard/QuickRanksDTOFromLeaderboardDTOProvider.java @@ -65,33 +65,44 @@ public class QuickRanksDTOFromLeaderboardDTOProvider extends AbstractQuickRanksD } if (raceColumnName != null) { final List competitorsFromBestToWorst = leaderboard.getCompetitorsFromBestToWorst(raceColumnName); - int rank = 1; - for (final CompetitorDTO c : competitorsFromBestToWorst) { - if (Util.contains(raceCompetitorSet.getIdsOfCompetitorsParticipatingInRaceAsStrings(), c.getIdAsString())) { - final LeaderboardRowDTO row = leaderboard.rows.get(c); - final int oneBasedLegNumber; - if (row != null) { - final LeaderboardEntryDTO raceEntryForCompetitor = row.fieldsByRaceColumnName.get(raceColumnName); - if (raceEntryForCompetitor != null && raceEntryForCompetitor.legDetails != null) { - oneBasedLegNumber = raceEntryForCompetitor.getOneBasedCurrentLegNumber(); - lastLeaderboardProvidedLegNumbers = true; + if (competitorsFromBestToWorst.isEmpty()) { + for (CompetitorDTO c : leaderboard.competitors) { + final QuickRankDTO quickRank = new QuickRankDTO(c, /* oneBasedRank */ 0, /* leg number ignored */ 0); + quickRanks.put(c.getIdAsString(), quickRank); + notifyListeners(c.getIdAsString(), quickRank); + } + } else { + int oneBasedRank = 1; + for (final CompetitorDTO c : competitorsFromBestToWorst) { + if (Util.contains(raceCompetitorSet.getIdsOfCompetitorsParticipatingInRaceAsStrings(), c.getIdAsString())) { + final LeaderboardRowDTO row = leaderboard.rows.get(c); + final int oneBasedLegNumber; + if (row != null) { + final LeaderboardEntryDTO raceEntryForCompetitor = row.fieldsByRaceColumnName.get(raceColumnName); + if (raceEntryForCompetitor != null && raceEntryForCompetitor.legDetails != null) { + oneBasedLegNumber = raceEntryForCompetitor.getOneBasedCurrentLegNumber(); + lastLeaderboardProvidedLegNumbers = true; + } else { + oneBasedLegNumber = 0; + lastLeaderboardProvidedLegNumbers = false; + } } else { oneBasedLegNumber = 0; - lastLeaderboardProvidedLegNumbers = false; } - } else { - oneBasedLegNumber = 0; - } - QuickRankDTO quickRankToUpdate = quickRanks.get(c.getIdAsString()); - if (quickRankToUpdate == null) { - quickRanks.put(c.getIdAsString(), new QuickRankDTO(c, rank, oneBasedLegNumber)); - } else { - quickRankToUpdate.rank = rank; - if (lastLeaderboardProvidedLegNumbers) { - quickRankToUpdate.legNumberOneBased = oneBasedLegNumber; + QuickRankDTO quickRankToUpdate = quickRanks.get(c.getIdAsString()); + if (quickRankToUpdate == null) { + final QuickRankDTO quickRankDTO = new QuickRankDTO(c, oneBasedRank, oneBasedLegNumber); + quickRanks.put(c.getIdAsString(), quickRankDTO); + notifyListeners(c.getIdAsString(), quickRankDTO); + } else { + quickRankToUpdate.oneBasedRank = oneBasedRank; + if (lastLeaderboardProvidedLegNumbers) { + quickRankToUpdate.legNumberOneBased = oneBasedLegNumber; + } + notifyListeners(c.getIdAsString(), quickRankToUpdate); } + oneBasedRank++; } - rank++; } } } 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 99306010541..6b14144896b 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 @@ -2224,17 +2224,17 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S actualTimePoint = timePoint; } final RaceDefinition race = trackedRace.getRace(); - int rank = 1; + int oneBasedRank = 1; final List competitorsFromBestToWorst = trackedRace.getCompetitorsFromBestToWorst(actualTimePoint); for (Competitor competitor : competitorsFromBestToWorst) { TrackedLegOfCompetitor trackedLeg = trackedRace.getTrackedLeg(competitor, actualTimePoint); if (trackedLeg != null) { int legNumberOneBased = race.getCourse().getLegs().indexOf(trackedLeg.getLeg()) + 1; QuickRankDTO quickRankDTO = new QuickRankDTO(baseDomainFactory.convertToCompetitorDTO(competitor), - rank, legNumberOneBased); + oneBasedRank, legNumberOneBased); result.add(quickRankDTO); } - rank++; + oneBasedRank++; } } return new QuickRanksDTO(result); diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactQuickRankDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactQuickRankDTO.java index 87a27e00a53..79f63dee094 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactQuickRankDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactQuickRankDTO.java @@ -28,7 +28,7 @@ public class CompactQuickRankDTO implements IsSerializable { return competitorIdAsString; } - public int getRank() { + public int getOneBasedRank() { return rank; } diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java index 4ca82a9787f..ac575043df0 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/CompactRaceMapDataDTO.java @@ -47,7 +47,7 @@ public class CompactRaceMapDataDTO implements IsSerializable { Map competitorIdAsStringToOneBasedLegNumber = new HashMap<>(); if (quickRanks != null) { for (QuickRankDTO quickRank : quickRanks.getQuickRanks()) { - this.quickRanks.add(new CompactQuickRankDTO(quickRank.competitor.getIdAsString(), quickRank.rank, + this.quickRanks.add(new CompactQuickRankDTO(quickRank.competitor.getIdAsString(), quickRank.oneBasedRank, quickRank.legNumberOneBased)); competitorIdAsStringToOneBasedLegNumber.put(quickRank.competitor.getIdAsString(), quickRank.legNumberOneBased); } @@ -63,7 +63,7 @@ public class CompactRaceMapDataDTO implements IsSerializable { for (CompactQuickRankDTO compactQuickRank : this.quickRanks) { final CompetitorDTO competitorDTO = competitorsByIdAsString.get(compactQuickRank.getCompetitorIdAsString()); if (competitorDTO != null) { - result.quickRanks.put(compactQuickRank.getCompetitorIdAsString(), new QuickRankDTO(competitorDTO, compactQuickRank.getRank(), compactQuickRank.getLegNumber())); + result.quickRanks.put(compactQuickRank.getCompetitorIdAsString(), new QuickRankDTO(competitorDTO, compactQuickRank.getOneBasedRank(), compactQuickRank.getLegNumber())); } } result.courseSidelines = courseSidelines; diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/QuickRankDTO.java b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/QuickRankDTO.java index 2bb2bdf9c98..af06ad6330d 100755 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/QuickRankDTO.java +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/ui/shared/QuickRankDTO.java @@ -5,7 +5,8 @@ import com.sap.sailing.domain.common.dto.CompetitorDTO; public class QuickRankDTO implements IsSerializable { public CompetitorDTO competitor; - public int rank; + + public int oneBasedRank; /** * The {@link #competitor}'s leg number, starting with 1 for the first leg @@ -16,7 +17,7 @@ public class QuickRankDTO implements IsSerializable { public QuickRankDTO(CompetitorDTO competitorDTO, int rank, int legNumberOneBased) { this.competitor = competitorDTO; - this.rank = rank; + this.oneBasedRank = rank; this.legNumberOneBased = legNumberOneBased; } } From 7d2feff5bfa8840813973e0a0413cf24662e9d4a Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Tue, 20 Jun 2017 13:51:02 +0200 Subject: [PATCH 6/7] add IControlsListener to receive position update for static marks; unsubscribe listeners properly again in onStop Change-Id: I8aa109fd572bc68e8c0d2d14a76e58979e036715 --- .../impl/TracTracRaceTrackerImpl.java | 26 ++++++++++++++++--- 1 file changed, 22 insertions(+), 4 deletions(-) diff --git a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java index c66f37c7d70..bc78c3ec403 100755 --- a/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java +++ b/java/com.sap.sailing.domain.tractracadapter/src/com/sap/sailing/domain/tractracadapter/impl/TracTracRaceTrackerImpl.java @@ -65,6 +65,7 @@ import com.tractrac.subscription.lib.api.ISubscriberFactory; import com.tractrac.subscription.lib.api.SubscriberInitializationException; import com.tractrac.subscription.lib.api.SubscriptionLocator; import com.tractrac.subscription.lib.api.competitor.ICompetitorsListener; +import com.tractrac.subscription.lib.api.control.IControlsListener; import com.tractrac.subscription.lib.api.event.IConnectionStatusListener; import com.tractrac.subscription.lib.api.event.ILiveDataEvent; import com.tractrac.subscription.lib.api.event.IStoredDataEvent; @@ -194,6 +195,9 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl private final com.sap.sailing.domain.base.Regatta regatta; private final IEventSubscriber eventSubscriber; private final IRaceSubscriber raceSubscriber; + private final IRacesListener racesListener; + private final IControlsListener controlsListener; + private final ICompetitorsListener competitorsListener; private final Set receivers; private final DomainFactory domainFactory; private final WindStore windStore; @@ -331,7 +335,16 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl // Initialize data controller using live and stored data sources ISubscriberFactory subscriberFactory = SubscriptionLocator.getSusbcriberFactory(); eventSubscriber = subscriberFactory.createEventSubscriber(tractracEvent, liveURI, effectifeStoredURI); - eventSubscriber.subscribeCompetitors(new ICompetitorsListener() { + controlsListener = new IControlsListener() { + @Override + public void updateControl(IControl control) {} + @Override + public void deleteControl(UUID controlId) {} + @Override + public void addControl(IControl control) {} + }; + eventSubscriber.subscribeControls(controlsListener); + competitorsListener = new ICompetitorsListener() { @Override public void updateCompetitor(ICompetitor competitor) { if (!competitor.isNonCompeting()) { @@ -349,8 +362,9 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl @Override public void addCompetitor(ICompetitor competitor) { } - }); - eventSubscriber.subscribeRaces(new IRacesListener() { + }; + eventSubscriber.subscribeCompetitors(competitorsListener); + racesListener = new IRacesListener() { @Override public void abandonRace(UUID raceId) {} @Override public void addRace(IRace race) {} @Override public void deleteRace(UUID raceId) {} @@ -369,7 +383,8 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl } } } - }); + }; + eventSubscriber.subscribeRaces(racesListener); // Start live and stored data streams final Regatta effectiveRegatta; raceSubscriber = subscriberFactory.createRaceSubscriber(tractracRace, liveURI, effectifeStoredURI); @@ -532,6 +547,9 @@ public class TracTracRaceTrackerImpl extends AbstractRaceTrackerImpl protected void onStop(boolean stopReceiversPreemtively) throws InterruptedException { if (!stopped) { stopped = true; + eventSubscriber.unsubscribeRaces(racesListener); + eventSubscriber.unsubscribeCompetitors(competitorsListener); + eventSubscriber.unsubscribeControls(controlsListener); raceSubscriber.stop(); eventSubscriber.stop(); raceSubscriber.unsubscribeConnectionStatus(this); From 5fb3a831183aa269e63623d819974e960b56a749 Mon Sep 17 00:00:00 2001 From: Papick Garcia Taboada Date: Tue, 20 Jun 2017 15:44:27 +0200 Subject: [PATCH 7/7] Removed duplicate release notes file, copied missing content to the "real one" --- .../resources/SailingAnalyticsNotes.html | 55 ++ .../home/solutions/SailingAnalyticsNotes.html | 630 ------------------ 2 files changed, 55 insertions(+), 630 deletions(-) delete mode 100644 java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/SailingAnalyticsNotes.html diff --git a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html index 233fb9ba1e6..20f7d5bc49c 100644 --- a/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html +++ b/java/com.sap.sailing.gwt.ui/src/main/java/com/sap/sailing/gwt/home/desktop/places/whatsnew/resources/SailingAnalyticsNotes.html @@ -4,6 +4,61 @@

What's New - SAP Sailing Analytics

+ +
May 2017
+
    +
  • It's now possible to show nationality flags in addition to competitor images in leaderboards if both are available. + If no competitor images are available, this won't have any effect. + You can activate this option through the leaderboard settings dialog to be found in various views. +
  • +
+ +
April 2017
+
    +
  • Several pages are now enabled to save user settings. In settings dialogs there is a "Make default" + button in the lower-left corner which is displayed when saving of settings is enabled. A click on that + button causes the system to use the new settings as default values for the same page for any other context opened, e.g. another event. + A click on OK button causes the system to store current settings for the current context and restore the same settings + when the user opens the page with the same context again unless there are specific parameters given by the URL. Settings saved this way are + stored locally in the browser's local storage and in the user profile if you are logged in. + Some settings are only valid in a specific context (e.g. the selected race columns to show are stored for a specific leaderboard only). + This feature required changing several URL parameters, which means that previously saved bookmarks might work slightly different. + Affected pages: +
      +
    • /gwt/Leaderboard.html
    • +
    • /gwt/RegattaOverview.html
    • +
    • Leaderboards embedded in /gwt/Home.html
    • +
    +
  • +
  • The maneuver loss calculation for penalty circles has been improved. Previously, the end of the + penalty circle maneuver was considered to be at the last of the two adjacent tack/gybe maneuvers. + This, usually, is earlier than when approaching the course over ground at the beginning of the + penalty circle again. With this, the extrapolation of the speed/course at the beginning of + the maneuver did not last long enough. Furthermore, penalties with strong accelerations during + the gybing phase could have led to an incorrect inference of the speed and course at the + beginning of the maneuver. Both of these issues have now been fixed, leading to good results + also in very specific and unusual speed developments during a penalty circle maneuver. +
  • +
  • The advantage line display has been fixed for legs changing their type (upwind/downwind/reach) + while the race is going on. It now uses the leg type at the same current time point that the + leaderboard uses for calculating the competitor ranking. +
  • +
  • Images in the overall image gallery ("Photographers love Sailing") are now linked with the + events to which they belong. Clicking the image navigates to the event page. +
  • +
+ +
March 2017
+
    +
  • The SAP Sailing Analytics are now also available in French, Portugese, as well as Spanish. + Find the language chooser in the lower right corner of the footer, or when signed in, go to your user + profile and set your preferred language. +
  • +
  • The application's locale now switches based on the client's "Accept-Language" HTTP header field. + This means that the application in all its parts will show in the user's default language if + supported, defaulting to English otherwise. +
  • +
February 2017
  • The definition of "ride height" for foiling sensors has been changed. Instead of showing diff --git a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/SailingAnalyticsNotes.html b/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/SailingAnalyticsNotes.html deleted file mode 100644 index 4c19eeae971..00000000000 --- a/java/com.sap.sailing.gwt.ui/src/main/resources/com/sap/sailing/gwt/home/solutions/SailingAnalyticsNotes.html +++ /dev/null @@ -1,630 +0,0 @@ - - - -
    -

    What's New - SAP Sailing Analytics

    -
    -
    May 2017
    -
      -
    • It's now possible to show nationality flags in addition to competitor images in leaderboards if both are available. - If no competitor images are available, this won't have any effect. - You can activate this option through the leaderboard settings dialog to be found in various views. -
    • -
    - -
    April 2017
    -
      -
    • Several pages are now enabled to save user settings. In settings dialogs there is a "Make default" - button in the lower-left corner which is displayed when saving of settings is enabled. A click on that - button causes the system to use the new settings as default values for the same page for any other context opened, e.g. another event. - A click on OK button causes the system to store current settings for the current context and restore the same settings - when the user opens the page with the same context again unless there are specific parameters given by the URL. Settings saved this way are - stored locally in the browser's local storage and in the user profile if you are logged in. - Some settings are only valid in a specific context (e.g. the selected race columns to show are stored for a specific leaderboard only). - This feature required changing several URL parameters, which means that previously saved bookmarks might work slightly different. - Affected pages: -
        -
      • /gwt/Leaderboard.html
      • -
      • /gwt/RegattaOverview.html
      • -
      • Leaderboards embedded in /gwt/Home.html
      • -
      -
    • -
    • The maneuver loss calculation for penalty circles has been improved. Previously, the end of the - penalty circle maneuver was considered to be at the last of the two adjacent tack/gybe maneuvers. - This, usually, is earlier than when approaching the course over ground at the beginning of the - penalty circle again. With this, the extrapolation of the speed/course at the beginning of - the maneuver did not last long enough. Furthermore, penalties with strong accelerations during - the gybing phase could have led to an incorrect inference of the speed and course at the - beginning of the maneuver. Both of these issues have now been fixed, leading to good results - also in very specific and unusual speed developments during a penalty circle maneuver. -
    • -
    • The advantage line display has been fixed for legs changing their type (upwind/downwind/reach) - while the race is going on. It now uses the leg type at the same current time point that the - leaderboard uses for calculating the competitor ranking. -
    • -
    • Images in the overall image gallery ("Photographers love Sailing") are now linked with the - events to which they belong. Clicking the image navigates to the event page. -
    • -
    - -
    March 2017
    -
      -
    • The SAP Sailing Analytics are now also available in French, Portugese, as well as Spanish. - Find the language chooser in the lower right corner of the footer, or when signed in, go to your user - profile and set your preferred language. -
    • -
    • The application's locale now switches based on the client's "Accept-Language" HTTP header field. - This means that the application in all its parts will show in the user's default language if - supported, defaulting to English otherwise. -
    • -
    - -
    February 2017
    -
      -
    • The definition of "ride height" for foiling sensors has been changed. Instead of showing - the ride height calculated by the sensor we are now showing the minimum of the port and - starboard ride heights. Therefore, this new definition of ride height can be seen as the - distance of lowest hull part from the water. -
    • -
    - -
    January 2017
    -
      -
    • An issue with wind-up map rotation which sometimes left the map centered over the African continent has been fixed. -
    • -
    - -
    October 2016
    -
      -
    • An issue with live re-calculation of the maximum speed over ground (SOG) values has been fixed. - In some circumstances it was possible that new live data did not properly update the MaxSOG figures. - Since this depended on the timing of read and write access it could have led to different values - for different server instances, giving confusing results. The fix provided now ensures that - the MaxSOG values are properly evaluated when new data has arrived. -
    • -
    • The buoy zone radius is now determined by a configurable regatta property. By default, it is - set to two hull lengths, and a regatta administrator may choose to change it to any other factor - such as 3 or 2.5. This determines the default used in the race viewer (RaceBoard.html). Users can - still change the radius in the map settings to any arbitrary number, deviating from what has - "officially" been set for the regatta. -
    • -
    - -
    August 2016
    -
      -
    • In order to prepare user-specific configuration support in additional parts of the application, - various modules now require a user account to be accessed. Please sign up and log in to use their - full functionality as usual. The following modules are affected: -
        -
      • /gwt/Simulator.html
      • -
      • /gwt/DataMining.html
      • -
      -
    • -
    - -
    July 2016
    -
      -
    • The Wind arrow in the top left corner of the race map / player now serves as a toggle button - for the wind streamlet overlay which can still also be reached through the map settings. -
    • -
    • If you want to analyze a race, the navigation slightly changed. The button, which is shown for tracked - races won't lead you to the race board directly, but provides a small menu. There you can choose from - several pre-defined analysis, which will launch the race board with respective configurations: -
        -
      • Replay/Watch live: Launches race board in play mode starting at race start, if race is already - finished (Replay) or at the actual time, if the race is currently live (Watch live) -
      • -
      • Race analysis: Shows the race results by setting the timer to the end of the race. - This option is only available, if the race is already finished. -
      • -
      • Start analysis: Shows the race start 10 sec after start and shows the speed over ground (SOG) of - the best starters within the competitor chart (zoomed to start). - This option is only available, if the race is already started. -
      • -
      • Winning lanes: Shows the full lanes of the best competitor, if race is already finished or the of - leading competitors if the race is currently live. -
      • -
      -
    • -
    • On the user profile page you now can maintain a list of you favorite boat classes and competitors. - You can also opt in for notification mails about your favorites to stay informed. - A requirement for notification mails to work for you is that you entered a valid mail address - to your user profile and verified the address by clicking the link that has been sent via email. -
    • -
    • Users now may choose their preferred language on the user profile page. - Currently only the mail notification mechanism will accept this setting to send mails in the preferred - language. This also prepares additional language sensitive feature, e.g. automatically switching the - website to your preferred language when logged in. -
    • -
    • The leaderboard now has a new measure value called "Ride height", which shows the ride height on - foils of a competitor, if respective data is available (currently ESS only). This information can - also be shown in the competitor chart on the RaceBaord page. -
    • -
    • To visualize multiple information at once, the competitor chart on the RaceBaord page can now be - configured to show two Y-axes. -
    • -
    - -
    May 2016
    -
      -
    • It is now possible to follow races on the RaceBoard, even if there's no wind data available.
    • -
    - -
    April 2016
    -
      -
    • The /gwt/RaceBoard.html entry point now accepts an optional parameter "t" in the form - [[hh:]mm:]ss, for example t=12:34 to place the timer so much after the race start - if the viewer comes up in Replay mode. You may also use just seconds, as in t=1234 which - makes specifying a time before the start somewhat easier (t=-123 instead of - t=-02:-03). -
    • -
    - -
    February 2016
    -
      -
    • The website now features a user registration and authentication interface. - User accounts are currently used to grant special permissions to administrators and moderators. - New user specific features (e.g. preferences) will be available in the next couple of months. -
    • -
    • The RaceBoard URL Parameter "canReplayDuringLiveRaces" has been removed. - This parameter was used to allow users to trigger autoplay for live races on non-live points in time. - The permission "CAN_REPLAY_DURING_LIVE_RACES" and role "moderator" now grant this functionality to users. -
    • -
    • Mark positions are no longer interpolated between fixes. Instead, when a mark's track is asked for the - mark's position, the last known position at or before the time point requested will be used. The - "jumping" of marks in the UI that may otherwise be caused by this is ameliorated by introducing - the same CSS animation for the marks that is already used for the boats. -
    • -
    - -
    January 2016
    -
      -
    • Show maneuver markers on the race board by default - (tacks, jibes and penalty circles) with a smaller icon, which - shows an info bubble when the marker is clicked. - The icon turns green if the direction change is to starboard - and red if to port. -
    • -
    - -
    December 2015
    -
      -
    • The advantage line is now animated and moves smoothly with the boats. -
    • -
    • New line hover behavior in race map. When the mouse is moved onto a line, for example the start line, a transparent, - wider line is activated behind it that acts as a buffer for the thin line, where the text displayed by the hover is still shown. - This makes it easier to hover over the lines. These wider lines can be made semi transparent instead of transparent - by unticking the corresponding checkbox in the settings of the race map. Furthermore the stroke weight of the wider lines can be - configured in the same settings dialog. -
    • -
    • The race viewer (RaceBoard.html) now intelligently chooses a layout that suits the screen size. - When only little horizontal space is available, only the sail number and nationality but not the - competitor's long name is displayed by default. When vertical space is scarce, competitor and wind - charts are disabled and more horizontal space can be given to the time slider at the bottom. The - leaderboard section can then be scaled to smaller widths and will reduce the column widths better - than in the past. -
    • -
    • With the changes described above, the RaceBoard is now also accessible from the races page on mobile devices. -
    • -
    • For overall / series leaderboards such as a national sailing league or the Extreme Sailing Series, the - live status of the overall leaderboard is now indicated. As long as a live race is ongoing, the respective - column in the overall leaderboard is highlighted, and a comment is displayed that indicates which regatta - is currently still live. -
    • -
    - -
    November 2015
    - -
      -
    • The smartphone page provides some structural navigation items for several pages in the upper right burger menu. - You will see links to e.g. events, regattas and series when navigating on event and series pages. -
    • -
    • It is now possible to filter for competitors in the race list of events/regattas. - If competitor information is available, there is a filter box in the upper right corner of the races page. - The filter is searching for competitors by sail ID as well as the competitor's name. -
    • -
    • Regattas can now be filtered by boat category in the smartphone page. - The filter will be shown for events with multiple regattas if there is more than on boat category associated with the event. -
    • -
    - -
    October 2015
    - -
      -
    • A variety of tablets will now show the desktop page by default. - The mobile version of the website is primarily designed to be used on smartphones, - but you can manually switch back to the mobile version by clicking the corresponding link in the page footer. -
    • -
    • There is some amount of caching on the web page to reduce the consumed traffic for mobile users. - Contents aren't loaded again on each page switch. This improves the traffic consumption and loading speed on fast page switches. -
    • -
    • The fullscreen image viewer (start page and event media page) can automatically slide through the images. - This can be turned on by clicking the corresponding icon in the upper right corner of the fullscreen viewer. -
    • -
    • There is an optimized search and results page for mobile devices now. -
    • -
    - -
    September 2015
    - -
      -
    • Races in the event/regatta race list can now be shown in 'competition format'. - In this format the races and flights are shown in a structural view. - In contrast to the default list view there is no grouping by live and finished states and all - (including upcoming) races/flights are shown in one structural view. -
    • -
    • The mobile page now provides regatta overview pages for events having multiple regattas. - This page shows live races, an excerpt of the current results as well as statistics for the regatta. -
    • -
    - -
    August 2015
    - -
      -
    • Series events appear as only one entry in the events list, with a marker that - this is only one of a certain number of events in a series. This way, the many series events - don't produce clutter in the events list. -
    • -
    • Bug fix: memory leaks for distance cache have been solved. The issue could - also lead to endless recursions, ultimately leading to server lock-ups. -
    • -
    • Bug fix: master data import detached race logs and regatta logs from database. - Log entries added after import on the importing server were not written to the - database and therefore were lost upon server restart. -
    • -
    • There is a smartphone-optimized version of the "solutions" page known from the desktop. -
    • -
    • The smartphone event history now has a slider that allows swiping through the events of a year. -
    • -
    - -
    July 2015
    - -
      -
    • Major improvements to the web page, with specific enhancements for smartphone - users. Race lists are now sortable by various criteria by clicking on the column - headers. A new fancy progress indicator now also provides visual feedback for - multi-fleet regattas. A news ticker box has been implemented, and the event - overview page can hold a video player on its stage. The event media gallery section - has been greatly improved. -
    • -
    • Fixed a bug in the wind streamlet visualization. Now the wind streamlets - correctly show the wind at the location selected also in wind-up mode. -
    • -
    • Added the "Course Geometry" option to the map display. With this option selected, - the map will show an info text for each leg and the start and finish line (only - for one of start/finish line if they are identical), telling the length of the leg - or line, respectively, and its angle to the wind. For the start and finish line - the advantage of the favored side is shown in meters. -
    • -
    • The SAP Sailing Analytics start page is now available in the smartphone version. - This page features some events that are currently interesting as well as showing a - quickfinder for a variety of recent and upcoming events. - Previously, the start page redirected to the event history. -
    • -
    - -
    May 2015
    - -
      -
    • Competitors can have a separate flag image, such as a club flag to be displayed instead of - the nationality flag usually displayed with the sail number. This enables national league events - to display the club flags consistently when all nation flags would be the same anyway. -
    • -
    • The wind estimation from the tracked courses over ground has been improved. It now takes into - account any polar data available for the boat class concerned. In particular, this helps to rule - out very unlikely gybing angles in downwind clusters that otherwise could have led to an incorrect - split of the downwind fleet into two clusters where actually all boats were on the same tack. -
    • -
    • Some significant improvements have been applied to the simulator feature which is embedded in the - race viewer since some time now. The simulator now copes with very shifty wind conditions and finds - solutions in scenarios where an upwind leg flips to a reach or even into a downwind leg over time. -
    • -
    - -
    April 2015
    - -
      -
    • There is a new map setting "Wind-Up (show wind from top of map)". Selecting it will - align the map's top with the combined true wind average direction around the time currently - selected in the time slider. In this new view there are limitations as to how far you can zoom - out. Furthermore, you will not be able to use the Satellite view with this setting. Tip: - if you want to re-align to wind-up after a major wind shift, un-check the setting, confirm - the setting change, then set it again. A compass shows the true north orientation. Clicking - the compass will toggle between wind-up and north-up display. -
    • -
    • Bug fixes around addition of the "Regatta Rank" column in the leaderboard which could have - distorted the leaderboard's column layout under certain conditions. -
    • -
    • Bug fix for cumulative points in the leaderboard ("Show added scores for each entry in leaderboard"). - With this setting, scores of competitors not tracked in the current race were not correctly represented - and may have stayed blank. -
    • -
    • Bug fix for the leg counter in the regatta page: when the first boat has finished, the leg counter went - to zero (e.g., "0 of 8"). This has now been fixed, and it will show, e.g., "8 of 8" upon the first boat - crossing the finish line. -
    • -
    - -
    March 2015
    - -
      -
    • Added competitor chart type for Course Over Ground (COG) -
    • -
    • Added leaderboard column and competitor chart entry for distances considering - gate starts. With gate starts, late starters sail a shorter distance in their first - leg. To still make distances comparable, a new metric has been introduced that for a gate - start adds the distance between the port side of the start line and the position where - the competitor started to the distance sailed in the first leg. -
    • -
    • The weights of the wind measurement units, the wind estimation and manual wind inputs by the - race committee are now used properly for the wind streamlet visualization. This fixes issues - with exaggerated weights for an estimated wind direction of very low confidence. -
    • -
    • The expand/collapse toggle button in the race viewer's time panel has correct arrows now. -
    • -
    - -
    February 2015
    - -
      -
    • The leaderboard offers a new column: "Start delay." It shows the number of seconds that passed - since the race start and the time the competitor passes the start mark / start line. As usual, - you can sort for this column. For line starts, this is a fairly direct measure of start quality - except for port starts which may have to be considered differently. For gate starts, you can use - this measure to find out about early and late starters and dig into possible correlations between - results and start time point. -
    • -
    - -
    January 2015
    - -
      -
    • The race viewer ("RaceBoard.html") now offers a new option in the map settings: - "Show simulation overlay". Checking this feature will simulate the current leader's leg - using different strategies and using the wind as it has been measured and estimated. - This feature becomes particularly interesting when combined with the "Show wind streamlets overlay" - feature that visualizes the spatial distribution of the wind field over the course. -
    • -
    • The race viewer is now more resilient against exceptional conditions such as the missing - of all wind information and the missing of a course with its waypoints. If no wind information - is available at all (no wind direction indicator is then shown in the upper left corner of - the map) then all ranking and distances will be based on a projection onto the course instead - of onto the wind direction, even for upwind and downwind legs. -
    • -
    - -
    September 2014
    - -
      -
    • The search feature now also includes results from currently ongoing events that may not yet be - included in our archive. Search keywords are now properly used in constructing the query. -
    • -
    - -
    August 2014
    - -
      -
    • The 2D race viewer, also known as the "Race Board," now comes with a fresh, new design. The orange - checkbox toggle buttons that open or close views such as the leaderboard or the charts for wind - and competitor comparison, have been replaced by tabs with ">>" symbols. Clicking on those - will expand or collapse the respective view. The views can now be resized using the handles attached - to the dividers separating the views. They work much better on mobile and touch devices than the splitters - used previously did. The settings buttons that used to be integrated with the orange toggle - checkbox buttons have now been moved into the views that they control. For example, the map settings - button is now displayed on the map, below the zoom control on the right map border. -
    • -
    • The leaderboard now has an improved search and filter control. Above the table displaying the - competitors and their figures is a new field with a text input that you can use to filter for - competitors, by their sail number or their name. While filtered, use the checkboxes in front of - the table row to add the competitor to or remove it from the selection. Use the "X" button to the - right of the search input field to clear the search. To the left of the search input field is a - filter symbol that you can use to select and create advanced filtering rules, such as filtering - by rank or nationality. Left of the filter button is the leaderboard settings button. -
    • -
    • The leaderboard has a new first column: a selection check box. We hope this makes it easier in - particular for users on mobile or other touch devices to select more than one competitor at a time. - The old-style multi-selection using the Control and Shift keys, as known well from typical desktop - file explorers, still work like they did before. Note that it is possible to sort for the checkbox - column, putting the selected competitors at the top of the leaderboard. -
    • -
    • When using the auto-zoom feature from the map settings, boats and wind streamlets will now - continue to move smoothly as long as the map only needs panning. Please also note that auto-zooming - to "tails" may slow down the display considerable, particularly when tracking at high frequency. -
    • -
    • When viewing a single race, the leaderboard now by default does no longer show the regatta rank as - the first column (and not as the second one either, given that the first column now usually shows - the checkboxes used for selecting items). If you'd like to get the regatta rank column back, also when - looking at a single race, please choose it from the leaderboard settings dialog where you can find it - in the "Overall Details" section. It may still be a nice column to have when following the last race - in a regatta. If you're just interested in the relative regatta rank ordering you may as well sort by - the right-most total points column which (surprising to some but welcome by most) sorts by the regatta - rank and not by the plain points sum (taking into account medal race and gold/silver/... fleet participations). -
    • -
    - -
    July 2014
    - -
      -
    • The advantage line now uses the wind at the position in the middle of the leg that the - competitor to which the line is attached is currently sailing in. Before, the "global" wind - average was used which is still displayed in the top left corner of the map. You can understand - the details by turning on the "Streamlet" wind visualization in the Map Settings. -
    • -
    • Faster wind streamlet loading: when you activate the streamlets for wind visualization, they now - display much faster. -
    • -
    • Faster boat position display: We found that what you're probably the most interested in when - you first open a race map or move the time slider to some very different point in time is the - position of the boats at that point in time; and you want to know it fast. - The tails which show how the boats got there are also interesting, but you won't blame us as much - if it takes a little bit to update them. So we've separated the placement of the boats from the - drawing of their tails. Now you may blame us for the tails temporarily not being attached to the - boats anymore ;-). -
    • -
    - -
    June 2014
    - -
      -
    • In the "Maps" settings in the RaceBoard page there is a new checkbox named - "Show wind streamlets overlay." If you select it and wait for about a minute, - a complete wind field will be calculated and visualized as an overlay on top - of the race course display. It reacts to you moving the time slider and shows - how the wind differs at different locations on the course. Stronger and faster-moving - streamlets mean stronger breeze. -
    • -
    • Wind is now calculated location-based. At locations closer to a sensor, that sensor - has a higher influence on the local wind field than sensors further away. The wind - estimations based on the courses sailed are assigned a location that corresponds with - the "center of gravity" of the field that was used for the estimation. For example, - if the fleet is approaching the windward mark and the wind sensor in on the committee - boat on the leeward side of the course, the course-based estimation influences the - assumed wind field more on the windward side whereas the sensor counts more for - the leeward side. -
    • -
    • Switched to the latest version of the Highcharts charting framework. This leads to improvements - especially on mobile devices. Charts can be zoomed using pinch gestures now. -
    • -
    • To support the use of the SAP Sailing Analytics also for live video streaming formats, we - introduced a new URL parameter viewShowMapControls for the Spectator.html - and the RaceBoard.html pages. Setting this parameter to "false" - will suppress the zoom, pan and map type selection - controls on the map display. Example: - http://danishleague2014.sapsailing.com/gwt/Spectator.html?leaderboardGroupName=Danish%20League%202014&showRaceDetails=true&viewShowMapControls=false. - When used for the Spectator.html URL, this will apply - to all RaceBoard.html pages (all races) reached from there. Of course, the map - can still be panned and zoomed using the usual mouse gestures. A left double click zooms - in, a right double click zooms out. Zoom can also be controlled using the mouse wheel. - And panning works by clicking on the map and dragging the mouse. Without the map type - control, switching to satellite view is not possible (keep this in mind when tracking - races in China, see below for Google's random coordinate transformations there). -
    • -
    - -
    May 2014
    - -
      -
    • Fixed an issue with the wind speed charts. Charts that only had a single, unchanged - wind speed crashed the browser. The solution scales the right hand side Y-axis denoting - the wind speed such that it always starts at 0kts. -
    • -
    • Analysis of tacks and jibes around mark passings has now been improved. Before, - a jibe followed by a tack that turns in the same direction (e.g., to port) so far - was always called a penalty maneuver. Now, if a mark passing separates such a tack - from such a jibe, no penalty is called anymore, and the two maneuvers are identified - correctly. -
    • -
    • Another note on maneuver analysis: we received questions regarding the maneuvers - involved in clearing an OCS. The SAP Sailing Analytics treat a clearing of - an OCS, going back behind the start line and crossing it again, as if the competitor - started again. In particular this means that all maneuvers prior to this new start - do not count for the metrics; neither does the distance sailed between the first (OCS) - start and the clear start. -
    • -
    • A hint regarding the "distance to start line" metrics: please keep in mind that currently this - measurement is calculated as the distance of the GPS tracker from the line. Unless the tracker - is mounted near the bow (or at the bow sprit, if any), the distance calculated this way will - be greater than what counts for crossing the line. Ordering, comparing and aggregating over this - figure still makes sense as long as you remember this detail. -
    • -
    • Introduced a new leaderboard entry "XTE +/-" featuring a signed cross-track error which shows - for a leg and also aggregated for an entire race on which side of the leg a competitor stayed on - average. Currently, the "side" is defined as viewing in the leg's direction, so "left" for a downwind - leg is the same side as "right" for the corresponding upwind leg. -
    • -
    - -
    April 2014
    - -
      -
    • China races, such as in Qingdao, may have the map background displayed at slightly wrong locations. - This is due to an intentional random transformation that Google applies to GPS locations for - China. You can circumvent this issue by switching to satellite view where the map background is displayed - correctly. -
    • -
    • Fixed a bug that in some cases has caused the wind chart, the advantage line, the wind indicator on the map - and the leaderboard to use slightly different wind values. The problem was related to different averaging and - damping methods used for these values which has now been aligned. -
    • -
    • Fixed a bug that caused the time spent in a leg, race or regatta to always show the time at the end of the leg - if the leg has been started by the competitor at the time point queried, e.g., by the time slider. While this - did not have any serious effects in live mode, for replay races this left the leg time and race time constant - at the total time spent so far up to the end of the last leg the competitor had started at that time. -
    • -
    - -
    March 2014
    - -
      -
    • Added a new setting for a leaderboard called "Show added scores". Using this feature one - can display added scores for each leaderboard entry. An added score is the sum over all scores - from the first column to the current column. One can describe it as an intermediate overall score. - This feature can be used to know the score until a specific race while another race is live and - constantly changes the overall score.
    • - -
    • Added tooltips for leaderboard settings.
    • - -
    • Fixed HighPointOverall scheme to also break a tie by looking at the ranks of the last regatta.
    • - -
    • Progress indicator when expanding a race in the RaceBoard view
    • - -
    • Leaderboard column "GPS Lag" shows how far a competitor's GPS signal lags behind the average - tracking quality. A background color bar classifies into good/ok/bad (green/yellow/red).
    • -
    - -
    February 2014
    - -
      -
    • Fixed an issue with boat rotation on Safari browsers
    • - -
    • Maneuver analysis now counts tacks and jibes based on wind - at boat position and not based on leg type; subsequently, - penalty circle analysis is now based on a tack and a jibe - in close succession instead of a full circle.
    • - -
    • Regatta rank and overall rank in competitor chart of leaderboard panel - has been fixed; it did erroneously display the rank of the particular - race, not the regatta/overall rank after that race.
    • - -
    • Regatta overview now also displays all regatta leaderboards
    • - -
    • Boat colors match tail colors
    • - -
    • When selecting boats in leaderboard, de-selected boats' tails turn grey
    • - -
    - - -
    January 2014
    - -
      -
    • Boat movement animated smoothly
    • - -
    • Start and Finish line show line parameters (length, angle to wind, - advantage) when mouse hovers over them
    • - -
    • Start line has a count-down indicator displayed next to it - before the start. The count-down is also displayed underneath the time - slider
    • - -
    • Competitor and Wind charts show pre-start data where possible
    • - -
    • Competitors can be assigned a fixed color which they will keep - for all races in a regatta
    • - -
    • By default, videos are now shown in a moveable, resizable box embedded in - the web page. For a two screen setup, the embedded box can be popped out to a - dedicated browser window.
    • - -
    • Official score corrections for historic races are only applied after - the respective competitor finished the race. Therefore, in-between race - scores now correspond with tracking, regardless of final corrected score.
    • - -
    • By using negative playback speed (e.g., -1), replays can be run backwards.
    • - -
    -
    -
    - -