Started creating a test for polar fix grouping

This commit is contained in:
Frederik Petersen committed 2014-04-03 10:12:34 +02:00
1 parent af51393773
commit c861ab5b3d
11 files changed
+343 -32

No files matched your search

@@ -16,4 +16,8 @@ public interface WindStepping extends Serializable{
double getDistanceToLevelFloor(double speed);
int hashCode();
boolean equals(Object obj);
}
@@ -0,0 +1,120 @@
package com.sap.sailing.polars.mining.test;
import static org.mockito.Matchers.any;
import static org.mockito.Matchers.eq;
import static org.mockito.Mockito.mock;
import static org.mockito.Mockito.when;
import java.util.Calendar;
import java.util.concurrent.TimeoutException;
import org.junit.Test;
import com.sap.sailing.domain.base.BoatClass;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Course;
import com.sap.sailing.domain.base.RaceDefinition;
import com.sap.sailing.domain.base.Waypoint;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.TimePoint;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.DegreePosition;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MillisecondsTimePoint;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.MarkPassing;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.impl.DynamicGPSFixMovingTrackImpl;
import com.sap.sailing.polars.mining.PolarDataMiner;
public class PolarDataMinerTest {
private static final int MILLISECONDS_OVER_WHICH_TO_AVERAGE_SPEED = 30;
@Test
public void testGrouping() throws InterruptedException, TimeoutException {
PolarDataMiner miner = new PolarDataMiner();
GPSFixMoving fix = createMockedFix();
Competitor competitor = mock(Competitor.class);
TrackedRace trackedRace = createMockedTrackedRace(competitor, fix);
miner.addFix(fix, competitor, trackedRace);
int millisLeft = 500000;
while (miner.isCurrentlyActiveAndOrHasQueue() && millisLeft > 0) {
System.out.println("Executor active");
Thread.sleep(100);
millisLeft = millisLeft - 100;
if (miner.isCurrentlyActiveAndOrHasQueue() && millisLeft <= 0) {
throw new TimeoutException();
}
}
}
private GPSFixMoving createMockedFix() {
GPSFixMoving fix = mock(GPSFixMoving.class);
when(fix.getPosition()).thenReturn(new DegreePosition(54.431952, 10.186767));
Calendar cal = Calendar.getInstance();
cal.set(2014, 4, 3, 13, 00);
TimePoint fixTimePoint = new MillisecondsTimePoint(cal.getTime());
when(fix.getTimePoint()).thenReturn(fixTimePoint);
Bearing bearing = new DegreeBearingImpl(45);
SpeedWithBearing speedWithBearing = new KnotSpeedWithBearingImpl(10.5, bearing);
when(fix.getSpeed()).thenReturn(speedWithBearing);
return fix;
}
private TrackedRace createMockedTrackedRace(Competitor competitor, GPSFixMoving fix) {
TrackedRace trackedRace = mock(TrackedRace.class);
DynamicGPSFixTrack<Competitor, GPSFixMoving> track = new DynamicGPSFixMovingTrackImpl<Competitor>(competitor,
MILLISECONDS_OVER_WHICH_TO_AVERAGE_SPEED);
track.add(fix);
when(trackedRace.getTrack(competitor)).thenReturn(track);
RaceDefinition mockedRaceDefinition = createMockedRaceDefinition();
when(trackedRace.getRace()).thenReturn(mockedRaceDefinition);
MarkPassing markpassing = createMockedStartMarkPassing();
when(trackedRace.getMarkPassing(eq(competitor), any(Waypoint.class))).thenReturn(markpassing);
Calendar cal = Calendar.getInstance();
cal.set(2014, 4, 3, 12, 00);
TimePoint startOfRace = new MillisecondsTimePoint(cal.getTime());
cal.set(2014, 4, 3, 15, 00);
TimePoint endOfRace = new MillisecondsTimePoint(cal.getTime());
when(trackedRace.getStartOfRace()).thenReturn(startOfRace);
when(trackedRace.getEndOfRace()).thenReturn(endOfRace);
return trackedRace;
}
private MarkPassing createMockedStartMarkPassing() {
Calendar cal = Calendar.getInstance();
cal.set(2014, 4, 3, 12, 15);
TimePoint startOfRaceForCompetitor = new MillisecondsTimePoint(cal.getTime());
MarkPassing passing = mock(MarkPassing.class);
when(passing.getTimePoint()).thenReturn(startOfRaceForCompetitor);
return passing;
}
private RaceDefinition createMockedRaceDefinition() {
RaceDefinition raceDefinition = mock(RaceDefinition.class);
Course mockedCourse = createMockedCourse();
when(raceDefinition.getCourse()).thenReturn(mockedCourse);
BoatClass mockedBoatClass = mock(BoatClass.class);
when(mockedBoatClass.getManeuverDegreeAngleThreshold()).thenReturn(20.0);
when(raceDefinition.getBoatClass()).thenReturn(mockedBoatClass);
return raceDefinition;
}
private Course createMockedCourse() {
Course course = mock(Course.class);
return course;
}
}
@@ -2,22 +2,26 @@ package com.sap.sailing.polars.mining;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.SpeedWithBearing;
import com.sap.sailing.domain.common.impl.WindSteppingWithMaxDistance;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.TrackedRace;
import com.sap.sailing.domain.tracking.Wind;
import com.sap.sse.datamining.shared.annotations.Dimension;
public class GPSFixMovingWithPolarContext {
public class GPSFixMovingWithPolarContext implements PolarClusterKey {
private final GPSFixMoving fix;
private final TrackedRace race;
private final Competitor competitor;
private final PolarSheetGenerationSettings defaultPolarSheetGenerationSettings;
public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor) {
public GPSFixMovingWithPolarContext(GPSFixMoving fix, TrackedRace race, Competitor competitor,
PolarSheetGenerationSettings defaultPolarSheetGenerationSettings) {
this.fix = fix;
this.race = race;
this.competitor = competitor;
this.defaultPolarSheetGenerationSettings = defaultPolarSheetGenerationSettings;
}
public GPSFixMoving getFix() {
@@ -32,13 +36,21 @@ public class GPSFixMovingWithPolarContext {
return competitor;
}
@Dimension(messageKey = "roundedAngle")
public int roundedAngleToTheWind() {
@Override
public RoundedAngleToTheWind getRoundedAngleToTheWind() {
SpeedWithBearing boatSpeed = race.getTrack(competitor).getEstimatedSpeed(fix.getTimePoint());
Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint());
Bearing bearing = boatSpeed.getBearing();
int roundedAngle = (int) Math.round(bearing.getDifferenceTo(wind.getBearing()).getDegrees());
return roundedAngle;
int angle = (int) Math.round(bearing.getDifferenceTo(wind.getBearing()).getDegrees());
return new RoundedAngleToTheWind(angle);
}
@Override
public WindSpeedLevel getWindSpeedLevel() {
Wind wind = race.getWind(fix.getPosition(), fix.getTimePoint());
WindSteppingWithMaxDistance stepping = defaultPolarSheetGenerationSettings.getWindStepping();
int level = stepping.getLevelIndexForValue(wind.getKnots());
return new WindSpeedLevel(level, stepping);
}
}
@@ -0,0 +1,43 @@
package com.sap.sailing.polars.mining;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.impl.components.GroupedDataEntry;
import com.sap.sse.datamining.shared.GroupKey;
public class IncrementalRegressionProcessor implements Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>> {
private final Map<GroupKey, Set<GPSFixMovingWithPolarContext>> container = new HashMap<GroupKey, Set<GPSFixMovingWithPolarContext>>();
@Override
public void onElement(GroupedDataEntry<GPSFixMovingWithPolarContext> element) {
GroupKey key = element.getKey();
if (!container.containsKey(key)) {
container.put(key, new HashSet<GPSFixMovingWithPolarContext>());
}
Set<GPSFixMovingWithPolarContext> set = container.get(key);
set.add(element.getDataEntry());
}
@Override
public void finish() throws InterruptedException {
// Nothing to do here
}
@Override
public void abort() {
// TODO Auto-generated method stub
}
@Override
public AdditionalResultDataBuilder getAdditionalResultData(AdditionalResultDataBuilder additionalDataBuilder) {
// TODO Auto-generated method stub
return null;
}
}
@@ -0,0 +1,21 @@
package com.sap.sailing.polars.mining;
import com.sap.sse.datamining.shared.annotations.Dimension;
public interface PolarClusterKey {
/**
*
* @return degrees boat to windDirection in DEG
*/
@Dimension(messageKey = "angleToWind")
public abstract RoundedAngleToTheWind getRoundedAngleToTheWind();
/**
*
* @return wind speed level
*/
@Dimension(messageKey = "windSpeed")
public abstract WindSpeedLevel getWindSpeedLevel();
}
@@ -8,6 +8,8 @@ import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.common.PolarSheetGenerationSettings;
import com.sap.sailing.domain.common.impl.PolarSheetGenerationSettingsImpl;
import com.sap.sailing.domain.common.impl.Util.Triple;
import com.sap.sailing.domain.tracking.GPSFixMoving;
import com.sap.sailing.domain.tracking.TrackedRace;
@@ -22,6 +24,7 @@ public class PolarDataMiner {
private static final int THREAD_POOL_SIZE = Math.max(Runtime.getRuntime().availableProcessors(), 3);
private static final ThreadPoolExecutor executor = createExecutor();
private final PolarSheetGenerationSettings defaultPolarSheetGenerationSettings;
private static ThreadPoolExecutor createExecutor() {
return new ThreadPoolExecutor(THREAD_POOL_SIZE, THREAD_POOL_SIZE, 60, TimeUnit.SECONDS,
@@ -32,6 +35,7 @@ public class PolarDataMiner {
private AbstractEnrichingProcessor<Triple<GPSFixMoving, TrackedRace, Competitor>, GPSFixMovingWithPolarContext> enrichingProcessor;
public PolarDataMiner() {
defaultPolarSheetGenerationSettings = PolarSheetGenerationSettingsImpl.createStandardPolarSettings();
try {
setUpWorkflow();
} catch (ClassCastException | NoSuchMethodException | SecurityException e) {
@@ -44,23 +48,36 @@ public class PolarDataMiner {
private void setUpWorkflow() throws ClassCastException, NoSuchMethodException,
SecurityException {
Collection<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>>> grouperResultReceivers = null;
Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>> incrementalRegressionProcessor = new IncrementalRegressionProcessor();
Collection<Processor<GroupedDataEntry<GPSFixMovingWithPolarContext>>> grouperResultReceivers = Arrays
.asList(incrementalRegressionProcessor);
Collection<Function<?>> dimensions = new ArrayList<>();
Function<Integer> function = FunctionFactory.createMethodWrappingFunction(GPSFixMovingWithPolarContext.class
.getMethod("roundedAngleToTheWind", new Class<?>[0]));
dimensions.add(function);
Function<RoundedAngleToTheWind> angleFunction = FunctionFactory
.createMethodWrappingFunction(PolarClusterKey.class.getMethod("getRoundedAngleToTheWind",
new Class<?>[0]));
Function<WindSpeedLevel> windSpeedFunction = FunctionFactory.createMethodWrappingFunction(PolarClusterKey.class
.getMethod("getWindSpeedLevel", new Class<?>[0]));
dimensions.add(angleFunction);
dimensions.add(windSpeedFunction);
Processor<GPSFixMovingWithPolarContext> groupingProcessor = new ParallelMultiDimensionalGroupingProcessor<GPSFixMovingWithPolarContext>(
executor, grouperResultReceivers, dimensions);
Collection<Processor<GPSFixMovingWithPolarContext>> filteringResultReceivers = Arrays.asList(groupingProcessor);
Processor<GPSFixMovingWithPolarContext> filteringProcessor = new ParallelFilteringProcessor<GPSFixMovingWithPolarContext>(
executor, filteringResultReceivers, new PolarFixFilterCriteria());
Collection<Processor<GPSFixMovingWithPolarContext>> enrichingResultReceivers = Arrays
.asList(filteringProcessor);
enrichingProcessor = new AbstractEnrichingProcessor<Triple<GPSFixMoving, TrackedRace, Competitor>, GPSFixMovingWithPolarContext>(executor, enrichingResultReceivers) {
enrichingProcessor = new AbstractEnrichingProcessor<Triple<GPSFixMoving, TrackedRace, Competitor>, GPSFixMovingWithPolarContext>(
executor, enrichingResultReceivers) {
@Override
protected GPSFixMovingWithPolarContext enrich(Triple<GPSFixMoving, TrackedRace, Competitor> element) {
return new GPSFixMovingWithPolarContext(element.getA(), element.getB(), element.getC());
return new GPSFixMovingWithPolarContext(element.getA(), element.getB(), element.getC(),
defaultPolarSheetGenerationSettings);
}
};
}
@@ -70,4 +87,10 @@ public class PolarDataMiner {
enrichingProcessor.onElement(new Triple<GPSFixMoving, TrackedRace, Competitor>(fix, trackedRace, competitor));
}
public boolean isCurrentlyActiveAndOrHasQueue() {
boolean isActive = executor.getActiveCount() > 0;
boolean hasQueue = executor.getQueue().size() > 0;
return isActive || hasQueue;
}
}
@@ -0,0 +1,33 @@
package com.sap.sailing.polars.mining;
public class RoundedAngleToTheWind {
private final int angle;
public RoundedAngleToTheWind(int angle) {
this.angle = angle;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + angle;
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
RoundedAngleToTheWind other = (RoundedAngleToTheWind) obj;
if (angle != other.angle)
return false;
return true;
}
}
@@ -0,0 +1,44 @@
package com.sap.sailing.polars.mining;
import com.sap.sailing.domain.common.WindStepping;
public class WindSpeedLevel {
private final int level;
private final WindStepping stepping;
public WindSpeedLevel(int level, WindStepping stepping) {
this.level = level;
this.stepping = stepping;
}
@Override
public int hashCode() {
final int prime = 31;
int result = 1;
result = prime * result + level;
result = prime * result + ((stepping == null) ? 0 : stepping.hashCode());
return result;
}
@Override
public boolean equals(Object obj) {
if (this == obj)
return true;
if (obj == null)
return false;
if (getClass() != obj.getClass())
return false;
WindSpeedLevel other = (WindSpeedLevel) obj;
if (level != other.level)
return false;
if (stepping == null) {
if (other.stepping != null)
return false;
} else if (!stepping.equals(other.stepping))
return false;
return true;
}
}
@@ -4,6 +4,7 @@ import com.sap.sse.datamining.shared.GroupKey;
public class CompoundGroupKey extends AbstractGroupKey {
private static final long serialVersionUID = -7902450253393172550L;
private GroupKey mainKey;
@@ -0,0 +1,26 @@
package com.sap.sse.datamining.factories;
import java.util.Iterator;
import com.sap.sse.datamining.functions.Function;
import com.sap.sse.datamining.shared.GroupKey;
import com.sap.sse.datamining.shared.impl.CompoundGroupKey;
import com.sap.sse.datamining.shared.impl.GenericGroupKey;
public class GroupKeyFactory {
public static <DataType> GroupKey createCompoundKeyFor(DataType input, Iterator<Function<?>> dimensionsIterator) {
Function<?> mainDimension = dimensionsIterator.next();
GroupKey key = createGroupKeyFor(input, mainDimension);
if (dimensionsIterator.hasNext()) {
key = new CompoundGroupKey(key, createCompoundKeyFor(input, dimensionsIterator));
}
return key;
}
public static <DataType> GroupKey createGroupKeyFor(DataType input, Function<?> mainDimension) {
Object keyValue = mainDimension.tryToInvoke(input);
return new GenericGroupKey<Object>(keyValue);
}
}
@@ -1,16 +1,13 @@
package com.sap.sse.datamining.impl.components;
import java.util.Collection;
import java.util.Iterator;
import java.util.concurrent.Callable;
import java.util.concurrent.ExecutorService;
import com.sap.sse.datamining.AdditionalResultDataBuilder;
import com.sap.sse.datamining.components.Processor;
import com.sap.sse.datamining.factories.GroupKeyFactory;
import com.sap.sse.datamining.functions.Function;
import com.sap.sse.datamining.shared.GroupKey;
import com.sap.sse.datamining.shared.impl.CompoundGroupKey;
import com.sap.sse.datamining.shared.impl.GenericGroupKey;
public class ParallelMultiDimensionalGroupingProcessor<DataType>
extends AbstractSimpleParallelProcessor<DataType, GroupedDataEntry<DataType>> {
@@ -47,25 +44,12 @@ public class ParallelMultiDimensionalGroupingProcessor<DataType>
return new Callable<GroupedDataEntry<DataType>>() {
@Override
public GroupedDataEntry<DataType> call() throws Exception {
return new GroupedDataEntry<DataType>(createCompoundKeyFor(element, dimensions.iterator()), element);
return new GroupedDataEntry<DataType>(GroupKeyFactory.createCompoundKeyFor(element,
dimensions.iterator()), element);
}
};
}
private GroupKey createCompoundKeyFor(DataType input, Iterator<Function<?>> dimensionsIterator) {
Function<?> mainDimension = dimensionsIterator.next();
GroupKey key = createGroupKeyFor(input, mainDimension);
if (dimensionsIterator.hasNext()) {
key = new CompoundGroupKey(key, createCompoundKeyFor(input, dimensionsIterator));
}
return key;
}
private GroupKey createGroupKeyFor(DataType input, Function<?> mainDimension) {
Object keyValue = mainDimension.tryToInvoke(input);
return new GenericGroupKey<Object>(keyValue);
}
@Override
protected void setAdditionalData(AdditionalResultDataBuilder additionalDataBuilder) {
}