mirror of
https://github.com/eclipse-sailing-analytics/sailing-analytics.git
synced 2026-10-25 21:43:49 +00:00
Changed polar sheet generation to support advanced histograms
This commit is contained in:
1 parent
51e990bd74
commit
e8f461bf75
10 files changed
+187
-113
No files matched your search
+52
-48
@@ -44,6 +44,7 @@ import com.sap.sailing.domain.common.impl.Util;
|
|||||||
import com.sap.sailing.domain.common.impl.Util.Pair;
|
import com.sap.sailing.domain.common.impl.Util.Pair;
|
||||||
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
import com.sap.sailing.domain.common.impl.WindSourceImpl;
|
||||||
import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance;
|
import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance;
|
||||||
|
import com.sap.sailing.domain.polarsheets.DataPointWithOriginInfo;
|
||||||
import com.sap.sailing.domain.polarsheets.PerRaceAndCompetitorPolarSheetGenerationWorker;
|
import com.sap.sailing.domain.polarsheets.PerRaceAndCompetitorPolarSheetGenerationWorker;
|
||||||
import com.sap.sailing.domain.polarsheets.PolarSheetGenerationWorker;
|
import com.sap.sailing.domain.polarsheets.PolarSheetGenerationWorker;
|
||||||
import com.sap.sailing.domain.polarsheets.PolarSheetHistogramBuilder;
|
import com.sap.sailing.domain.polarsheets.PolarSheetHistogramBuilder;
|
||||||
@@ -117,16 +118,16 @@ public class PolarSheetGenerationTest {
|
|||||||
PolarSheetHistogramBuilder builder = new PolarSheetHistogramBuilder(settings);
|
PolarSheetHistogramBuilder builder = new PolarSheetHistogramBuilder(settings);
|
||||||
|
|
||||||
|
|
||||||
List<Double> rawData = new ArrayList<Double>();
|
List<DataPointWithOriginInfo> rawData = new ArrayList<DataPointWithOriginInfo>();
|
||||||
rawData.add(1.09);
|
rawData.add(new DataPointWithOriginInfo(1.09, ""));
|
||||||
rawData.add(1.0);
|
rawData.add(new DataPointWithOriginInfo(1.0, ""));
|
||||||
rawData.add(1.11);
|
rawData.add(new DataPointWithOriginInfo(1.11, ""));
|
||||||
rawData.add(1.46);
|
rawData.add(new DataPointWithOriginInfo(1.46, ""));
|
||||||
rawData.add(1.56);
|
rawData.add(new DataPointWithOriginInfo(1.56, ""));
|
||||||
rawData.add(2.05);
|
rawData.add(new DataPointWithOriginInfo(2.05, ""));
|
||||||
rawData.add(2.09);
|
rawData.add(new DataPointWithOriginInfo(2.09, ""));
|
||||||
rawData.add(3.0);
|
rawData.add(new DataPointWithOriginInfo(3.0, ""));
|
||||||
rawData.add(2.999);
|
rawData.add(new DataPointWithOriginInfo(2.999, ""));
|
||||||
PolarSheetsHistogramData result = builder.build(rawData, 0, 0);
|
PolarSheetsHistogramData result = builder.build(rawData, 0, 0);
|
||||||
Number[] xValues = result.getxValues();
|
Number[] xValues = result.getxValues();
|
||||||
Assert.assertEquals(10, xValues.length);
|
Assert.assertEquals(10, xValues.length);
|
||||||
@@ -142,48 +143,51 @@ public class PolarSheetGenerationTest {
|
|||||||
@Test
|
@Test
|
||||||
public void testOutlierNeighborhoodAlgorithm() {
|
public void testOutlierNeighborhoodAlgorithm() {
|
||||||
PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings();
|
PolarSheetGenerationSettings settings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings();
|
||||||
List<Double> values = new ArrayList<Double>();
|
List<DataPointWithOriginInfo> values = new ArrayList<DataPointWithOriginInfo>();
|
||||||
values.add(0.);
|
values.add(new DataPointWithOriginInfo(0., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
values.add(20.);
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
|
values.add(new DataPointWithOriginInfo(20., ""));
|
||||||
int[] count = new int[1];
|
int[] count = new int[1];
|
||||||
count[0] = values.size();
|
count[0] = values.size();
|
||||||
Map<Integer, List<Double>> valuesInMap = new HashMap<Integer, List<Double>>();
|
Map<Integer, List<DataPointWithOriginInfo>> valuesInMap = new HashMap<Integer, List<DataPointWithOriginInfo>>();
|
||||||
valuesInMap.put(0, values);
|
valuesInMap.put(0, values);
|
||||||
double pct = PolarSheetGenerationWorker.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 0, 0., settings);
|
double pct = PolarSheetGenerationWorker.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 0,
|
||||||
|
new DataPointWithOriginInfo(0.0, ""), settings);
|
||||||
|
|
||||||
Assert.assertTrue(pct < 0.05);
|
Assert.assertTrue(pct < 0.05);
|
||||||
|
|
||||||
pct = PolarSheetGenerationWorker.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 2, 20., settings);
|
pct = PolarSheetGenerationWorker.getNeighboorhoodSizePercentage(count, valuesInMap, 0, 2,
|
||||||
|
new DataPointWithOriginInfo(20.0, ""), settings);
|
||||||
|
|
||||||
Assert.assertTrue(pct > 0.05);
|
Assert.assertTrue(pct > 0.05);
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|||||||
-28
@@ -1,28 +0,0 @@
|
|||||||
package com.sap.sailing.domain.polarsheets;
|
|
||||||
|
|
||||||
import com.sap.sailing.domain.common.Speed;
|
|
||||||
|
|
||||||
public class BoatAndWindSpeedImpl implements BoatAndWindSpeed{
|
|
||||||
|
|
||||||
private Speed boatSpeed;
|
|
||||||
private Speed windSpeed;
|
|
||||||
|
|
||||||
|
|
||||||
|
|
||||||
public BoatAndWindSpeedImpl(Speed boatSpeed, Speed windSpeed) {
|
|
||||||
this.boatSpeed = boatSpeed;
|
|
||||||
this.windSpeed = windSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Speed getBoatSpeed() {
|
|
||||||
return boatSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public Speed getWindSpeed() {
|
|
||||||
return windSpeed;
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
}
|
|
||||||
+3
-1
@@ -2,10 +2,12 @@ package com.sap.sailing.domain.polarsheets;
|
|||||||
|
|
||||||
import com.sap.sailing.domain.common.Speed;
|
import com.sap.sailing.domain.common.Speed;
|
||||||
|
|
||||||
public interface BoatAndWindSpeed {
|
public interface BoatAndWindSpeedWithOriginInfo {
|
||||||
|
|
||||||
public Speed getBoatSpeed();
|
public Speed getBoatSpeed();
|
||||||
|
|
||||||
public Speed getWindSpeed();
|
public Speed getWindSpeed();
|
||||||
|
|
||||||
|
public String getWindGaugesIdString();
|
||||||
|
|
||||||
}
|
}
|
||||||
+32
@@ -0,0 +1,32 @@
|
|||||||
|
package com.sap.sailing.domain.polarsheets;
|
||||||
|
|
||||||
|
import com.sap.sailing.domain.common.Speed;
|
||||||
|
|
||||||
|
public class BoatAndWindSpeedWithOriginInfoImpl implements BoatAndWindSpeedWithOriginInfo {
|
||||||
|
|
||||||
|
private Speed boatSpeed;
|
||||||
|
private Speed windSpeed;
|
||||||
|
private String windGaugesIdString;
|
||||||
|
|
||||||
|
public BoatAndWindSpeedWithOriginInfoImpl(Speed boatSpeed, Speed windSpeed, String windGaugesIdString) {
|
||||||
|
this.boatSpeed = boatSpeed;
|
||||||
|
this.windSpeed = windSpeed;
|
||||||
|
this.windGaugesIdString = windGaugesIdString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Speed getBoatSpeed() {
|
||||||
|
return boatSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public Speed getWindSpeed() {
|
||||||
|
return windSpeed;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public String getWindGaugesIdString() {
|
||||||
|
return windGaugesIdString;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+27
@@ -0,0 +1,27 @@
|
|||||||
|
package com.sap.sailing.domain.polarsheets;
|
||||||
|
|
||||||
|
public class DataPointWithOriginInfo implements Comparable<DataPointWithOriginInfo>{
|
||||||
|
|
||||||
|
private Double rawData;
|
||||||
|
|
||||||
|
private String windGaugeIdString;
|
||||||
|
|
||||||
|
public DataPointWithOriginInfo(Double rawData, String windGaugeIdString) {
|
||||||
|
this.rawData = rawData;
|
||||||
|
this.windGaugeIdString = windGaugeIdString;
|
||||||
|
}
|
||||||
|
|
||||||
|
public Double getRawData() {
|
||||||
|
return rawData;
|
||||||
|
}
|
||||||
|
|
||||||
|
public String getWindGaugeIdString() {
|
||||||
|
return windGaugeIdString;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public int compareTo(DataPointWithOriginInfo o) {
|
||||||
|
return getRawData().compareTo(o.getRawData());
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
+1
-2
@@ -152,8 +152,7 @@ public class PerRaceAndCompetitorPolarSheetGenerationWorker implements Runnable{
|
|||||||
//continue;
|
//continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
polarSheetGenerationWorker.addPolarData(Math.round(polarFix.getAngleToWind()),
|
polarSheetGenerationWorker.addPolarData(polarFix);
|
||||||
polarFix.getBoatSpeed(), polarFix.getWindSpeed());
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -21,6 +21,7 @@ public class PolarFix {
|
|||||||
private SpeedWithBearing boatSpeed;
|
private SpeedWithBearing boatSpeed;
|
||||||
private Speed windSpeed;
|
private Speed windSpeed;
|
||||||
private double angleToWind;
|
private double angleToWind;
|
||||||
|
private String gaugeIdString;
|
||||||
|
|
||||||
public PolarFix(GPSFixMoving fix, TrackedRace race, GPSFixTrack<Competitor, GPSFixMoving> track, Wind windSpeed,
|
public PolarFix(GPSFixMoving fix, TrackedRace race, GPSFixTrack<Competitor, GPSFixMoving> track, Wind windSpeed,
|
||||||
PolarSheetGenerationSettings settings) {
|
PolarSheetGenerationSettings settings) {
|
||||||
@@ -28,6 +29,7 @@ public class PolarFix {
|
|||||||
Bearing bearing = boatSpeed.getBearing();
|
Bearing bearing = boatSpeed.getBearing();
|
||||||
|
|
||||||
Position position = fix.getPosition();
|
Position position = fix.getPosition();
|
||||||
|
gaugeIdString = createWindGaugesString(race);
|
||||||
this.windSpeed = windSpeed;
|
this.windSpeed = windSpeed;
|
||||||
Set<WindSource> windSourcesToExclude;
|
Set<WindSource> windSourcesToExclude;
|
||||||
if (settings.useOnlyEstimatedForWindDirection()) {
|
if (settings.useOnlyEstimatedForWindDirection()) {
|
||||||
@@ -45,6 +47,15 @@ public class PolarFix {
|
|||||||
angleToWind = bearing.getDifferenceTo(windBearing).getDegrees();
|
angleToWind = bearing.getDifferenceTo(windBearing).getDegrees();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private String createWindGaugesString(TrackedRace race) {
|
||||||
|
Iterable<WindSource> gaugeWindSources = race.getWindSources(WindSourceType.EXPEDITION);
|
||||||
|
String gaugeIdString = "";
|
||||||
|
for (WindSource source : gaugeWindSources) {
|
||||||
|
gaugeIdString = gaugeIdString + "+" + source.getId();
|
||||||
|
}
|
||||||
|
return gaugeIdString;
|
||||||
|
}
|
||||||
|
|
||||||
public Set<WindSource> collectWindSourcesToIgnoreForBearing(TrackedRace race, boolean excludeCourseBased) {
|
public Set<WindSource> collectWindSourcesToIgnoreForBearing(TrackedRace race, boolean excludeCourseBased) {
|
||||||
Set<WindSource> windSourcesToExclude = new HashSet<WindSource>();
|
Set<WindSource> windSourcesToExclude = new HashSet<WindSource>();
|
||||||
Iterable<WindSource> combinedSources = race.getWindSources(WindSourceType.COMBINED);
|
Iterable<WindSource> combinedSources = race.getWindSources(WindSourceType.COMBINED);
|
||||||
@@ -83,7 +94,9 @@ public class PolarFix {
|
|||||||
public double getAngleToWind() {
|
public double getAngleToWind() {
|
||||||
return angleToWind;
|
return angleToWind;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public String getGaugeIdString() {
|
||||||
|
return gaugeIdString;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
+29
-24
@@ -36,7 +36,7 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
|
|
||||||
private final Set<PerRaceAndCompetitorPolarSheetGenerationWorker> workers;
|
private final Set<PerRaceAndCompetitorPolarSheetGenerationWorker> workers;
|
||||||
|
|
||||||
private final List<List<BoatAndWindSpeed>> polarData;
|
private final List<List<BoatAndWindSpeedWithOriginInfo>> polarData;
|
||||||
|
|
||||||
private final Executor executor;
|
private final Executor executor;
|
||||||
|
|
||||||
@@ -80,10 +80,10 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private List<List<BoatAndWindSpeed>> initializePolarDataContainer() {
|
private List<List<BoatAndWindSpeedWithOriginInfo>> initializePolarDataContainer() {
|
||||||
List<List<BoatAndWindSpeed>> container = new ArrayList<List<BoatAndWindSpeed>>();
|
List<List<BoatAndWindSpeedWithOriginInfo>> container = new ArrayList<List<BoatAndWindSpeedWithOriginInfo>>();
|
||||||
for (int i = 0; i < 360; i++) {
|
for (int i = 0; i < 360; i++) {
|
||||||
container.add(new ArrayList<BoatAndWindSpeed>());
|
container.add(new ArrayList<BoatAndWindSpeedWithOriginInfo>());
|
||||||
}
|
}
|
||||||
return container;
|
return container;
|
||||||
}
|
}
|
||||||
@@ -108,12 +108,16 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
* @param windSpeed
|
* @param windSpeed
|
||||||
* wind's speed in knots
|
* wind's speed in knots
|
||||||
*/
|
*/
|
||||||
protected void addPolarData(long roundedAngle, Speed boatSpeed, Speed windSpeed) {
|
protected void addPolarData(PolarFix polarFix) {
|
||||||
|
long roundedAngle = Math.round(polarFix.getAngleToWind());
|
||||||
|
Speed boatSpeed = polarFix.getBoatSpeed();
|
||||||
|
Speed windSpeed = polarFix.getWindSpeed();
|
||||||
int angle = (int) roundedAngle;
|
int angle = (int) roundedAngle;
|
||||||
if (angle < 0) {
|
if (angle < 0) {
|
||||||
angle = (360 + angle);
|
angle = (360 + angle);
|
||||||
}
|
}
|
||||||
BoatAndWindSpeed speeds = new BoatAndWindSpeedImpl(boatSpeed, windSpeed);
|
BoatAndWindSpeedWithOriginInfo speeds = new BoatAndWindSpeedWithOriginInfoImpl(boatSpeed, windSpeed,
|
||||||
|
polarFix.getGaugeIdString());
|
||||||
polarData.get(angle).add(speeds);
|
polarData.get(angle).add(speeds);
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -145,14 +149,14 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
}
|
}
|
||||||
for (int angleIndex = 0; angleIndex < 360; angleIndex++) {
|
for (int angleIndex = 0; angleIndex < 360; angleIndex++) {
|
||||||
// Avoid Concurrent modification of lists, so get current state as an array; lock would slow things down
|
// Avoid Concurrent modification of lists, so get current state as an array; lock would slow things down
|
||||||
BoatAndWindSpeed[] values = polarData.get(angleIndex).toArray(
|
BoatAndWindSpeedWithOriginInfo[] values = polarData.get(angleIndex).toArray(
|
||||||
new BoatAndWindSpeed[polarData.get(angleIndex).size()]);
|
new BoatAndWindSpeedWithOriginInfo[polarData.get(angleIndex).size()]);
|
||||||
dataCount = dataCount + values.length;
|
dataCount = dataCount + values.length;
|
||||||
dataCountPerAngle[angleIndex] = values.length;
|
dataCountPerAngle[angleIndex] = values.length;
|
||||||
double[] sumsPerWindSpeed = new double[levelCount];
|
double[] sumsPerWindSpeed = new double[levelCount];
|
||||||
int[] dataCountPerWindSpeed = new int[levelCount];
|
int[] dataCountPerWindSpeed = new int[levelCount];
|
||||||
Map<Integer, List<Double>> dataSetForWindLevel = new HashMap<Integer, List<Double>>();
|
Map<Integer, List<DataPointWithOriginInfo>> dataSetForWindLevel = new HashMap<Integer, List<DataPointWithOriginInfo>>();
|
||||||
for (BoatAndWindSpeed singleDataPoint : values) {
|
for (BoatAndWindSpeedWithOriginInfo singleDataPoint : values) {
|
||||||
if (singleDataPoint != null) {
|
if (singleDataPoint != null) {
|
||||||
double windSpeed = singleDataPoint.getWindSpeed().getKnots();
|
double windSpeed = singleDataPoint.getWindSpeed().getKnots();
|
||||||
int level = stepping.getLevelIndexForValue(windSpeed);
|
int level = stepping.getLevelIndexForValue(windSpeed);
|
||||||
@@ -160,10 +164,11 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
double speed = singleDataPoint.getBoatSpeed().getKnots();
|
double speed = singleDataPoint.getBoatSpeed().getKnots();
|
||||||
|
String windGaugesIdString = singleDataPoint.getWindGaugesIdString();
|
||||||
if (!dataSetForWindLevel.containsKey(level)) {
|
if (!dataSetForWindLevel.containsKey(level)) {
|
||||||
dataSetForWindLevel.put(level, new ArrayList<Double>());
|
dataSetForWindLevel.put(level, new ArrayList<DataPointWithOriginInfo>());
|
||||||
}
|
}
|
||||||
dataSetForWindLevel.get(level).add(speed);
|
dataSetForWindLevel.get(level).add(new DataPointWithOriginInfo(speed, windGaugesIdString));
|
||||||
sumsPerWindSpeed[level] = sumsPerWindSpeed[level] + speed;
|
sumsPerWindSpeed[level] = sumsPerWindSpeed[level] + speed;
|
||||||
dataCountPerWindSpeed[level]++;
|
dataCountPerWindSpeed[level]++;
|
||||||
}
|
}
|
||||||
@@ -171,17 +176,17 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
|
|
||||||
for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) {
|
for (int levelIndex = 0; levelIndex < levelCount; levelIndex++) {
|
||||||
if (settings.shouldRemoveOutliers() && dataSetForWindLevel.get(levelIndex) != null) {
|
if (settings.shouldRemoveOutliers() && dataSetForWindLevel.get(levelIndex) != null) {
|
||||||
List<Double> withoutOutliers = new ArrayList<Double>();
|
List<DataPointWithOriginInfo> withoutOutliers = new ArrayList<DataPointWithOriginInfo>();
|
||||||
Collections.sort(dataSetForWindLevel.get(levelIndex));
|
Collections.sort(dataSetForWindLevel.get(levelIndex));
|
||||||
if (dataCountPerWindSpeed[levelIndex] > /* Minimum data count for outlier detection */10) {
|
if (dataCountPerWindSpeed[levelIndex] > /* Minimum data count for outlier detection */10) {
|
||||||
for (int dataIndex = 0; dataIndex < dataSetForWindLevel.get(levelIndex).size(); dataIndex++) {
|
for (int dataIndex = 0; dataIndex < dataSetForWindLevel.get(levelIndex).size(); dataIndex++) {
|
||||||
Double dataPoint = dataSetForWindLevel.get(levelIndex).get(dataIndex);
|
DataPointWithOriginInfo dataPoint = dataSetForWindLevel.get(levelIndex).get(dataIndex);
|
||||||
double pct = getNeighboorhoodSizePercentage(dataCountPerWindSpeed, dataSetForWindLevel,
|
double pct = getNeighboorhoodSizePercentage(dataCountPerWindSpeed, dataSetForWindLevel,
|
||||||
levelIndex, dataIndex, dataPoint, settings);
|
levelIndex, dataIndex, dataPoint, settings);
|
||||||
if (pct >= settings.getOutlierMinimumNeighborhoodPct()) {
|
if (pct >= settings.getOutlierMinimumNeighborhoodPct()) {
|
||||||
withoutOutliers.add(dataPoint);
|
withoutOutliers.add(dataPoint);
|
||||||
} else {
|
} else {
|
||||||
sumsPerWindSpeed[levelIndex] = sumsPerWindSpeed[levelIndex] - dataPoint;
|
sumsPerWindSpeed[levelIndex] = sumsPerWindSpeed[levelIndex] - dataPoint.getRawData();
|
||||||
dataCountPerWindSpeed[levelIndex]--;
|
dataCountPerWindSpeed[levelIndex]--;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -191,7 +196,7 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (dataCountPerWindSpeed[levelIndex] < settings.getMinimumDataCountPerAngle()) {
|
if (dataCountPerWindSpeed[levelIndex] < settings.getMinimumDataCountPerAngle()) {
|
||||||
dataSetForWindLevel.put(levelIndex, new ArrayList<Double>());
|
dataSetForWindLevel.put(levelIndex, new ArrayList<DataPointWithOriginInfo>());
|
||||||
sumsPerWindSpeed[levelIndex] = 0;
|
sumsPerWindSpeed[levelIndex] = 0;
|
||||||
dataCountPerWindSpeed[levelIndex] = 0;
|
dataCountPerWindSpeed[levelIndex] = 0;
|
||||||
}
|
}
|
||||||
@@ -209,7 +214,7 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
Double standardDeviation = Math.sqrt(variance);
|
Double standardDeviation = Math.sqrt(variance);
|
||||||
coefficiantOfVariation = standardDeviation / average;
|
coefficiantOfVariation = standardDeviation / average;
|
||||||
}
|
}
|
||||||
List<Double> rawData = dataSetForWindLevel.get(levelIndex);
|
List<DataPointWithOriginInfo> rawData = dataSetForWindLevel.get(levelIndex);
|
||||||
if (rawData == null || rawData.size() <= 0) {
|
if (rawData == null || rawData.size() <= 0) {
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
@@ -239,8 +244,8 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
* to the complete dataset.
|
* to the complete dataset.
|
||||||
*/
|
*/
|
||||||
public static double getNeighboorhoodSizePercentage(int[] dataCountPerWindSpeed,
|
public static double getNeighboorhoodSizePercentage(int[] dataCountPerWindSpeed,
|
||||||
Map<Integer, List<Double>> dataSetForWindLevel, int levelIndex, int dataIndex, Double dataPoint,
|
Map<Integer, List<DataPointWithOriginInfo>> dataSetForWindLevel, int levelIndex, int dataIndex,
|
||||||
PolarSheetGenerationSettings settings) {
|
DataPointWithOriginInfo dataPoint, PolarSheetGenerationSettings settings) {
|
||||||
int neighborCount = 0;
|
int neighborCount = 0;
|
||||||
double pct = .0;
|
double pct = .0;
|
||||||
|
|
||||||
@@ -253,8 +258,8 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
done = true;
|
done = true;
|
||||||
} else {
|
} else {
|
||||||
Double dataPointToCheckForNeighborStatus = dataSetForWindLevel.get(levelIndex).get(
|
Double dataPointToCheckForNeighborStatus = dataSetForWindLevel.get(levelIndex).get(
|
||||||
index);
|
index).getRawData();
|
||||||
if (dataPoint - dataPointToCheckForNeighborStatus <= settings
|
if (dataPoint.getRawData() - dataPointToCheckForNeighborStatus <= settings
|
||||||
.getOutlierDetectionNeighborhoodRadius()) {
|
.getOutlierDetectionNeighborhoodRadius()) {
|
||||||
neighborCount++;
|
neighborCount++;
|
||||||
} else {
|
} else {
|
||||||
@@ -275,8 +280,8 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
done = true;
|
done = true;
|
||||||
} else {
|
} else {
|
||||||
Double dataPointToCheckForNeighborStatus = dataSetForWindLevel.get(levelIndex).get(
|
Double dataPointToCheckForNeighborStatus = dataSetForWindLevel.get(levelIndex).get(
|
||||||
index);
|
index).getRawData();
|
||||||
if (dataPointToCheckForNeighborStatus - dataPoint <= settings
|
if (dataPointToCheckForNeighborStatus - dataPoint.getRawData() <= settings
|
||||||
.getOutlierDetectionNeighborhoodRadius()) {
|
.getOutlierDetectionNeighborhoodRadius()) {
|
||||||
neighborCount++;
|
neighborCount++;
|
||||||
} else {
|
} else {
|
||||||
@@ -309,7 +314,7 @@ public class PolarSheetGenerationWorker implements Future<PolarSheetsData>{
|
|||||||
* @return The complete set of datapoints that have been added. Can be quite big, depending on the amount of races
|
* @return The complete set of datapoints that have been added. Can be quite big, depending on the amount of races
|
||||||
* and competitors.
|
* and competitors.
|
||||||
*/
|
*/
|
||||||
public List<List<BoatAndWindSpeed>> getCompleteData() {
|
public List<List<BoatAndWindSpeedWithOriginInfo>> getCompleteData() {
|
||||||
return polarData;
|
return polarData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+25
-6
@@ -1,7 +1,9 @@
|
|||||||
package com.sap.sailing.domain.polarsheets;
|
package com.sap.sailing.domain.polarsheets;
|
||||||
|
|
||||||
import java.util.Collections;
|
import java.util.Collections;
|
||||||
|
import java.util.HashMap;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
|
import java.util.Map;
|
||||||
|
|
||||||
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
|
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
|
||||||
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
|
import com.sap.sailing.domain.common.PolarSheetsHistogramData;
|
||||||
@@ -15,9 +17,13 @@ public class PolarSheetHistogramBuilder {
|
|||||||
this.settings = settings;
|
this.settings = settings;
|
||||||
}
|
}
|
||||||
|
|
||||||
public PolarSheetsHistogramData build(List<Double> rawData, int angleIndex, double coefficiantOfVariation) {
|
public PolarSheetsHistogramData build(List<DataPointWithOriginInfo> dataPointsWithOriginInfo, int angleIndex,
|
||||||
Double min = Collections.min(rawData);
|
double coefficiantOfVariation) {
|
||||||
Double max = Collections.max(rawData);
|
DataPointWithOriginInfo minPoint = Collections.min(dataPointsWithOriginInfo);
|
||||||
|
DataPointWithOriginInfo maxPoint = Collections.max(dataPointsWithOriginInfo);
|
||||||
|
|
||||||
|
Double min = minPoint.getRawData();
|
||||||
|
Double max = maxPoint.getRawData();
|
||||||
|
|
||||||
int numberOfColumns = settings.getNumberOfHistogramColumns();
|
int numberOfColumns = settings.getNumberOfHistogramColumns();
|
||||||
double range = (max - min) / numberOfColumns;
|
double range = (max - min) / numberOfColumns;
|
||||||
@@ -26,9 +32,10 @@ public class PolarSheetHistogramBuilder {
|
|||||||
xValues[u] = min + u * range + (0.5 * range);
|
xValues[u] = min + u * range + (0.5 * range);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Map<String, Integer[]> yValuesByGaugeIds = new HashMap<String, Integer[]>();
|
||||||
Integer[] yValues = new Integer[numberOfColumns];
|
Integer[] yValues = new Integer[numberOfColumns];
|
||||||
for (Double dataPoint : rawData) {
|
for (DataPointWithOriginInfo dataPoint : dataPointsWithOriginInfo) {
|
||||||
double notRoundedYet = (dataPoint - min) / range;
|
double notRoundedYet = (dataPoint.getRawData() - min) / range;
|
||||||
int u = (int) notRoundedYet;
|
int u = (int) notRoundedYet;
|
||||||
if (u == numberOfColumns) {
|
if (u == numberOfColumns) {
|
||||||
// For max value
|
// For max value
|
||||||
@@ -38,10 +45,22 @@ public class PolarSheetHistogramBuilder {
|
|||||||
yValues[u] = 0;
|
yValues[u] = 0;
|
||||||
}
|
}
|
||||||
yValues[u]++;
|
yValues[u]++;
|
||||||
|
|
||||||
|
String gaugeIdsString = dataPoint.getWindGaugeIdString();
|
||||||
|
if (!yValuesByGaugeIds.containsKey(gaugeIdsString)) {
|
||||||
|
yValuesByGaugeIds.put(gaugeIdsString, new Integer[numberOfColumns]);
|
||||||
|
}
|
||||||
|
Integer[] yValuesForThatGaugeIdsString = yValuesByGaugeIds.get(gaugeIdsString);
|
||||||
|
if (yValuesForThatGaugeIdsString[u] == null) {
|
||||||
|
yValuesForThatGaugeIdsString[u] = 0;
|
||||||
|
}
|
||||||
|
yValuesForThatGaugeIdsString[u]++;
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
PolarSheetsHistogramData histogramData = new PolarSheetsHistogramDataImpl(angleIndex, xValues, yValues,
|
PolarSheetsHistogramData histogramData = new PolarSheetsHistogramDataImpl(angleIndex, xValues, yValues,
|
||||||
rawData.size(), coefficiantOfVariation);
|
dataPointsWithOriginInfo.size(), coefficiantOfVariation);
|
||||||
return histogramData;
|
return histogramData;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
+3
-2
@@ -42,8 +42,9 @@ public class PolarSheetsHistogramPanel extends DockLayoutPanel {
|
|||||||
|
|
||||||
public void setData(PolarSheetsHistogramData data) {
|
public void setData(PolarSheetsHistogramData data) {
|
||||||
chart.removeAllSeries();
|
chart.removeAllSeries();
|
||||||
chart.setTitle(new ChartTitle().setText(stringMessages.histogram()),
|
chart.setTitle(new ChartTitle().setText(stringMessages.histogram()), new ChartSubtitle().setText(stringMessages
|
||||||
new ChartSubtitle().setText(stringMessages.angleAndTotalNumberOfDataPointsAndCovAndCm(data.getAngle(), data.getDataCount(), data.getCoefficiantOfVariation(), data.getConfidenceMeasure())));
|
.angleAndTotalNumberOfDataPointsAndCovAndCm(data.getAngle(), data.getDataCount(),
|
||||||
|
data.getCoefficiantOfVariation(), data.getConfidenceMeasure())));
|
||||||
Point[] points = toPoints(data);
|
Point[] points = toPoints(data);
|
||||||
Series series = chart.createSeries();
|
Series series = chart.createSeries();
|
||||||
series.setPoints(points);
|
series.setPoints(points);
|
||||||
|
|||||||
Reference in new issue
Block a user