Merge branch 'bug1952'

This commit is contained in:
Axel Uhl committed 2016-12-21 21:16:45 +01:00
commit 3991cf37e2
5 files changed
+73 -17

No files matched your search

@@ -20,7 +20,10 @@ public interface Position extends Serializable {
Distance getDistance(Position p);
/**
* The bearing from this position towards <code>p</code> on a great circle
* The bearing from this position towards <code>p</code> on a great circle.
*
* @return {@code null} if {@code p} is {@code null}, otherwise the bearing from {@code this} {@link Position} to
* {@code p} along a great circle
*/
Bearing getBearingGreatCircle(Position p);
@@ -3,19 +3,22 @@ package com.sap.sailing.domain.test;
import static org.junit.Assert.assertEquals;
import java.util.Collections;
import java.util.List;
import org.junit.Before;
import org.junit.Test;
import com.sap.sailing.domain.base.Competitor;
import com.sap.sailing.domain.base.Mark;
import com.sap.sailing.domain.common.Bearing;
import com.sap.sailing.domain.common.Distance;
import com.sap.sailing.domain.common.NauticalSide;
import com.sap.sailing.domain.common.Position;
import com.sap.sailing.domain.common.impl.DegreeBearingImpl;
import com.sap.sailing.domain.common.impl.KnotSpeedWithBearingImpl;
import com.sap.sailing.domain.common.impl.MeterDistance;
import com.sap.sailing.domain.common.tracking.GPSFix;
import com.sap.sailing.domain.common.tracking.impl.GPSFixImpl;
import com.sap.sailing.domain.common.tracking.impl.GPSFixMovingImpl;
import com.sap.sailing.domain.tracking.DynamicGPSFixTrack;
import com.sap.sailing.domain.tracking.DynamicTrackedRace;
import com.sap.sailing.domain.tracking.LineDetails;
@@ -25,12 +28,14 @@ import com.sap.sse.common.impl.MillisecondsTimePoint;
public class LineAnalysisTest extends TrackBasedTest {
private TimePoint now;
private DynamicTrackedRace trackedRace;
private Competitor competitor;
@Before
public void setUp() {
final List<Competitor> emptyCompetitorList = Collections.emptyList();
competitor = createCompetitor("Test");
final Iterable<Competitor> competitorListWithOneCompetitor = Collections.singletonList(competitor);
now = MillisecondsTimePoint.now();
trackedRace = createTestTrackedRace("Test Regatta", "Test Race", "505", emptyCompetitorList, now, /* useMarkPassingCalculator */ false);
trackedRace = createTestTrackedRace("Test Regatta", "Test Race", "505", competitorListWithOneCompetitor, now, /* useMarkPassingCalculator */ false);
}
@Test
@@ -56,6 +61,37 @@ public class LineAnalysisTest extends TrackBasedTest {
assertEquals(advantage.getMeters(), finishLine.getAdvantage().getMeters(), 0.1);
assertEquals(NauticalSide.PORT, finishLine.getAdvantageousSideWhileApproachingLine());
}
@Test
public void testDistanceToLineWithOrthogonalProjectionOnLine() {
Mark left = getMark("Left lee gate buoy");
Mark right = getMark("Right lee gate buoy");
final Position leftPos = trackedRace.getOrCreateTrack(left).getEstimatedPosition(now, /* extrapolate */ false);
final Position rightPos = trackedRace.getOrCreateTrack(right).getEstimatedPosition(now, /* extrapolate */ false);
final Bearing lineBearingFromLeftToRight = leftPos.getBearingGreatCircle(rightPos);
final Bearing awayFromCourseSideOrthogonalToLine = lineBearingFromLeftToRight.add(new DegreeBearingImpl(90));
final Position lineMiddle = leftPos.translateGreatCircle(lineBearingFromLeftToRight, leftPos.getDistance(rightPos).scale(0.5));
final Distance distanceBehindLine = new MeterDistance(10);
final Position behindMiddleOfLine = lineMiddle.translateGreatCircle(awayFromCourseSideOrthogonalToLine, distanceBehindLine);
trackedRace.getTrack(competitor).add(new GPSFixMovingImpl(behindMiddleOfLine, now, new KnotSpeedWithBearingImpl(5, new DegreeBearingImpl(45))));
assertEquals(distanceBehindLine.getMeters(), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.000001);
}
@Test
public void testDistanceToLineWithOrthogonalProjectionOutsideLine() {
Mark left = getMark("Left lee gate buoy");
Mark right = getMark("Right lee gate buoy");
final Position leftPos = trackedRace.getOrCreateTrack(left).getEstimatedPosition(now, /* extrapolate */ false);
final Position rightPos = trackedRace.getOrCreateTrack(right).getEstimatedPosition(now, /* extrapolate */ false);
final Bearing lineBearingFromLeftToRight = leftPos.getBearingGreatCircle(rightPos);
final Bearing awayFromCourseSideOrthogonalToLine = lineBearingFromLeftToRight.add(new DegreeBearingImpl(90));
final Position outsideOfLine = rightPos.translateGreatCircle(lineBearingFromLeftToRight, new MeterDistance(30)); // 30m right of start boat
final Distance distanceBehindLine = new MeterDistance(10);
final Position behindOutsideOfLine = outsideOfLine.translateGreatCircle(awayFromCourseSideOrthogonalToLine, distanceBehindLine);
trackedRace.getTrack(competitor).add(new GPSFixMovingImpl(behindOutsideOfLine, now, new KnotSpeedWithBearingImpl(5, new DegreeBearingImpl(45))));
// the distance is that of the hypotenuse formed by the two cathesuses of 10m and 30m length, respectively:
assertEquals(Math.sqrt(10.*10.+30.*30.), trackedRace.getDistanceToStartLine(competitor, now).getMeters(), 0.001);
}
private Mark getMark(String name) {
for (Mark mark : trackedRace.getMarks()) {
@@ -3455,29 +3455,46 @@ public abstract class TrackedRaceImpl extends TrackedRaceWithWindEssentials impl
if (startWaypoint == null) {
result = null;
} else {
Position competitorPosition = getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolate */false);
Position competitorPosition = getTrack(competitor).getEstimatedPosition(timePoint, /* extrapolate */ false);
if (competitorPosition == null) {
result = null;
} else {
Iterable<Mark> marks = startWaypoint.getControlPoint().getMarks();
Iterator<Mark> marksIterator = marks.iterator();
Mark first = marksIterator.next();
Position firstPosition = getOrCreateTrack(first).getEstimatedPosition(timePoint, /* extrapolate */false);
Position firstPosition = getOrCreateTrack(first).getEstimatedPosition(timePoint, /* extrapolate */ false);
if (firstPosition == null) {
result = null;
} else {
if (marksIterator.hasNext()) {
// it's a line / gate
Mark second = marksIterator.next();
Position secondPosition = getOrCreateTrack(second).getEstimatedPosition(timePoint, /* extrapolate */
false);
final Bearing bearingGreatCircle = firstPosition.getBearingGreatCircle(secondPosition);
if (bearingGreatCircle == null) {
Position secondPosition = getOrCreateTrack(second).getEstimatedPosition(timePoint, /* extrapolate */ false);
if (secondPosition == null) {
result = null;
} else {
Position competitorProjectedOntoStartLine = competitorPosition.projectToLineThrough(
firstPosition, bearingGreatCircle);
result = competitorPosition.getDistance(competitorProjectedOntoStartLine);
final Bearing lineBearingGreatCircleFromFirstToSecond = firstPosition.getBearingGreatCircle(secondPosition);
// if the competitor is outside of the line when projected orthogonally, compute the distance to
// the nearest of the line's marks (see also bug 1952):
final Bearing bearingFromFirstToCompetitor = firstPosition.getBearingGreatCircle(competitorPosition);
final Bearing angleBetweenFromFirstToCompetitorAndLine = lineBearingGreatCircleFromFirstToSecond.getDifferenceTo(bearingFromFirstToCompetitor);
if (angleBetweenFromFirstToCompetitorAndLine.getDegrees() < -90 || angleBetweenFromFirstToCompetitorAndLine.getDegrees() > 90) {
// competitor's orthogonal projection onto the line's extension is outside of the line's ends on the side
// of the first mark; use distance between competitor and first mark:
result = competitorPosition.getDistance(firstPosition);
} else {
final Bearing bearingFromSecondToCompetitor = secondPosition.getBearingGreatCircle(competitorPosition);
final Bearing angleBetweenFromSecondToCompetitorAndReversedLine = lineBearingGreatCircleFromFirstToSecond.reverse().getDifferenceTo(bearingFromSecondToCompetitor);
if (angleBetweenFromSecondToCompetitorAndReversedLine.getDegrees() < -90 || angleBetweenFromSecondToCompetitorAndReversedLine.getDegrees() > 90) {
// competitor's orthogonal projection onto the line's extension is outside of the line's ends on the side
// of the first mark; use distance between competitor and first mark:
result = competitorPosition.getDistance(secondPosition);
} else {
Position competitorProjectedOntoStartLine = competitorPosition.projectToLineThrough(
firstPosition, lineBearingGreatCircleFromFirstToSecond);
result = competitorPosition.getDistance(competitorProjectedOntoStartLine);
}
}
}
} else {
result = competitorPosition.getDistance(firstPosition);
@@ -651,8 +651,8 @@ distanceToLineFiveSecondsBeforeStart=Distance to line 5s before start
speedOverGroundAtRaceStart=SOG at Start
speedOverGroundFiveSecondsBeforeStart=SOG 5s before start
speedOverGroundWhenPassingStart=SOG when Starting
distanceToLineAtRaceStartTooltip=Distance to start line at race start time
distanceToLineFiveSecondsBeforeStartTooltip=Distance to start line five seconds before the start
distanceToLineAtRaceStartTooltip=Distance to start line at race start time; distance to nearest line end in case the competitor projects beyond the line's ends
distanceToLineFiveSecondsBeforeStartTooltip=Distance to start line five seconds before the start; distance to nearest line end in case the competitor projects beyond the line's ends
speedOverGroundAtRaceStartTooltip=Speed over ground at race start time
speedOverGroundFiveSecondsBeforeStartTooltip=Speed over ground five seconds before the start
speedOverGroundWhenPassingStartTooltip=Speed over ground when the competitor passed the start
@@ -642,9 +642,9 @@ operatorEndsWith=Endet mit
nationality=Nationalität
numberMustBePositive=Die Zahl muß größer als Null sein.
distanceToLineAtRaceStart=Abstand zur Linie bei Start
distanceToLineAtRaceStartTooltip=Abstand zur Linie zur Startzeit des Rennens
distanceToLineAtRaceStartTooltip=Abstand zur Linie zur Startzeit des Rennens; Abstand zum näheren Ende der Linie falls Projektion des Teilnehmers außerhalb der Linie liegt
distanceToLineFiveSecondsBeforeStart=Abstand zur Linie 5s vor Start
distanceToLineFiveSecondsBeforeStartTooltip=Abstand zur Linie fünf Sekunden vor dem Start
distanceToLineFiveSecondsBeforeStartTooltip=Abstand zur Linie fünf Sekunden vor dem Start; Abstand zum näheren Ende der Linie falls Projektion des Teilnehmers außerhalb der Linie liegt
speedOverGroundAtRaceStart=FüG bei Rennstart
speedOverGroundAtRaceStartTooltip=Fahrt über Grund zur Startzeit des Rennens
speedOverGroundFiveSecondsBeforeStart=FüG 5s vor Start