Skip to content

Commit 49d2b42

Browse files
author
Zach Glueckert
committed
Corrected floating point error in rhumb line end point and distance calculation. Ported solution from WWA and updated a few unit tests.
1 parent b452ca3 commit 49d2b42

File tree

2 files changed

+45
-9
lines changed

2 files changed

+45
-9
lines changed

Diff for: src/gov/nasa/worldwind/geom/LatLon.java

+25-7
Original file line numberDiff line numberDiff line change
@@ -24,6 +24,12 @@ public class LatLon
2424
{
2525
public static final LatLon ZERO = new LatLon(Angle.ZERO, Angle.ZERO);
2626

27+
/**
28+
* A near zero threshold used in some of the rhumb line calculations where floating point calculations cause
29+
* errors.
30+
*/
31+
protected final static double NEAR_ZERO_THRESHOLD = 1e-15;
32+
2733
public final Angle latitude;
2834
public final Angle longitude;
2935

@@ -719,12 +725,18 @@ public static Angle rhumbDistance(LatLon p1, LatLon p2)
719725
// Taken from http://www.movable-type.co.uk/scripts/latlong.html
720726
double dLat = lat2 - lat1;
721727
double dLon = lon2 - lon1;
722-
double dPhi = Math.log(Math.tan(lat2 / 2.0 + Math.PI / 4.0) / Math.tan(lat1 / 2.0 + Math.PI / 4.0));
723-
double q = dLat / dPhi;
724-
if (Double.isNaN(dPhi) || Double.isNaN(q))
728+
729+
double q;
730+
if (Math.abs(dLat) < NEAR_ZERO_THRESHOLD)
725731
{
726732
q = Math.cos(lat1);
727733
}
734+
else
735+
{
736+
double dPhi = Math.log(Math.tan(lat2 / 2.0 + Math.PI / 4.0) / Math.tan(lat1 / 2.0 + Math.PI / 4.0));
737+
q = dLat / dPhi;
738+
}
739+
728740
// If lonChange over 180 take shorter rhumb across 180 meridian.
729741
if (Math.abs(dLon) > Math.PI)
730742
{
@@ -811,13 +823,19 @@ public static LatLon rhumbEndPosition(LatLon p, Angle rhumbAzimuth, Angle pathLe
811823
return p;
812824

813825
// Taken from http://www.movable-type.co.uk/scripts/latlong.html
814-
double lat2 = lat1 + distance * Math.cos(azimuth);
815-
double dPhi = Math.log(Math.tan(lat2 / 2.0 + Math.PI / 4.0) / Math.tan(lat1 / 2.0 + Math.PI / 4.0));
816-
double q = (lat2 - lat1) / dPhi;
817-
if (Double.isNaN(dPhi) || Double.isNaN(q) || Double.isInfinite(q))
826+
double dLat = distance * Math.cos(azimuth);
827+
double lat2 = lat1 + dLat;
828+
double q;
829+
if (Math.abs(dLat) < NEAR_ZERO_THRESHOLD)
818830
{
819831
q = Math.cos(lat1);
820832
}
833+
else
834+
{
835+
double dPhi = Math.log(Math.tan(lat2 / 2.0 + Math.PI / 4.0) / Math.tan(lat1 / 2.0 + Math.PI / 4.0));
836+
q = (lat2 - lat1) / dPhi;
837+
}
838+
821839
double dLon = distance * Math.sin(azimuth) / q;
822840
// Handle latitude passing over either pole.
823841
if (Math.abs(lat2) > Math.PI / 2.0)

Diff for: test/gov/nasa/worldwind/geom/LatLonTest.java

+20-2
Original file line numberDiff line numberDiff line change
@@ -653,8 +653,8 @@ public void testTrivialAzimuthB()
653653
double azimuthRadians = Math.toRadians(90.0);
654654
double distanceRadians = Math.toRadians(360.0);
655655
LatLon end = LatLon.rhumbEndPosition(begin, azimuthRadians, distanceRadians);
656-
assertEquals("Trivial Azimuth B (lat)", 2.204291436880279e-14, end.getLatitude().degrees, THRESHOLD);
657-
assertEquals("Trivial Azimuth B (lon)", -96.67061650574995, end.getLongitude().degrees,
656+
assertEquals("Trivial Azimuth B (lat)", 0.0, end.getLatitude().degrees, THRESHOLD);
657+
assertEquals("Trivial Azimuth B (lon)", 0.0, end.getLongitude().degrees,
658658
1e-1); // Custom threshold
659659
}
660660

@@ -681,6 +681,24 @@ public void testKnownPointsB()
681681
assertEquals("Known points B (lat)", 56.9679782407693, end.getLatitude().degrees, THRESHOLD);
682682
assertEquals("Known points B (lon)", 95.78434282105843, end.getLongitude().degrees, THRESHOLD);
683683
}
684+
685+
//////////////////////////////////////////////////////////
686+
// Test problem points.
687+
//////////////////////////////////////////////////////////
688+
689+
public void testProblemPointsA()
690+
{
691+
// This specific lat/lon and distance identified a floating point error
692+
Angle initialLat = Angle.fromDegrees(4.076552742498428);
693+
Angle initialLon = Angle.fromDegrees(-21.377644877408443);
694+
Angle azimuth = Angle.fromDegrees(90.0);
695+
Angle distance = Angle.fromDegrees(8.963656110719409);
696+
LatLon begin = LatLon.fromRadians(initialLat.getRadians(), initialLon.getRadians());
697+
LatLon end = LatLon.rhumbEndPosition(begin, azimuth, distance);
698+
assertEquals("Problem points A (lat)", initialLat.getDegrees(), end.getLatitude().getDegrees(), THRESHOLD);
699+
assertEquals("Problem points A (lon)", -12.391252821313167, end.getLongitude().getDegrees(), THRESHOLD);
700+
}
701+
684702
}
685703

686704
public static class EllipsoidalDistanceTests extends TestCase

0 commit comments

Comments
 (0)