From 29284adfa45cc1f270b3ff581d303fffb9bf4d55 Mon Sep 17 00:00:00 2001 From: Axel Uhl Date: Mon, 23 Mar 2015 22:25:40 +0100 Subject: [PATCH] consolidated acquisition of mark position from tracked races, regatta log and race logs --- .../tracking/impl/MongoGPSFixStoreImpl.java | 1 - .../leaderboard/FlexibleLeaderboard.java | 4 +- .../leaderboard/RegattaLeaderboard.java | 4 +- .../LeaderboardThatHasRegattaLike.java | 6 +++ .../gwt/ui/server/SailingServiceImpl.java | 28 ++++------ .../jaxrs/api/LeaderboardsResource.java | 53 ++++--------------- .../sailing/server/RacingEventService.java | 20 +++++++ .../server/impl/RacingEventServiceImpl.java | 47 ++++++++++++++++ 8 files changed, 97 insertions(+), 66 deletions(-) create mode 100644 java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/LeaderboardThatHasRegattaLike.java diff --git a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoGPSFixStoreImpl.java b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoGPSFixStoreImpl.java index 5d182ae9522..f37945d1ed9 100755 --- a/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoGPSFixStoreImpl.java +++ b/java/com.sap.sailing.domain.persistence/src/com/sap/sailing/domain/persistence/racelog/tracking/impl/MongoGPSFixStoreImpl.java @@ -120,7 +120,6 @@ public class MongoGPSFixStoreImpl implements MongoGPSFixStore { public void loadMarkTrack(DynamicGPSFixTrack track, AbstractLog log, Mark mark) throws NoCorrespondingServiceRegisteredException, TransformationException{ List> mappings = new DeviceMarkMappingFinder<>(log).analyze().get(mark); - if (mappings != null) { for (DeviceMapping mapping : mappings) { loadTrack(track, mapping.getDevice(), mapping.getTimeRange().from(), mapping.getTimeRange().to(), true /*inclusive*/); diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleLeaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleLeaderboard.java index f2b4185d949..0ba0e2d3119 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleLeaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/FlexibleLeaderboard.java @@ -6,8 +6,8 @@ import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.base.Series; import com.sap.sailing.domain.common.Renamable; -import com.sap.sailing.domain.regattalike.HasRegattaLike; import com.sap.sailing.domain.regattalike.IsRegattaLike; +import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; import com.sap.sailing.domain.tracking.TrackedRace; /** @@ -23,7 +23,7 @@ import com.sap.sailing.domain.tracking.TrackedRace; * @author Axel Uhl (D043530) * */ -public interface FlexibleLeaderboard extends Leaderboard, Renamable, HasRegattaLike, IsRegattaLike { +public interface FlexibleLeaderboard extends LeaderboardThatHasRegattaLike, Renamable, IsRegattaLike { /** * Moves the column with the name name up. * @param name The name of the column to move. diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/RegattaLeaderboard.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/RegattaLeaderboard.java index 3f67fd35cef..17d07147edc 100755 --- a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/RegattaLeaderboard.java +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/leaderboard/RegattaLeaderboard.java @@ -2,8 +2,8 @@ package com.sap.sailing.domain.leaderboard; import com.sap.sailing.domain.base.Regatta; import com.sap.sailing.domain.common.Renamable; -import com.sap.sailing.domain.regattalike.HasRegattaLike; +import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; -public interface RegattaLeaderboard extends Leaderboard, Renamable, HasRegattaLike { +public interface RegattaLeaderboard extends Renamable, LeaderboardThatHasRegattaLike { Regatta getRegatta(); } diff --git a/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/LeaderboardThatHasRegattaLike.java b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/LeaderboardThatHasRegattaLike.java new file mode 100644 index 00000000000..6899eb31421 --- /dev/null +++ b/java/com.sap.sailing.domain/src/com/sap/sailing/domain/regattalike/LeaderboardThatHasRegattaLike.java @@ -0,0 +1,6 @@ +package com.sap.sailing.domain.regattalike; + +import com.sap.sailing.domain.leaderboard.Leaderboard; + +public interface LeaderboardThatHasRegattaLike extends Leaderboard, HasRegattaLike { +} 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 8467ef2ce87..6934a5d975b 100755 --- 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 @@ -71,14 +71,12 @@ import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.gate.Readon import com.sap.sailing.domain.abstractlog.race.state.racingprocedure.rrs26.ReadonlyRRS26RacingProcedure; import com.sap.sailing.domain.abstractlog.race.tracking.RaceLogCloseOpenEndedDeviceMappingEvent; import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.DefinedMarkFinder; -import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogLastPingMappingEventFinder; import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogOpenEndedDeviceMappingCloser; import com.sap.sailing.domain.abstractlog.race.tracking.analyzing.impl.RaceLogTrackingStateAnalyzer; import com.sap.sailing.domain.abstractlog.regatta.RegattaLog; import com.sap.sailing.domain.abstractlog.shared.analyzing.DeviceCompetitorMappingFinder; import com.sap.sailing.domain.abstractlog.shared.analyzing.DeviceMarkMappingFinder; import com.sap.sailing.domain.abstractlog.shared.analyzing.RegisteredCompetitorsAnalyzer; -import com.sap.sailing.domain.abstractlog.shared.events.DeviceMappingEvent; import com.sap.sailing.domain.base.BoatClass; import com.sap.sailing.domain.base.Competitor; import com.sap.sailing.domain.base.ControlPoint; @@ -220,6 +218,7 @@ import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.impl.DeviceMappingImpl; import com.sap.sailing.domain.regattalike.HasRegattaLike; +import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.swisstimingadapter.StartList; import com.sap.sailing.domain.swisstimingadapter.SwissTimingAdapter; @@ -253,7 +252,6 @@ import com.sap.sailing.domain.tracking.WindLegTypeAndLegBearingCache; import com.sap.sailing.domain.tracking.WindPositionMode; import com.sap.sailing.domain.tracking.WindTrack; import com.sap.sailing.domain.tracking.WindWithConfidence; -import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl; import com.sap.sailing.domain.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.tracking.impl.GPSFixMovingImpl; import com.sap.sailing.domain.tracking.impl.WindImpl; @@ -4884,28 +4882,18 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S return existing; } } - Serializable id = UUID.randomUUID(); - return baseDomainFactory.getOrCreateMark(id, dto.getName(), dto.type, dto.color, dto.shape, dto.pattern); } /** * Also finds the last position of the marks, if set by pinging them */ - private Collection convertToMarkDTOs(RaceLog raceLog, Iterable marks) { + private Collection convertToMarkDTOs(LeaderboardThatHasRegattaLike leaderboard, RaceLog raceLog, Iterable marks) { List dtos = new ArrayList(); + final TimePoint now = MillisecondsTimePoint.now(); for (Mark mark : marks) { - DynamicGPSFixTrackImpl track = new DynamicGPSFixTrackImpl(mark, 0); - DeviceMappingEvent lastPingMappingEvent = new RaceLogLastPingMappingEventFinder(raceLog, mark).analyze(); - if (lastPingMappingEvent != null) { - try { - getService().getGPSFixStore().loadMarkTrack(track, DeviceMappingImpl.convertToDeviceMapping(lastPingMappingEvent)); - } catch (TransformationException | NoCorrespondingServiceRegisteredException e) { - logger.log(Level.WARNING, "Could not convert to Mark DTO: " + mark); - } - } - Position lastPos = track.getLastRawFix() == null ? null : track.getLastRawFix().getPosition(); + final Position lastPos = getService().getMarkPosition(mark, leaderboard, now, raceLog); dtos.add(convertToMarkDTO(mark, lastPos)); } return dtos; @@ -4924,14 +4912,14 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S public Iterable getMarksInRaceLog(String leaderboardName, String raceColumnName, String fleetName) { RaceLog raceLog = getRaceLog(leaderboardName, raceColumnName, fleetName); Iterable marks = new DefinedMarkFinder(raceLog).analyze(); - return convertToMarkDTOs(raceLog, marks); + return convertToMarkDTOs((LeaderboardThatHasRegattaLike) getService().getLeaderboardByName(leaderboardName), raceLog, marks); } @Override public Iterable getMarksInRaceLogsAndTrackedRaces(String leaderboardName) { Leaderboard leaderboard = getService().getLeaderboardByName(leaderboardName); if (leaderboard == null) { - //TODO: implement proper Exception Handling + // TODO: implement proper Exception Handling return null; } Set markDTOs = new HashSet<>(); @@ -4948,7 +4936,9 @@ public class SailingServiceImpl extends ProxiedRemoteServiceServlet implements S marksById.put(markFromTrackedRace.getId(), markFromTrackedRace); } } - Util.addAll(convertToMarkDTOs(raceLog, marksById.values()), markDTOs); + Util.addAll(convertToMarkDTOs( + (LeaderboardThatHasRegattaLike) getService().getLeaderboardByName(leaderboardName), + raceLog, marksById.values()), markDTOs); } } return markDTOs; diff --git a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java index 76325a2ebfa..b962165a92f 100644 --- a/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java +++ b/java/com.sap.sailing.server.gateway/src/com/sap/sailing/server/gateway/jaxrs/api/LeaderboardsResource.java @@ -54,6 +54,7 @@ import com.sap.sailing.domain.common.Distance; import com.sap.sailing.domain.common.LeaderboardNameConstants; import com.sap.sailing.domain.common.MaxPointsReason; import com.sap.sailing.domain.common.NoWindException; +import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.dto.CompetitorDTO; import com.sap.sailing.domain.common.dto.FleetDTO; import com.sap.sailing.domain.common.dto.LeaderboardDTO; @@ -64,18 +65,16 @@ import com.sap.sailing.domain.common.racelog.RaceLogServletConstants; import com.sap.sailing.domain.common.racelog.tracking.DeviceMappingConstants; import com.sap.sailing.domain.leaderboard.Leaderboard; import com.sap.sailing.domain.leaderboard.SettableScoreCorrection; -import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapter; import com.sap.sailing.domain.racelogtracking.RaceLogTrackingAdapterFactory; import com.sap.sailing.domain.racelogtracking.impl.SmartphoneUUIDIdentifierImpl; import com.sap.sailing.domain.regattalike.HasRegattaLike; import com.sap.sailing.domain.regattalike.IsRegattaLike; +import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; import com.sap.sailing.domain.tracking.GPSFix; -import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.TrackedRace; -import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl; import com.sap.sailing.domain.tracking.impl.GPSFixImpl; import com.sap.sailing.domain.tracking.impl.GPSFixTrackImpl; import com.sap.sailing.server.RacingEventService; @@ -654,9 +653,10 @@ public class LeaderboardsResource extends AbstractSailingServerResource { return Response.status(Status.NOT_FOUND).entity("Could not find a mark with ID '" + markId + "'.") .type(MediaType.TEXT_PLAIN).build(); } - - GPSFix lastKnownFix = getLastKnownFix(leaderboard, mark, regattaLog); - + MillisecondsTimePoint now = MillisecondsTimePoint.now(); + Position lastKnownPosition = getService().getMarkPosition(mark, + (LeaderboardThatHasRegattaLike) leaderboard, now, + /* raceLog==null means use all race logs */ null); GPSFix fix = null; try { Object requestBody = JSONValue.parseWithException(json); @@ -671,11 +671,11 @@ public class LeaderboardsResource extends AbstractSailingServerResource { RaceLogTrackingAdapter adapter = getRaceLogTrackingAdapter(); RacingEventService service = getService(); try { - if (lastKnownFix != null) { + if (lastKnownPosition != null) { // ping again to avoid interpolation, avoid filtering by paying attention to maxSpeedForSmoothing - final Distance distance = lastKnownFix.getPosition().getDistance(fix.getPosition()); + final Distance distance = lastKnownPosition.getDistance(fix.getPosition()); final Duration minDuration = GPSFixTrackImpl.getDefaultMaxSpeedForSmoothing().getDuration(distance); - final GPSFix repeatedFixAvoidingCreepingMark = new GPSFixImpl(lastKnownFix.getPosition(), fix.getTimePoint().minus(minDuration)); + final GPSFix repeatedFixAvoidingCreepingMark = new GPSFixImpl(lastKnownPosition, fix.getTimePoint().minus(minDuration)); adapter.pingMark(regattaLog, mark, repeatedFixAvoidingCreepingMark, service); } adapter.pingMark(regattaLog, mark, fix, service); @@ -684,43 +684,12 @@ public class LeaderboardsResource extends AbstractSailingServerResource { logger.log(Level.WARNING, "Could not ping mark " + mark.getName()); } - if (lastKnownFix != null) { - JSONObject lastKnownFixJson = fixSerializer.serialize(lastKnownFix); + if (lastKnownPosition != null) { + JSONObject lastKnownFixJson = fixSerializer.serialize(new GPSFixImpl(lastKnownPosition, now)); String fixJson = lastKnownFixJson.toJSONString(); return Response.ok(fixJson, MediaType.APPLICATION_JSON).build(); } else { return Response.ok().build(); } } - - /** - * Looks at the mark tracks in the tracked races attached to the leaderboard - * and if it doesn't find the mark requested there, looks in the regattaLog - * for device mappings for the mark and tried to load fixes from the {@link GPSFixStore}. - * The latter is only necessary if the mark isn't found in any tracked race because should there - * be a tracked race in the leaderboard that has the mark, it will also have received the fixes - * from the {@link GPSFixStore} through the regatta log mapping. - */ - private GPSFix getLastKnownFix(Leaderboard leaderboard, Mark mark, RegattaLog regattaLog) { - GPSFixTrack track = null; - for (TrackedRace trackedRace : leaderboard.getTrackedRaces()) { - if (Util.contains(trackedRace.getMarks(), mark)) { - track = trackedRace.getOrCreateTrack(mark); - break; - } - } - if (track == null) { // not found in any tracked race, or no tracked races found - // try to load from store - DynamicGPSFixTrackImpl loadedTrack = new DynamicGPSFixTrackImpl(mark, 0); - track = loadedTrack; - try { - getService().getGPSFixStore().loadMarkTrack(loadedTrack, regattaLog, mark); - } catch (Exception e) { - logger.log(Level.WARNING, "Couldn't load mark track for mark "+mark, e); - return null; - } - } - GPSFix lastKnownFix = track.getLastFixAtOrBefore(MillisecondsTimePoint.now()); - return lastKnownFix; - } } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java index 0be79ec25ef..bd3353afcc9 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/RacingEventService.java @@ -24,6 +24,7 @@ import com.sap.sailing.domain.base.EventBase; import com.sap.sailing.domain.base.Fleet; import com.sap.sailing.domain.base.LeaderboardSearchResult; import com.sap.sailing.domain.base.LeaderboardSearchResultBase; +import com.sap.sailing.domain.base.Mark; import com.sap.sailing.domain.base.RaceColumn; import com.sap.sailing.domain.base.RaceDefinition; import com.sap.sailing.domain.base.Regatta; @@ -35,6 +36,7 @@ import com.sap.sailing.domain.base.configuration.DeviceConfigurationIdentifier; import com.sap.sailing.domain.base.configuration.DeviceConfigurationMatcher; import com.sap.sailing.domain.base.configuration.RegattaConfiguration; import com.sap.sailing.domain.common.DataImportProgress; +import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RaceFetcher; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaFetcher; @@ -54,6 +56,7 @@ import com.sap.sailing.domain.leaderboard.ScoringScheme; import com.sap.sailing.domain.persistence.DomainObjectFactory; import com.sap.sailing.domain.persistence.MongoObjectFactory; import com.sap.sailing.domain.racelog.tracking.GPSFixStore; +import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.RaceListener; import com.sap.sailing.domain.tracking.RaceTracker; @@ -124,6 +127,22 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch */ Leaderboard getLeaderboardByName(String name); + /** + * Looks at the mark tracks in the tracked races attached to the leaderboard. If it doesn't find a + * track for the mark requested there which has fixes before and after timePoint (to + * ensure that no track cropping has taken place, removing the fixes for the interesting time period), looks in the + * leaderboard's regatta log and the specific raceLog (if provided) or all race logs attached to the + * leaderboard (if not provided) for device mappings for the mark and tries to load fixes from the + * {@link GPSFixStore}. The latter is only necessary if the mark isn't found in any tracked race with fixes + * surrounding timePoint because should there be a tracked race in the leaderboard that has the mark + * then it will also have received the fixes from the {@link GPSFixStore} through the regatta log mapping. + *

+ * + * @return the position obtained by interpolation but never extrapolation from the track identified as described + * above + */ + Position getMarkPosition(Mark mark, LeaderboardThatHasRegattaLike leaderboard, TimePoint timePoint, RaceLog raceLog); + /** * Stops tracking all races of the regatta specified. This will also stop tracking wind for all races of this regatta. * See {@link #stopTrackingWind(Regatta, RaceDefinition)}. If there were multiple calls to @@ -581,4 +600,5 @@ public interface RacingEventService extends TrackedRegattaRegistry, RegattaFetch FileStorageManagementService getFileStorageManagementService(); ClassLoader getCombinedMasterDataClassLoader(); + } diff --git a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java index 877571ab3bb..1725e4075cc 100755 --- a/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java +++ b/java/com.sap.sailing.server/src/com/sap/sailing/server/impl/RacingEventServiceImpl.java @@ -43,6 +43,7 @@ import org.osgi.framework.BundleContext; import org.osgi.framework.ServiceReference; import org.osgi.util.tracker.ServiceTracker; +import com.sap.sailing.domain.abstractlog.AbstractLog; import com.sap.sailing.domain.abstractlog.AbstractLogEventAuthor; import com.sap.sailing.domain.abstractlog.impl.LogEventAuthorImpl; import com.sap.sailing.domain.abstractlog.race.RaceLog; @@ -85,6 +86,7 @@ import com.sap.sailing.domain.base.impl.RegattaImpl; import com.sap.sailing.domain.base.impl.RemoteSailingServerReferenceImpl; import com.sap.sailing.domain.common.DataImportProgress; import com.sap.sailing.domain.common.LeaderboardNameConstants; +import com.sap.sailing.domain.common.Position; import com.sap.sailing.domain.common.RegattaAndRaceIdentifier; import com.sap.sailing.domain.common.RegattaIdentifier; import com.sap.sailing.domain.common.RegattaName; @@ -125,11 +127,13 @@ import com.sap.sailing.domain.racelog.RaceLogStore; import com.sap.sailing.domain.racelog.tracking.GPSFixStore; import com.sap.sailing.domain.racelogtracking.DeviceIdentifier; import com.sap.sailing.domain.regattalike.IsRegattaLike; +import com.sap.sailing.domain.regattalike.LeaderboardThatHasRegattaLike; import com.sap.sailing.domain.regattalog.RegattaLogStore; import com.sap.sailing.domain.tracking.DynamicTrackedRace; import com.sap.sailing.domain.tracking.DynamicTrackedRegatta; import com.sap.sailing.domain.tracking.GPSFix; import com.sap.sailing.domain.tracking.GPSFixMoving; +import com.sap.sailing.domain.tracking.GPSFixTrack; import com.sap.sailing.domain.tracking.MarkPassing; import com.sap.sailing.domain.tracking.RaceChangeListener; import com.sap.sailing.domain.tracking.RaceHandle; @@ -144,6 +148,7 @@ import com.sap.sailing.domain.tracking.WindStore; import com.sap.sailing.domain.tracking.WindTracker; import com.sap.sailing.domain.tracking.WindTrackerFactory; import com.sap.sailing.domain.tracking.impl.AbstractRaceChangeListener; +import com.sap.sailing.domain.tracking.impl.DynamicGPSFixTrackImpl; import com.sap.sailing.domain.tracking.impl.DynamicTrackedRegattaImpl; import com.sap.sailing.expeditionconnector.ExpeditionWindTrackerFactory; import com.sap.sailing.polars.PolarDataService; @@ -970,6 +975,48 @@ public class RacingEventServiceImpl implements RacingEventService, ClearStateTes return leaderboardsByName.get(name); } + @Override + public Position getMarkPosition(Mark mark, LeaderboardThatHasRegattaLike leaderboard, TimePoint timePoint, RaceLog raceLog) { + GPSFixTrack track = null; + for (TrackedRace trackedRace : leaderboard.getTrackedRaces()) { + if (Util.contains(trackedRace.getMarks(), mark)) { + GPSFixTrack trackCandidate = trackedRace.getOrCreateTrack(mark); + if (spansTimePoint(trackCandidate, timePoint)) { + track = trackCandidate; + break; + } + } + } + if (track == null) { // no spanning track found in any tracked race, or no tracked races found + // try to load from store + DynamicGPSFixTrackImpl loadedTrack = new DynamicGPSFixTrackImpl(mark, 0); + track = loadedTrack; + Set> logs = new HashSet<>(); + logs.add(leaderboard.getRegattaLike().getRegattaLog()); + if (raceLog == null) { // no race log explicitly provided --> use all race logs + for (RaceColumn raceColumn : leaderboard.getRaceColumns()) { + for (Fleet fleet : raceColumn.getFleets()) { + logs.add(raceColumn.getRaceLog(fleet)); + } + } + } else { + logs.add(raceLog); + } + for (AbstractLog log : logs) { + try { + getGPSFixStore().loadMarkTrack(loadedTrack, log, mark); + } catch (Exception e) { + logger.log(Level.WARNING, "Couldn't load mark track for mark " + mark + " from log " + log, e); + } + } + } + return track.getEstimatedPosition(timePoint, /* extrapolate */ false); + } + + private boolean spansTimePoint(GPSFixTrack track, TimePoint timePoint) { + return track.getLastFixAtOrBefore(timePoint) != null && track.getFirstFixAtOrAfter(timePoint) != null; + } + @Override public Map getLeaderboards() { return Collections.unmodifiableMap(new HashMap(leaderboardsByName));