bug5932: added statistics on leg/race of competitor for tack type ratio

This commit is contained in:
Axel Uhl
2023-12-27 18:57:57 +01:00
parent 288708c551
commit 7467348f50
16 changed files with 257 additions and 20 deletions
@@ -269,4 +269,6 @@ InTrackingInterval=In tracking interval
NumberOfCompetitors=Number of Competitors
CompetitorInLeaderboard=Competitor in Leaderboard
CompetitorSailingDomainRetrieverChain=Competitors in Leaderboards
SmoothedSpeed=Smoothed Speed
SmoothedSpeed=Smoothed Speed
RatioDistanceLongVsShortTack=Ratio distance long tack / short tack
RatioDurationLongVsShortTack=Ratio duration long tack / short tack
@@ -267,4 +267,6 @@ InTrackingInterval=Im Tracking-Zeitraum der Wettfahrt
NumberOfCompetitors=Anzahl Teilnehmer
CompetitorInLeaderboard=Teilnehmer aus Rangliste
CompetitorSailingDomainRetrieverChain=Teilnehmer in Ranglisten
SmoothedSpeed=Geglättete Geschwindigkeit
SmoothedSpeed=Geglättete Geschwindigkeit
RatioDistanceLongVsShortTack=Verhältnis gesegelte Distanz auf Streck- zu Holebug
RatioDurationLongVsShortTack=Verhältnis gesegelte Dauer auf Streck- zu Holebug
@@ -89,7 +89,9 @@ public class SailingDataRetrievalChainDefinitions {
//
final DataRetrieverChainDefinition<RacingEventService, HasRaceOfCompetitorContext> raceOfCompetitorRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
trackedRaceRetrieverChainDefinition, HasRaceOfCompetitorContext.class, "RaceOfCompetitorSailingDomainRetrieverChain");
raceOfCompetitorRetrieverChainDefinition.endWith(TrackedRaceRetrievalProcessor.class, RaceOfCompetitorRetrievalProcessor.class, HasRaceOfCompetitorContext.class, "Competitor");
// use TackTypeSegmentsDataMiningSettings here to support the per-leg statistics about tack type segments
raceOfCompetitorRetrieverChainDefinition.endWith(TrackedRaceRetrievalProcessor.class, RaceOfCompetitorRetrievalProcessor.class, HasRaceOfCompetitorContext.class,
TackTypeSegmentsDataMiningSettings.class, TackTypeSegmentsDataMiningSettings.createDefaultSettings(), "Competitor");
dataRetrieverChainDefinitions.add(raceOfCompetitorRetrieverChainDefinition);
//
final DataRetrieverChainDefinition<RacingEventService, HasCompetitorDayContext> competitorDayRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
@@ -123,8 +125,9 @@ public class SailingDataRetrievalChainDefinitions {
//
final DataRetrieverChainDefinition<RacingEventService, HasTrackedLegOfCompetitorContext> legOfCompetitorRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
legRetrieverChainDefinition, HasTrackedLegOfCompetitorContext.class, "LegOfCompetitorSailingDomainRetrieverChain");
// use TackTypeSegmentsDataMiningSettings here to support the per-leg statistics about tack type segments
legOfCompetitorRetrieverChainDefinition.endWith(TrackedLegRetrievalProcessor.class, TrackedLegOfCompetitorRetrievalProcessor.class,
HasTrackedLegOfCompetitorContext.class, "LegOfCompetitor");
HasTrackedLegOfCompetitorContext.class, TackTypeSegmentsDataMiningSettings.class, TackTypeSegmentsDataMiningSettings.createDefaultSettings(), "LegOfCompetitor");
dataRetrieverChainDefinitions.add(legOfCompetitorRetrieverChainDefinition);
//
final DataRetrieverChainDefinition<RacingEventService, HasGPSFixContext> gpsFixRetrieverChainDefinition = new SimpleDataRetrieverChainDefinition<>(
@@ -172,4 +172,10 @@ public interface HasRaceOfCompetitorContext {
@Statistic(messageKey="AverageRaceWindSpeed")
Speed getAverageRaceWindSpeed();
@Statistic(messageKey="RatioDurationLongVsShortTack", resultDecimals=2)
public double getRatioDurationLongVsShortTack();
@Statistic(messageKey="RatioDistanceLongVsShortTack", resultDecimals=2)
public double getRatioDistanceLongVsShortTack();
}
@@ -24,6 +24,9 @@ public interface HasTackTypeSegmentContext {
@Dimension(messageKey="LegType")
LegType getLegType() throws NoWindException;
@Dimension(messageKey="LegNumber")
int getLegNumber();
@Dimension(messageKey="TackTypeSegmentName")
String getName();
@@ -109,4 +109,10 @@ public interface HasTrackedLegOfCompetitorContext extends HasWindOnTrackedLeg {
@Statistic(messageKey="VMG", resultDecimals=2)
public Speed getVelocityMadeGood();
@Statistic(messageKey="RatioDurationLongVsShortTack", resultDecimals=2)
public double getRatioDurationLongVsShortTack();
@Statistic(messageKey="RatioDistanceLongVsShortTack", resultDecimals=2)
public double getRatioDistanceLongVsShortTack();
}
@@ -7,17 +7,26 @@ import java.util.concurrent.ExecutorService;
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.datamining.impl.data.RaceOfCompetitorWithContext;
import com.sap.sailing.datamining.shared.TackTypeSegmentsDataMiningSettings;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
public class RaceOfCompetitorRetrievalProcessor extends AbstractRetrievalProcessor<HasTrackedRaceContext, HasRaceOfCompetitorContext> {
/**
* Settings will be used to control the retrieval of tack type segments for the
* corresponding statistics such as distance/duration relation between long and
* short tack for the race of the competitor
*/
private final TackTypeSegmentsDataMiningSettings settings;
public RaceOfCompetitorRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasRaceOfCompetitorContext, ?>> resultReceivers, int retrievalLevel,
String retrievedDataTypeMessageKey) {
super(HasTrackedRaceContext.class, HasRaceOfCompetitorContext.class, executor, resultReceivers, retrievalLevel,
Collection<Processor<HasRaceOfCompetitorContext, ?>> resultReceivers,
TackTypeSegmentsDataMiningSettings settings, int retrievalLevel, String retrievedDataTypeMessageKey) {
super(HasTrackedRaceContext.class,
HasRaceOfCompetitorContext.class, executor, resultReceivers, retrievalLevel,
retrievedDataTypeMessageKey);
this.settings = settings;
}
@Override
@@ -28,7 +37,7 @@ public class RaceOfCompetitorRetrievalProcessor extends AbstractRetrievalProcess
if (isAborted()) {
break;
}
HasRaceOfCompetitorContext raceOfCompetitorWithContext = new RaceOfCompetitorWithContext(element, competitor);
HasRaceOfCompetitorContext raceOfCompetitorWithContext = new RaceOfCompetitorWithContext(element, competitor, settings);
raceOfCompetitorsWithContext.add(raceOfCompetitorWithContext);
}
}
@@ -37,7 +37,7 @@ public class TackTypeSegmentRetrievalProcessor extends AbstractRetrievalProcesso
}
@Override
protected Iterable<HasTackTypeSegmentContext> retrieveData(HasRaceOfCompetitorContext element) {
public Iterable<HasTackTypeSegmentContext> retrieveData(HasRaceOfCompetitorContext element) {
List<HasTackTypeSegmentContext> tackTypeSegments = new ArrayList<>();
final TrackedRace trackedRace = element.getTrackedRaceContext().getTrackedRace();
final TimePoint startOfRace = element.getTrackedRaceContext().getTrackedRace().getStartOfRace();
@@ -7,17 +7,25 @@ import java.util.concurrent.ExecutorService;
import com.sap.sailing.datamining.data.HasTrackedLegContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.datamining.impl.data.TrackedLegOfCompetitorWithContext;
import com.sap.sailing.datamining.shared.TackTypeSegmentsDataMiningSettings;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
public class TrackedLegOfCompetitorRetrievalProcessor extends AbstractRetrievalProcessor<HasTrackedLegContext, HasTrackedLegOfCompetitorContext> {
/**
* Settings will be used to control the retrieval of tack type segments for the
* corresponding statistics such as distance/duration relation between long and
* short tack for the leg of the competitor
*/
private final TackTypeSegmentsDataMiningSettings settings;
public TrackedLegOfCompetitorRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasTrackedLegOfCompetitorContext, ?>> resultReceivers, int retrievalLevel,
String retrievedDataTypeMessageKey) {
Collection<Processor<HasTrackedLegOfCompetitorContext, ?>> resultReceivers,
TackTypeSegmentsDataMiningSettings settings, int retrievalLevel, String retrievedDataTypeMessageKey) {
super(HasTrackedLegContext.class, HasTrackedLegOfCompetitorContext.class, executor, resultReceivers,
retrievalLevel, retrievedDataTypeMessageKey);
this.settings = settings;
}
@Override
@@ -27,7 +35,7 @@ public class TrackedLegOfCompetitorRetrievalProcessor extends AbstractRetrievalP
if (isAborted()) {
break;
}
HasTrackedLegOfCompetitorContext trackedLegOfCompetitorWithContext = new TrackedLegOfCompetitorWithContext(element, element.getTrackedLeg().getTrackedLeg(competitor));
HasTrackedLegOfCompetitorContext trackedLegOfCompetitorWithContext = new TrackedLegOfCompetitorWithContext(element, element.getTrackedLeg().getTrackedLeg(competitor), settings);
trackedLegOfCompetitorsWithContext.add(trackedLegOfCompetitorWithContext);
}
return trackedLegOfCompetitorsWithContext;
@@ -17,9 +17,10 @@ import com.sap.sse.datamining.impl.components.AbstractRetrievalProcessor;
public class TrackedRaceRetrievalProcessor extends AbstractRetrievalProcessor<HasLeaderboardContext, HasTrackedRaceContext> {
public TrackedRaceRetrievalProcessor(ExecutorService executor,
Collection<Processor<HasTrackedRaceContext, ?>> resultReceivers, int retrievalLevel,
String retrievedDataTypeMessageKey) {
super(HasLeaderboardContext.class, HasTrackedRaceContext.class, executor, resultReceivers, retrievalLevel,
Collection<Processor<HasTrackedRaceContext, ?>> resultReceivers,
int retrievalLevel, String retrievedDataTypeMessageKey) {
super(HasLeaderboardContext.class,
HasTrackedRaceContext.class, executor, resultReceivers, retrievalLevel,
retrievedDataTypeMessageKey);
}
@@ -11,7 +11,10 @@ import java.util.function.BiFunction;
import com.sap.sailing.datamining.Activator;
import com.sap.sailing.datamining.SailingClusterGroups;
import com.sap.sailing.datamining.data.HasRaceOfCompetitorContext;
import com.sap.sailing.datamining.data.HasTackTypeSegmentContext;
import com.sap.sailing.datamining.data.HasTrackedRaceContext;
import com.sap.sailing.datamining.impl.components.TackTypeSegmentRetrievalProcessor;
import com.sap.sailing.datamining.shared.TackTypeSegmentsDataMiningSettings;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
@@ -53,10 +56,12 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
private final HasTrackedRaceContext trackedRaceContext;
private final Competitor competitor;
private final TackTypeSegmentsDataMiningSettings settings;
public RaceOfCompetitorWithContext(HasTrackedRaceContext trackedRaceContext, Competitor competitor) {
public RaceOfCompetitorWithContext(HasTrackedRaceContext trackedRaceContext, Competitor competitor, TackTypeSegmentsDataMiningSettings settings) {
this.trackedRaceContext = trackedRaceContext;
this.competitor = competitor;
this.settings = settings;
}
@Override
@@ -662,4 +667,51 @@ public class RaceOfCompetitorWithContext implements HasRaceOfCompetitorContext {
Integer rank = getTrackedRace().getRank(getCompetitor(), timePoint);
return rank == 0 ? null : rank;
}
@Override
public double getRatioDurationLongVsShortTack() {
final TackTypeRatioCollector<Duration> resultProcessor = new TackTypeRatioCollector<Duration>(Duration.NULL) {
@Override
protected Duration add(Duration a, Duration b) {
return a.plus(b);
}
@Override
protected double divide(Duration a, Duration b) {
return a.divide(b);
}
@Override
protected Duration getAddable(HasTackTypeSegmentContext element) {
return element.getDuration();
}
};
final TackTypeSegmentRetrievalProcessor tackTypeSegmentRetriever = new TackTypeSegmentRetrievalProcessor(
/* executor */ null,
Collections.emptySet(), settings, 0, "TackTypeSegments");
return Util.stream(tackTypeSegmentRetriever.retrieveData(this)).collect(resultProcessor);
}
@Override
public double getRatioDistanceLongVsShortTack() {
final TackTypeRatioCollector<Distance> resultProcessor = new TackTypeRatioCollector<Distance>(Distance.NULL) {
@Override
protected Distance add(Distance a, Distance b) {
return a.add(b);
}
@Override
protected double divide(Distance a, Distance b) {
return a.divide(b);
}
@Override
protected Distance getAddable(HasTackTypeSegmentContext element) {
return element.getDistance();
}
};
final TackTypeSegmentRetrievalProcessor tackTypeSegmentRetriever = new TackTypeSegmentRetrievalProcessor(/* executor */ null,
Collections.emptySet(), settings, 0, "TackTypeSegments");
return Util.stream(tackTypeSegmentRetriever.retrieveData(this)).collect(resultProcessor);
}
}
@@ -0,0 +1,75 @@
package com.sap.sailing.datamining.impl.data;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.function.BiConsumer;
import java.util.function.BinaryOperator;
import java.util.function.Function;
import java.util.function.Supplier;
import java.util.stream.Collector;
import com.sap.sailing.datamining.data.HasTackTypeSegmentContext;
import com.sap.sailing.domain.common.TackType;
public abstract class TackTypeRatioCollector<ADDABLE> implements Collector<HasTackTypeSegmentContext, Map<TackType, ADDABLE>, Double> {
private final ADDABLE nullValue;
public TackTypeRatioCollector(ADDABLE nullValue) {
this.nullValue = nullValue;
}
@Override
public Supplier<Map<TackType, ADDABLE>> supplier() {
return ()->{
final Map<TackType, ADDABLE> result = new HashMap<>();
for (final TackType tt : TackType.values()) {
result.put(tt, nullValue);
}
return result;
};
}
@Override
public BiConsumer<Map<TackType, ADDABLE>, HasTackTypeSegmentContext> accumulator() {
return (sumPerTackType, element)->{
final TackType tt = element.getTackType();
synchronized (this) {
sumPerTackType.put(tt, add(sumPerTackType.get(tt), getAddable(element)));
}
};
}
@Override
public BinaryOperator<Map<TackType, ADDABLE>> combiner() {
return (r1, r2) -> {
for (final Entry<TackType, ADDABLE> e : r1.entrySet()) {
r2.put(e.getKey(), add(e.getValue(), r2.get(e.getKey())));
}
return r2;
};
}
@Override
public Function<Map<TackType, ADDABLE>, Double> finisher() {
return sumPerTackType->divide(sumPerTackType.get(TackType.LONGTACK), sumPerTackType.get(TackType.SHORTTACK));
}
@Override
public Set<Characteristics> characteristics() {
return Collections.emptySet();
}
protected abstract ADDABLE add(ADDABLE a, ADDABLE b);
protected abstract double divide(ADDABLE a, ADDABLE b);
protected abstract ADDABLE getAddable(HasTackTypeSegmentContext element);
}
@@ -10,6 +10,7 @@ import com.sap.sailing.domain.common.NoWindException;
import com.sap.sailing.domain.common.TackType;
import com.sap.sailing.domain.common.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.GPSFixTrack;
import com.sap.sailing.domain.tracking.TrackedLeg;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sse.common.Distance;
import com.sap.sse.common.Duration;
@@ -80,11 +81,22 @@ public class TackTypeSegmentWithContext implements HasTackTypeSegmentContext {
return tackType;
}
@Override
public LegType getLegType() throws NoWindException {
return getTrackedRace().getTrackedLeg(getCompetitor(), getStartOfTackTypeSegment()).getTrackedLeg().getLegType(
return getTrackedLeg().getLegType(
getStartOfTackTypeSegment().plus(getStartOfTackTypeSegment().until(getEndOfTackTypeSegment()).divide(2)));
}
@Override
public int getLegNumber() {
final TrackedLeg trackedLeg = getTrackedLeg();
return trackedLeg == null ? 0 : getTrackedRace().getRace().getCourse().getIndexOfWaypoint(trackedLeg.getLeg().getTo());
}
private TrackedLeg getTrackedLeg() {
return getTrackedRace().getTrackedLeg(getCompetitor(), getStartOfTackTypeSegment()).getTrackedLeg();
}
@Override
public String toString() {
return "TackTypeSegmentWithContext [gpsFixTrackContext=" + gpsFixTrackContext + ", startOfTackTypeSegment="
@@ -10,8 +10,11 @@ import java.util.function.Supplier;
import com.sap.sailing.datamining.Activator;
import com.sap.sailing.datamining.SailingClusterGroups;
import com.sap.sailing.datamining.data.HasTackTypeSegmentContext;
import com.sap.sailing.datamining.data.HasTrackedLegContext;
import com.sap.sailing.datamining.data.HasTrackedLegOfCompetitorContext;
import com.sap.sailing.datamining.impl.components.TackTypeSegmentRetrievalProcessor;
import com.sap.sailing.datamining.shared.TackTypeSegmentsDataMiningSettings;
import com.sap.sailing.domain.base.Boat;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Leg;
@@ -44,6 +47,7 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
private static final long serialVersionUID = 5944904146286262768L;
private final HasTrackedLegContext trackedLegContext;
private final TackTypeSegmentsDataMiningSettings settings;
private final TrackedLegOfCompetitor trackedLegOfCompetitor;
private final Competitor competitor;
@@ -54,10 +58,11 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
private boolean isRankAtFinishInitialized;
private Wind wind;
public TrackedLegOfCompetitorWithContext(HasTrackedLegContext trackedLegContext, TrackedLegOfCompetitor trackedLegOfCompetitor) {
public TrackedLegOfCompetitorWithContext(HasTrackedLegContext trackedLegContext, TrackedLegOfCompetitor trackedLegOfCompetitor, TackTypeSegmentsDataMiningSettings settings) {
this.trackedLegContext = trackedLegContext;
this.trackedLegOfCompetitor = trackedLegOfCompetitor;
this.competitor = trackedLegOfCompetitor.getCompetitor();
this.settings = settings;
}
@Override
@@ -516,4 +521,56 @@ public class TrackedLegOfCompetitorWithContext implements HasTrackedLegOfCompeti
final TimePoint finishTime = getTrackedLegOfCompetitor().getFinishTime();
return getTrackedLegOfCompetitor().getAverageVelocityMadeGood(finishTime == null ? TimePoint.now() : finishTime);
}
@Override
public double getRatioDurationLongVsShortTack() {
final TackTypeRatioCollector<Duration> resultProcessor = new TackTypeRatioCollector<Duration>(Duration.NULL) {
@Override
protected Duration add(Duration a, Duration b) {
return a.plus(b);
}
@Override
protected double divide(Duration a, Duration b) {
return a.divide(b);
}
@Override
protected Duration getAddable(HasTackTypeSegmentContext element) {
return element.getDuration();
}
};
final TackTypeSegmentRetrievalProcessor tackTypeSegmentRetriever = new TackTypeSegmentRetrievalProcessor(
/* executor */ null,
Collections.emptySet(), settings, 0, "TackTypeSegments");
return Util.stream(tackTypeSegmentRetriever.retrieveData(
new RaceOfCompetitorWithContext(getTrackedLegContext().getTrackedRaceContext(), competitor, settings)))
.filter(tt->tt.getLegNumber() == getTrackedLegContext().getLegNumber()).collect(resultProcessor);
}
@Override
public double getRatioDistanceLongVsShortTack() {
final TackTypeRatioCollector<Distance> resultProcessor = new TackTypeRatioCollector<Distance>(Distance.NULL) {
@Override
protected Distance add(Distance a, Distance b) {
return a.add(b);
}
@Override
protected double divide(Distance a, Distance b) {
return a.divide(b);
}
@Override
protected Distance getAddable(HasTackTypeSegmentContext element) {
return element.getDistance();
}
};
final TackTypeSegmentRetrievalProcessor tackTypeSegmentRetriever = new TackTypeSegmentRetrievalProcessor(
/* executor */ null,
Collections.emptySet(), settings, 0, "TackTypeSegments");
return Util.stream(tackTypeSegmentRetriever.retrieveData(
new RaceOfCompetitorWithContext(getTrackedLegContext().getTrackedRaceContext(), competitor, settings)))
.filter(tt->tt.getLegNumber() == getTrackedLegContext().getLegNumber()).collect(resultProcessor);
}
}
@@ -3,6 +3,7 @@ package com.sap.sailing.datamining.impl.data;
import java.util.function.BiFunction;
import com.sap.sailing.datamining.data.HasTrackedLegContext;
import com.sap.sailing.datamining.shared.TackTypeSegmentsDataMiningSettings;
import com.sap.sailing.domain.tracking.TrackedLegOfCompetitor;
import com.sap.sse.common.TimePoint;
@@ -26,7 +27,7 @@ public class TrackedLegOfCompetitorWithSpecificTimePointWithContext extends Trac
public TrackedLegOfCompetitorWithSpecificTimePointWithContext(HasTrackedLegContext trackedLegContext,
TrackedLegOfCompetitor trackedLegOfCompetitor, TimePoint timePoint) {
super(trackedLegContext, trackedLegOfCompetitor);
super(trackedLegContext, trackedLegOfCompetitor, TackTypeSegmentsDataMiningSettings.createDefaultSettings());
this.timePoint = timePoint;
}