package org.opensha2.geo; import static org.junit.Assert.assertEquals; import static org.junit.Assert.assertTrue; import static org.opensha2.geo.Coordinates.EARTH_RADIUS_MEAN; import static org.opensha2.geo.Locations.angle; import static org.opensha2.geo.Locations.areSimilar; import static org.opensha2.geo.Locations.azimuth; import static org.opensha2.geo.Locations.azimuthRad; import static org.opensha2.geo.Locations.bounds; import static org.opensha2.geo.Locations.distanceToLine; import static org.opensha2.geo.Locations.distanceToLineFast; import static org.opensha2.geo.Locations.distanceToSegment; import static org.opensha2.geo.Locations.distanceToSegmentFast; import static org.opensha2.geo.Locations.horzDistance; import static org.opensha2.geo.Locations.horzDistanceFast; import static org.opensha2.geo.Locations.isPole; import static org.opensha2.geo.Locations.linearDistance; import static org.opensha2.geo.Locations.linearDistanceFast; import static org.opensha2.geo.Locations.location; import static org.opensha2.geo.Locations.vertDistance; import org.opensha2.eq.model.Distance; import org.opensha2.util.Maths; import org.junit.Test; import com.google.common.collect.Lists; import java.util.List; import java.util.Random; @SuppressWarnings("javadoc") public class LocationsTest { // private static LocationList locs1, locs2; // private static Location p1,p2,p3,p4,p5,p6,p7,p8,p9; // // private double result_p3p4_p8 = 78.62078721818267; // private double result_p4_p8 = 111.19505230826488; // private double result_p6p7_p9 = 78.55334545519592; // private double result_p6_p9 = 111.04266335361307; // // @BeforeClass // public static void setUp() { // p1 = Location.create(-5,0); // p2 = Location.create(-3,-2); // p3 = Location.create(-2,-2); // p4 = Location.create(0,0); // p5 = Location.create(2,2); // p6 = Location.create(3,2); // p7 = Location.create(5,0); // // p8 = Location.create(-1,0); // p9 = Location.create(3,1); // // locs1 = LocationList.create(p1,p2,p3,p4,p5,p6,p7); // locs2 = LocationList.create(p1,p3,p2,p4,p6,p5,p7); // } // // /** // * @throws java.lang.Exception // */ // @BeforeClass public static void setUpBeforeClass() throws Exception {} // // // @Test // public final void testMinDistToLocation() { // assertEquals(result_p4_p8, ll1.minDistToLocation(p8), 0); // assertEquals(result_p6_p9, ll1.minDistToLocation(p9), 0); // assertEquals( // Locations.horzDistanceFast(p6, p9), // ll1.minDistToLocation(p9), 0); // assertEquals( // Locations.horzDistanceFast(p4, p8), // ll1.minDistToLocation(p8), 0); // } // // @Test // public final void testMinDistToLine() { // assertEquals(result_p6p7_p9, ll1.minDistToLine(p9), 0); // assertEquals(result_p3p4_p8, ll1.minDistToLine(p8), 0); // // test against underlying distToSegment // assertEquals( // Locations.distanceToLineSegmentFast(p6, p7, p9), // ll1.minDistToLine(p9), 0); // assertEquals( // Locations.distanceToLineSegmentFast(p3, p4, p8), // ll1.minDistToLine(p8), 0); // } // short-range, small-angle test points private static Location L1 = Location.create(32.6, 20.4); private static Location L2 = Location.create(32.4, 20.0); private static Location L3 = Location.create(32.2, 20.6); private static Location L4 = Location.create(32.0, 20.2, 10.0); // polar and long-distance, large-angle test points private static Location L5 = Location.create(90, 0); private static Location L6 = Location.create(-90, 0); // Expected results from methods in this class were computed using the // class methods and compared to the results provided by one or more // reputable online calculators. // p2p: L1 to L2 eg // vd: Vincenty distance (most accurate, provided for comparison) // sd: expected values of surfaceDistance() // fsd: expected values of fastSurfaceDistance() // angle: in radians between points // az-rad: azimuth in radians from L1 to L2 // az-deg: azimuth in degrees from L1 to L2 // p2p vd (km) sd fsd angle az-rad az-deg // --- --------- --------- --------- ----------- ----------- --------- // d51 6393.578 6382.596 6474.888 1.001818991 3.141592654 180.0 // d25 6415.757 6404.835 6493.824 1.005309649 0.0 0.0 // d46 13543.818 13565.796 13707.303 2.129301687 3.141592654 180.0 // d63 13565.996 13588.035 13735.216 2.132792346 0.0 0.0 // d12 43.645957 43.6090311 43.6090864 0.006844919 4.179125015 239.44623 // d13 48.183337 48.2790582 48.2790921 0.007577932 2.741190313 157.05864 // d14 69.150258 69.3145862 69.3146382 0.010879690 3.417161139 195.78891 // d23 60.706703 60.6198752 60.6200022 0.009514959 1.943625801 111.36156 // d42 48.198212 48.2952067 48.2952403 0.007580467 5.883856933 337.12017 // d43 43.787840 43.7518411 43.7518956 0.006867335 1.035735858 59.34329 // fdtl dtl // d321 34.472999888 34.425229936 // d231 34.472999888 -34.425229936 // d432 47.859144611 -47.851004687 // d413 30.170948729 30.205855981 // deltas - based on what decimal place known values above were clipped private static double ldD = 0.001; // long-distance private static double sdD = 0.0000001; // short-distance private static double angleD = 0.000000001; // angle private static double azrD = 0.000000001; // azimuth-rad private static double azdD = 0.00001; // azimuth-deg private static double dtlD = 0.000000001; // dist to line // @Test // public final void testLocations() { // // silly test of no arg private constructor // try { // Object obj = TestUtils.callPrivateNoArgConstructor( // Locations.class); // } catch (Exception e) { // fail("Private no-arg constructor failed to initialize: " + // e.getMessage()); // } // } @Test public final void testAngle() { assertEquals(1.001818991, angle(L5, L1), angleD); assertEquals(1.005309649, angle(L2, L5), angleD); assertEquals(2.129301687, angle(L4, L6), angleD); assertEquals(2.132792346, angle(L6, L3), angleD); assertEquals(0.006844919, angle(L1, L2), angleD); assertEquals(0.007577932, angle(L1, L3), angleD); assertEquals(0.010879690, angle(L1, L4), angleD); assertEquals(0.009514959, angle(L2, L3), angleD); assertEquals(0.007580467, angle(L4, L2), angleD); assertEquals(0.006867335, angle(L4, L3), angleD); } @Test public final void testHorzDistance() { assertEquals(6382.596, horzDistance(L5, L1), ldD); assertEquals(6404.835, horzDistance(L2, L5), ldD); assertEquals(13565.796, horzDistance(L4, L6), ldD); assertEquals(13588.035, horzDistance(L6, L3), ldD); assertEquals(43.6090311, horzDistance(L1, L2), sdD); assertEquals(48.2790582, horzDistance(L1, L3), sdD); assertEquals(69.3145862, horzDistance(L1, L4), sdD); assertEquals(60.6198752, horzDistance(L2, L3), sdD); assertEquals(48.2952067, horzDistance(L4, L2), sdD); assertEquals(43.7518411, horzDistance(L4, L3), sdD); } // same locations, different reference frame // distance between 1 and 2 should be the same as between 4 and 5 // P3 and P6 are used to test distanceToLineFast private static Location P1 = Location.create(32.0, -160.0, 2.0); private static Location P2 = Location.create(31.0, -163.0, 0.0); private static Location P3 = Location.create(33.0, -164.0, 0.0); private static Location P4 = Location.create(32.0, 200.0, 2.0); private static Location P5 = Location.create(31.0, 197.0, 0.0); private static Location P6 = Location.create(33.0, 196.0, 0.0); @Test public final void testHorzDistanceFast() { assertEquals(6474.888, horzDistanceFast(L5, L1), ldD); assertEquals(6493.824, horzDistanceFast(L2, L5), ldD); assertEquals(13707.303, horzDistanceFast(L4, L6), ldD); assertEquals(13735.216, horzDistanceFast(L6, L3), ldD); assertEquals(43.6090864, horzDistanceFast(L1, L2), sdD); assertEquals(48.2790921, horzDistanceFast(L1, L3), sdD); assertEquals(69.3146382, horzDistanceFast(L1, L4), sdD); assertEquals(60.6200022, horzDistanceFast(L2, L3), sdD); assertEquals(48.2952403, horzDistanceFast(L4, L2), sdD); assertEquals(43.7518956, horzDistanceFast(L4, L3), sdD); // test change to allow lon values up to 360 double original = horzDistanceFast(P1, P2); double updated = horzDistanceFast(P4, P5); assertEquals(original, updated, sdD); } @Test public final void testVertDistance() { Location L1 = Location.create(23, -32, 2); Location L2 = Location.create(-12, -112, -2); Location L3 = Location.create(-34, 86, 10); assertEquals(-4, vertDistance(L1, L2), 0); assertEquals(8, vertDistance(L1, L3), 0); assertEquals(12, vertDistance(L2, L3), 0); } @Test public final void testLinearDistance() { double delta = 0.000000001; // small angles Location L1 = Location.create(20.0, 20.0, 2); Location L2 = Location.create(20.1, 20.1, 2); Location L3 = Location.create(20.1, 20.1, 17); double sd12 = horzDistance(L1, L2); // 15.256270609 double ld12 = linearDistance(L1, L2); // 15.251477684 double ld13 = linearDistance(L1, L3); // 21.378955649 assertTrue("Linear distance should be shorter", ld12 < sd12); assertTrue("ld12 should be shorter than ld13", ld13 > ld12); assertEquals(15.251477684, ld12, delta); assertEquals(21.378955649, ld13, delta); // large angles Location L4 = Location.create(45.0, -20.0, 2); Location L5 = Location.create(-40.0, 20.0, 17); Location L6 = Location.create(-50.0, 20.0, 17); double ld45 = linearDistance(L4, L5); // 9172.814801278 double ld46 = linearDistance(L4, L6); // 9828.453361410 assertEquals(9172.814801278, ld45, delta); assertEquals(9828.453361410, ld46, delta); } @Test public final void testLinearDistanceFast() { double delta = 0.000000001; // small angles Location L1 = Location.create(20.0, 20.0, 2); Location L2 = Location.create(20.1, 20.1, 2); Location L3 = Location.create(20.1, 20.1, 17); double ld12 = linearDistanceFast(L1, L2); // 15.256271986 double ld13 = linearDistanceFast(L1, L3); // 21.395182516 assertEquals(15.256271986, ld12, delta); assertEquals(21.395182516, ld13, delta); // test change to allow lon values up to 360 double original = linearDistanceFast(P1, P2); double updated = linearDistanceFast(P4, P5); assertEquals(original, updated, sdD); } // additional locoations for line and segment tests private static Location l1 = Location.create(2, 0); private static Location l2 = Location.create(4, 0); private static Location p1 = Location.create(1, 0); private static Location p2 = Location.create(1, -1); private static Location p3 = Location.create(3, -1); private static Location p4 = Location.create(5, -1); private static Location p5 = Location.create(5, 0); private static Location p6 = Location.create(5, 1); private static Location p7 = Location.create(3, 1); private static Location p8 = Location.create(1, 1); @Test public final void testDistanceToLine() { // set 1 assertEquals(34.425229936, distanceToLine(L3, L2, L1), dtlD); assertEquals(-34.425229936, distanceToLine(L2, L3, L1), dtlD); assertEquals(-47.851004687, distanceToLine(L4, L3, L2), dtlD); assertEquals(30.205855981, distanceToLine(L4, L1, L3), dtlD); // set 2 assertEquals(0.0, distanceToLine(l1, l2, p1), 0); assertEquals(-111.17811504377568, distanceToLine(l1, l2, p2), 0); assertEquals(-111.04264791008788, distanceToLine(l1, l2, p3), 0); assertEquals(-110.77187883896175, distanceToLine(l1, l2, p4), 0); assertEquals(0.0, distanceToLine(l1, l2, p5), 0); assertEquals(110.7718788389617, distanceToLine(l1, l2, p6), 0); assertEquals(111.04264791008788, distanceToLine(l1, l2, p7), 0); assertEquals(111.17811504377575, distanceToLine(l1, l2, p8), 0); } @Test public final void testDistanceToLineFast() { // set 1 assertEquals(34.472999888, distanceToLineFast(L3, L2, L1), dtlD); assertEquals(-34.472999888, distanceToLineFast(L2, L3, L1), dtlD); assertEquals(-47.859144611, distanceToLineFast(L4, L3, L2), dtlD); assertEquals(30.170948729, distanceToLineFast(L4, L1, L3), dtlD); // set 2 assertEquals(0.0, distanceToLineFast(l1, l2, p1), 0); assertEquals(-111.12731528678844, distanceToLineFast(l1, l2, p2), 0); assertEquals(-111.04266335361307, distanceToLineFast(l1, l2, p3), 0); assertEquals(-110.92418674948573, distanceToLineFast(l1, l2, p4), 0); assertEquals(0.0, distanceToLineFast(l1, l2, p5), 0); assertEquals(110.92418674948573, distanceToLineFast(l1, l2, p6), 0); assertEquals(111.04266335361307, distanceToLineFast(l1, l2, p7), 0); assertEquals(111.12731528678844, distanceToLineFast(l1, l2, p8), 0); // test change to allow lon values up to 360 double original = distanceToLineFast(P2, P3, P1); double updated = distanceToLineFast(P5, P6, P4); assertEquals(original, updated, sdD); } @Test public final void testDistanceToSegment() { // set 1 assertEquals(34.425229936, distanceToSegment(L3, L2, L1), dtlD); assertEquals(34.425229936, distanceToSegment(L2, L3, L1), dtlD); assertEquals(47.851004687, distanceToSegment(L4, L3, L2), dtlD); assertEquals(30.205855981, distanceToSegment(L4, L1, L3), dtlD); // set 2 assertEquals(111.19505230826488, distanceToSegment(l1, l2, p1), 0); assertEquals(157.22560972181338, distanceToSegment(l1, l2, p2), 0); assertEquals(111.04264791008788, distanceToSegment(l1, l2, p3), 0); assertEquals(157.0103400810619, distanceToSegment(l1, l2, p4), 0); assertEquals(111.19505230826486, distanceToSegment(l1, l2, p5), 0); assertEquals(157.0103400810619, distanceToSegment(l1, l2, p6), 0); assertEquals(111.04264791008788, distanceToSegment(l1, l2, p7), 0); assertEquals(157.22560972181338, distanceToSegment(l1, l2, p8), 0); } @Test public final void testDistanceToSegmentFast() { // set 1 assertEquals(34.472999888, distanceToSegmentFast(L3, L2, L1), dtlD); assertEquals(34.472999888, distanceToSegmentFast(L2, L3, L1), dtlD); assertEquals(47.859144611, distanceToSegmentFast(L4, L3, L2), dtlD); assertEquals(30.170948729, distanceToSegmentFast(L4, L1, L3), dtlD); // set 2 assertEquals(111.19505230826488, distanceToSegmentFast(l1, l2, p1), 0); assertEquals(157.2056610325692, distanceToSegmentFast(l1, l2, p2), 0); assertEquals(111.04266335361307, distanceToSegmentFast(l1, l2, p3), 0); assertEquals(157.0621369518209, distanceToSegmentFast(l1, l2, p4), 0); assertEquals(111.19505230826486, distanceToSegmentFast(l1, l2, p5), 0); assertEquals(157.0621369518209, distanceToSegmentFast(l1, l2, p6), 0); assertEquals(111.04266335361307, distanceToSegmentFast(l1, l2, p7), 0); assertEquals(157.2056610325692, distanceToSegmentFast(l1, l2, p8), 0); // test change to allow lon values up to 360 double original = distanceToSegmentFast(P2, P3, P1); double updated = distanceToSegmentFast(P5, P6, P4); assertEquals(original, updated, sdD); } @Test public final void testAzimuth() { assertEquals(180.0, azimuth(L5, L1), azdD); assertEquals(0.0, azimuth(L2, L5), azdD); assertEquals(180.0, azimuth(L4, L6), azdD); assertEquals(0.0, azimuth(L6, L3), azdD); assertEquals(239.44623, azimuth(L1, L2), azdD); assertEquals(157.05864, azimuth(L1, L3), azdD); assertEquals(195.78891, azimuth(L1, L4), azdD); assertEquals(111.36156, azimuth(L2, L3), azdD); assertEquals(337.12017, azimuth(L4, L2), azdD); assertEquals(59.34329, azimuth(L4, L3), azdD); } @Test public final void testAzimuthRad() { assertEquals(3.141592654, azimuthRad(L5, L1), azrD); assertEquals(0.0, azimuthRad(L2, L5), azrD); assertEquals(3.141592654, azimuthRad(L4, L6), azrD); assertEquals(0.0, azimuthRad(L6, L3), azrD); assertEquals(4.179125015, azimuthRad(L1, L2), azrD); assertEquals(2.741190313, azimuthRad(L1, L3), azrD); assertEquals(3.417161139, azimuthRad(L1, L4), azrD); assertEquals(1.943625801, azimuthRad(L2, L3), azrD); assertEquals(5.883856933, azimuthRad(L4, L2), azrD); assertEquals(1.035735858, azimuthRad(L4, L3), azrD); } @Test public final void testLocationLocationDoubleDouble() { LocationVector d = LocationVector.create(L1, L2); // TODO need to switch to getAzimuthRad assertTrue(areSimilar(L2, location( L1, d.azimuth(), d.horizontal()))); d = LocationVector.create(L1, L3); assertTrue(areSimilar(L3, location( L1, d.azimuth(), d.horizontal()))); d = LocationVector.create(L2, L3); assertTrue(areSimilar(L3, location( L2, d.azimuth(), d.horizontal()))); } @Test public final void testLocationLocationDirection() { assertTrue(areSimilar(L2, location(L1, LocationVector.create(L1, L2)))); assertTrue(areSimilar(L3, location(L1, LocationVector.create(L1, L3)))); assertTrue(areSimilar(L4, location(L1, LocationVector.create(L1, L4)))); assertTrue(areSimilar(L3, location(L2, LocationVector.create(L2, L3)))); assertTrue(areSimilar(L2, location(L4, LocationVector.create(L4, L2)))); assertTrue(areSimilar(L3, location(L4, LocationVector.create(L4, L3)))); } @Test public final void testLocationVector() { assertEquals(43.6090311, LocationVector.create(L1, L2).horizontal(), sdD); assertEquals(48.2790582, LocationVector.create(L1, L3).horizontal(), sdD); assertEquals(69.3145862, LocationVector.create(L1, L4).horizontal(), sdD); assertEquals(60.6198752, LocationVector.create(L2, L3).horizontal(), sdD); assertEquals(48.2952067, LocationVector.create(L4, L2).horizontal(), sdD); assertEquals(43.7518411, LocationVector.create(L4, L3).horizontal(), sdD); // TODO these will need to be replaced with azimuthRad() assertEquals(239.44623, LocationVector.create(L1, L2).azimuthDegrees(), azdD); assertEquals(157.05864, LocationVector.create(L1, L3).azimuthDegrees(), azdD); assertEquals(195.78891, LocationVector.create(L1, L4).azimuthDegrees(), azdD); assertEquals(111.36156, LocationVector.create(L2, L3).azimuthDegrees(), azdD); assertEquals(337.12017, LocationVector.create(L4, L2).azimuthDegrees(), azdD); assertEquals(59.34329, LocationVector.create(L4, L3).azimuthDegrees(), azdD); assertEquals(0, LocationVector.create(L1, L2).vertical(), 0); assertEquals(0, LocationVector.create(L1, L3).vertical(), 0); assertEquals(10, LocationVector.create(L1, L4).vertical(), 0); assertEquals(0, LocationVector.create(L2, L3).vertical(), 0); assertEquals(-10, LocationVector.create(L4, L2).vertical(), 0); assertEquals(-10, LocationVector.create(L4, L3).vertical(), 0); // reverse tests Location L1 = Location.create(20.0, 20.0, 0); Location L2 = Location.create(20.1, 20.1, 2); LocationVector v = LocationVector.create(L1, L2); double az = v.azimuthDegrees(); double dv = v.vertical(); LocationVector vr = LocationVector.reverseOf(v); assertEquals((az + 180) % 360, vr.azimuthDegrees(), 0); assertEquals(-dv, vr.vertical(), 0); // test plunge LocationVector vPlunge = LocationVector.create(0.0, 2, 2); assertEquals(45.0, vPlunge.plungeDegrees(), 0.0); vPlunge = LocationVector.create(0.0, 2, -2); assertEquals(-45.0, vPlunge.plungeDegrees(), 0.0); } @Test public final void testBisect() { double tol = 0.000000000001; Location p1, p2, p3; LocationVector p2p1, p2p3, vTest; // general case p2 = Location.create(20, 20); p2p1 = LocationVector.create(220 * Maths.TO_RAD, 100, 0); p1 = Locations.location(p2, p2p1); p2p3 = LocationVector.create(90 * Maths.TO_RAD, 100, 0); p3 = Locations.location(p2, p2p3); vTest = Locations.bisect(p1, p2, p3); assertEquals(155, vTest.azimuthDegrees(), tol); // 4th quadrant 270-360 p2p1 = LocationVector.create(320 * Maths.TO_RAD, 100, 0); p1 = Locations.location(p2, p2p1); p2p3 = LocationVector.create(20 * Maths.TO_RAD, 100, 0); p3 = Locations.location(p2, p2p3); vTest = Locations.bisect(p1, p2, p3); assertEquals(170, vTest.azimuthDegrees(), tol); // p1 & p3 coincident p2p1 = LocationVector.create(90 * Maths.TO_RAD, 100, 0); p1 = Locations.location(p2, p2p1); p2p3 = LocationVector.create(90 * Maths.TO_RAD, 100, 0); p3 = Locations.location(p2, p2p3); vTest = Locations.bisect(p1, p2, p3); assertEquals(90, vTest.azimuthDegrees(), tol); // p1, p2, & p3 coincident vTest = Locations.bisect(p2, p2, p2); assertEquals(0, vTest.azimuthDegrees(), tol); } @Test public final void testIsPole() { Location sp = Location.create(-89.999999999999, 0); Location np = Location.create(89.999999999999, 0); Location ll = Location.create(22, 150); assertTrue(isPole(sp)); assertTrue(isPole(np)); assertTrue(!isPole(ll)); } @Test public final void testAreSimilar() { // NOTE the generic tolerance of 0.000000000001 in Locations imposes // different magnitude constraints on depth vs. lat lon Location p1, p2; // compare lats p1 = Location.create(30, 0, 0); p2 = Location.create(30.00000000001, 0, 0); assertTrue(areSimilar(p1, p2)); p2 = Location.create(30.0000000001, 0, 0); assertTrue(!areSimilar(p1, p2)); // compare lons p1 = Location.create(0, -30.0, 0); p2 = Location.create(0, -30.00000000001, 0); assertTrue(areSimilar(p1, p2)); p2 = Location.create(0, -30.0000000001, 0); assertTrue(!areSimilar(p1, p2)); // compare depths p1 = Location.create(0, 0, 5.0); p2 = Location.create(0, 0, 5.0000000000001); assertTrue(areSimilar(p1, p2)); p2 = Location.create(0, 0, 5.000000000001); assertTrue(!areSimilar(p1, p2)); } @Test public void testBounds() { Location p1 = Location.create(-10.0, -10.0); Location p2 = Location.create(-10.0, 10.0); Location p3 = Location.create(10.0, 10.0); Location p4 = Location.create(10.0, -10.0); LocationList locs = LocationList.create(p1, p2, p3, p4); Bounds b = bounds(locs); Location min = Location.create(-10.0, -10.0); Location max = Location.create(10.0, 10.0); assertEquals(min, b.min()); assertEquals(max, b.max()); } @Test(expected = IllegalArgumentException.class) public void testBoundsIAE() { List<Location> locs = Lists.newArrayList(); bounds(locs); } /** * DEVELOPER NOTE * * Test value generation along with various speed comparisons provided below. * Speed tests can be run with fixed or randomized values; randomization * generally only adds a set amount of time to each test. * * Internal methods marked with *OLD were removed intact from Locations to * preserve history and preserve ability to document performance enhancements. */ public static void main(String[] args) { // shared convenience fields Location L1, L2, L3, L4, L5, L6; int numIter = 1000000; // flag for using fixed (vs random) values in speed tests boolean fixedVals = true; // ========================================================== // VALUE GENERATION // ========================================================== L1 = Location.create(32.6, 20.4); L2 = Location.create(32.4, 20); L3 = Location.create(32.2, 20.6); L4 = Location.create(32, 20.2, 10); L5 = Location.create(90, 0); L6 = Location.create(-90, 0); // vd sd fsd angle az-rad az-deg // d51 6393.578 km 6382.596 6474.888 1.001818991 3.141592654 180.0 // d25 6415.757 km 6404.835 6493.824 1.005309649 0.0 0.0 // d46 13543.818 km 13565.796 13707.303 2.129301687 3.141592654 180.0 // d63 13565.996 km 13588.035 13735.216 2.132792346 0.0 0.0 // d12 43.645957 km 43.6090311 43.6090864 0.006844919 4.179125015 239.44623 // d13 48.183337 km 48.2790582 48.2790921 0.007577932 2.741190313 157.05864 // d14 69.150258 km 69.3145862 69.3146382 0.010879690 3.417161139 195.78891 // d23 60.706703 km 60.6198752 60.6200022 0.009514959 1.943625801 111.36156 // d42 48.198212 km 48.2952067 48.2952403 0.007580467 5.883856933 337.12017 // d43 43.787840 km 43.7518411 43.7518956 0.006867335 1.035735858 59.34329 // fdtl dtl // d321 34.472999888 34.425229936 // d231 34.472999888 -34.425229936 // d432 47.859144611 -47.851004687 // d413 30.170948729 30.205855981 Location p1 = L1; Location p2 = L2; Location p3 = L3; System.out.println(horzDistance(p1, p2)); System.out.println(horzDistanceFast(p1, p2)); System.out.println(angle(p1, p2)); System.out.println(azimuthRad(p1, p2)); System.out.println(azimuth(p1, p2)); System.out.println(distanceToLineFast(p1, p2, p3)); System.out.println(distanceToLine(p1, p2, p3)); // ========================================================== // Distance to Line Methods // // Summary: the highly accurate Haversine based formula is // much slower (up to 20x), but does not work // accross dateline and does not indicate // sidedness. // 1M repeat runs showed the following comp // times for fixed location pairs: // // DTL distanceToLine() 1600 ms // DTLFo distanceToLineFastOLD() 120 ms // DTLF distanceToLineFast() 1 ms // DTS distanceToSegment() 1 ms // DTSF distanceToSegmentFast() 1 ms // // // ========================================================== L2 = Location.create(32, -116); L1 = Location.create(37, -115); L3 = Location.create(34, -114); System.out.println("\nSPEED TEST -- Distance to Line\n"); System.out.println("distanceToLine(): " + distanceToLine(L1, L2, L3)); for (int i = 0; i < 5; i++) { long T = System.currentTimeMillis(); double d; for (int j = 0; j < numIter; j++) { d = (fixedVals) ? distanceToLine(L1, L2, L3) : distanceToLine(randomLoc(), randomLoc(), randomLoc()); } T = (System.currentTimeMillis() - T); System.out.println(" DTL: " + T); } System.out.println("distToLineFastOLD(): " + distanceToLineFastOLD(L1, L2, L3)); for (int i = 0; i < 5; i++) { long T = System.currentTimeMillis(); double d; for (int j = 0; j < numIter; j++) { d = (fixedVals) ? distanceToLineFastOLD(L1, L2, L3) : distanceToLineFastOLD( randomLoc(), randomLoc(), randomLoc()); } T = (System.currentTimeMillis() - T); System.out.println(" DTLFo: " + T); } System.out.println("distanceToLineFast(): " + distanceToLineFast(L1, L2, L3)); for (int i = 0; i < 5; i++) { long T = System.currentTimeMillis(); double d; for (int j = 0; j < numIter; j++) { d = (fixedVals) ? distanceToLineFast(L1, L2, L3) : distanceToLineFast(randomLoc(), randomLoc(), randomLoc()); } T = (System.currentTimeMillis() - T); System.out.println(" DTLF: " + T); } System.out.println("distanceToSegment(): " + distanceToSegment(L1, L2, L3)); for (int i = 0; i < 5; i++) { long T = System.currentTimeMillis(); double d; for (int j = 0; j < numIter; j++) { d = (fixedVals) ? distanceToSegment(L1, L2, L3) : distanceToSegment(randomLoc(), randomLoc(), randomLoc()); } T = (System.currentTimeMillis() - T); System.out.println(" DTS: " + T); } System.out.println("distanceToSegmentFast(): " + distanceToSegmentFast(L1, L2, L3)); for (int i = 0; i < 5; i++) { long T = System.currentTimeMillis(); double d; for (int j = 0; j < numIter; j++) { d = (fixedVals) ? distanceToSegmentFast(L1, L2, L3) : distanceToSegmentFast(randomLoc(), randomLoc(), randomLoc()); } T = (System.currentTimeMillis() - T); System.out.println(" DTSF: " + T); } // // ========================================================== // // Horizontal (Surface) Distance Methods // // // // Summary: Accurate, Haversine based methods of distance // // calculation have beeen shown to be much faster // // than existing methods (e.g. getHorzDistance). // // 1M repeat runs showed the following comp // // times for fixed location pairs: // // // // HDo getHorizDistanceOLD() 1285 ms // // AHDo getApproxHorzDistanceOLD() 955 ms // // HD horzDistance() 230 ms // // HDF horzDistanceFast() 1 ms // // ========================================================== // // // long pair ~9K km : discrepancies > 100km // // L1 = new Location(20,-10); // // L2 = new Location(-20,60); // // // mid pair ~250 km : discrepancies in 10s of meters // // L1 = new Location(32.1,-117.2); // // L2 = new Location(33.8, -115.4); // // // short pair : negligible discrepancy in values // L1 = new Location(32.132,-117.21); // L2 = new Location(32.306, -117.105); // // System.out.println("\nSPEED TEST -- Horizontal Distance\n"); // System.out.println("getHorzDistanceOLD(): " + // getHorzDistanceOLD(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // double d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // getHorzDistanceOLD(L1, L2) : // getHorzDistanceOLD(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" HDo: " + T); // } // // System.out.println("getApproxHorzDistanceOLD(): " + // getApproxHorzDistanceOLD(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // double d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // getApproxHorzDistanceOLD(L1, L2) : // getApproxHorzDistanceOLD(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" ADo: " + T); // } // // System.out.println("horzDistance(): " + // horzDistance(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // double d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // horzDistance(L1, L2) : // horzDistance(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" HD: " + T); // } // // System.out.println("horzDistanceFast(): " + // horzDistanceFast(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // double d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // horzDistanceFast(L1, L2) : // horzDistanceFast(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" HDF: " + T); // } // // // // // // ========================================================== // // Linear Distance Methods // // // // Summary: Accurate, Haversine based methods of distance // // calculation have beeen shown to be much faster // // than existing methods (e.g. getHorzDistance). // // 1M repeat runs showed the following comp // // times for fixed location pairs: // // // // TDo getTotalDistanceOLD() 1300 ms // // LD linearDistance() 240 ms // // LDF linearDistanceFast() 1 ms // // ========================================================== // // // mid pair ~250 km : discrepancies in 10s of meters // L1 = new Location(32.1,-117.2); // L2 = new Location(33.8, -115.4); // // // short pair : negligible discrepancy in values // // L1 = new Location(32.132,-117.21); // // L2 = new Location(32.306, -117.105); // // System.out.println("\nSPEED TEST -- Linear Distance\n"); // System.out.println("getTotalDistanceOLD(): " + // getTotalDistanceOLD(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // double d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // getTotalDistanceOLD(L1, L2) : // getTotalDistanceOLD(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" TDo: " + T); // } // // System.out.println("linearDistance(): " + // linearDistance(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // double d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // linearDistance(L1, L2) : // linearDistance(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" LD: " + T); // } // // System.out.println("linearDistanceFast(): " + // linearDistanceFast(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // double d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // linearDistanceFast(L1, L2) : // linearDistanceFast(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" LDF: " + T); // } // // // // // ========================================================== // // Azimuth Methods // // // // Summary: New, spherical geometry azimuth methods are // // faster than existing methods. // // 1M repeat runs showed the following comp // // times for fixed location pairs: // // // // gAo getAzimuthOLD() 1240 ms // // A azimuth() 348 ms // // ========================================================== // // L1 = new Location(32, -117); // L2 = new Location(33, -115); // // System.out.println("\nSPEED TEST -- Azimuth\n"); // System.out.println("getAzimuthOLD(): " + // getAzimuthOLD(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // double d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // getAzimuthOLD(L1, L2) : // getAzimuthOLD(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" gAo: " + T); // } // // System.out.println("azimuth(): " + // azimuth(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // double d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // azimuth(L1, L2) : // azimuth(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" A: " + T); // } // // // // // // ========================================================== // // Vector Methods // // // // Summary: New, spherical geometry direction methods are // // faster than existing methods. A test using // // horzDistanceFast instead of horzDistance // // realized no speed gain. // // 1M repeat runs showed the following comp // // times for fixed location pairs: // // // // gDo getDirectionOLD() 3700 ms // // V vector() 610 ms // // ========================================================== // // L1 = new Location(32, -117); // L2 = new Location(33, -115); // // System.out.println("\nSPEED TEST -- LocationVector\n"); // System.out.println("getDirectionOLD(): " + getDirectionOLD(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // LocationVector d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // getDirectionOLD(L1, L2) : // getDirectionOLD(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" gDo: " + T); // } // // System.out.println("vector(): " + vector(L1, L2)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // LocationVector d; // for (int j=0; j<numIter; j++) { // d = (fixedVals) ? // vector(L1, L2) : // vector(randomLoc(),randomLoc()); // } // T = (System.currentTimeMillis() - T); // System.out.println(" V: " + T); // } // // // // // ========================================================== // // Location Methods // // // // Summary: New, spherical geometry direction methods are // // slightly faster than existing methods. // // 1M repeat runs showed the following comp // // times for fixed location pairs: // // // // gLo getLocationOLD() 915 ms // // L location() 670 ms // // ========================================================== // // L1 = new Location(32, -117); // L2 = new Location(33, -115); // LocationVector dir = new LocationVector(20,111,10); // System.out.println("\nSPEED TEST -- Location\n"); // System.out.println("getLocationOLD(): " + getLocationOLD(L1, dir)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // Location loc; // for (int j=0; j<numIter; j++) { // loc = (fixedVals) ? // getLocationOLD(L1, dir) : // getLocationOLD(randomLoc(),dir); // } // T = (System.currentTimeMillis() - T); // System.out.println(" gL: " + T); // } // // System.out.println("location(): " + location(L1, dir)); // for (int i=0; i < 5; i++) { // long T = System.currentTimeMillis(); // Location loc; // for (int j=0; j<numIter; j++) { // loc = (fixedVals) ? // location(L1, dir) : // location(randomLoc(),dir); // } // T = (System.currentTimeMillis() - T); // System.out.println(" L: " + T); // } // // // // // ========================================================== // // The following code may be used to explore how old and // // new distance caclulation methods compare and how // // results cary with distance // // ========================================================== // // // commented values are accurate distances computed // // using the Vincenty formula // // Location L1a = new Location(20,-10); // 8818.496 km // Location L1b = new Location(-20,60); // // Location L2a = new Location(90,10); // 4461.118 km // Location L2b = new Location(50,80); // // Location L3a = new Location(-80,-30); // 3824.063 km // Location L3b = new Location(-50,20); // // Location L4a = new Location(-42,178); // 560.148 km // Location L4b = new Location(-38,-178); // // Location L5a = new Location(5,-90); // 784.028 km // Location L5b = new Location(0,-85); // // Location L6a = new Location(70,-40); // 1148.942 km // Location L6b = new Location(80,-50); // // Location L7a = new Location(-30,80); // 1497.148 km // Location L7b = new Location(-20,90); // // Location L8a = new Location(70,70); // 234.662 km // Location L8b = new Location(72,72); // // Location L9a = new Location(-20,120); // 305.532 km // Location L9b = new Location(-18,122); // // // LocationList llL1 = createLocList(L1a,L1b,0.2); // // LocationList llL2 = createLocList(L2a,L2b,0.2); // // LocationList llL3 = createLocList(L3a,L3b,0.2); // // LocationList llL4 = createLocList(L4a,L4b,356); // spans prime // meridian // LocationList llL5 = createLocList(L5a,L5b,0.05); // // LocationList llL6 = createLocList(L6a,L6b,0.05); // // LocationList llL7 = createLocList(L7a,L7b,0.05); // // LocationList llL8 = createLocList(L8a,L8b,0.001); // // LocationList llL9 = createLocList(L9a,L9b,0.001); // // LocationList LLtoUse = llL5; // Location startPt = LLtoUse.get(0); // for (int i = 1; i < LLtoUse.size(); i++) { // Location endPt = LLtoUse.get(i); // double surfDist = horzDistance(startPt, endPt); // double fastSurfDist = horzDistanceFast(startPt, endPt); // double delta1 = fastSurfDist - surfDist; // double horizDist = getHorzDistanceOLD(startPt, endPt); // double approxDist = getApproxHorzDistanceOLD(startPt, endPt); // double delta2 = approxDist - horizDist; // double delta3 = fastSurfDist - approxDist; // String s = String.format( // "sd: %03.4f sdf: %03.4f d: %03.4f " + // "hdO: %03.4f adO: %03.4f d: %03.4f Df: %03.4f", // surfDist, fastSurfDist, delta1, // horizDist, approxDist, delta2, delta3); // System.out.println(s); // } // } // utility method to create a locationlist between two points; points // are discretized in longitude using 'lonInterval'; latitude intervals // are whatever they need to be to get to L2 // // this is used in 'main' when exploring variations between distance // calculators private static LocationList createLocList( Location L1, Location L2, double lonInterval) { int numPoints = (int) Math.floor(Math.abs( L2.lon() - L1.lon()) / lonInterval); double dLat = (L2.lat() - L1.lat()) / numPoints; double dLon = (L1.lon() - L2.lon() < 0) ? lonInterval : -lonInterval; LocationList.Builder llb = LocationList.builder(); double lat = L1.lat(); double lon = L1.lon(); for (int i = 0; i <= numPoints; i++) { // System.out.println(lat + " " + lon); llb.add(Location.create(lat, lon)); lat += dLat; lon += dLon; } return llb.build(); } // utility method to generate random locations within +/- 40deg lat and // +/- 40 deg lon private static Random rand = new Random(); private static Location randomLoc() { return Location.create(randLatLon(), randLatLon()); } private static double randLatLon() { return (rand.nextDouble() * 80) - 40; } // ========================================================== // ARCHIVED METHODS // ========================================================== /** Earth radius constant */ private final static int R = 6367; /** Radians to degrees conversion constant */ private final static double RADIANS_CONVERSION = Math.PI / 180; /** Degree to Km conversion at equator */ private final static double D_COEFF = 111.11; /** * OLD METHOD */ private static double getHorzDistanceOLD(Location loc1, Location loc2) { return getHorzDistanceOLD( loc1.lat(), loc1.lon(), loc2.lat(), loc2.lon()); } /** * OLD METHOD * * Second way to calculate the distance between two points. Obtained off the * internet, but forgot where now. When used in comparision with the * latLonDistance function you see they give practically the same values at * the equator, and only start to diverge near the poles, but still reasonable * close to each other. Good for point of comparision. */ private static double getHorzDistanceOLD( double lat1, double lon1, double lat2, double lon2) { double xlat = lat1 * RADIANS_CONVERSION; double xlon = lon1 * RADIANS_CONVERSION; double st0 = Math.cos(xlat); double ct0 = Math.sin(xlat); double phi0 = xlon; xlat = lat2 * RADIANS_CONVERSION; xlon = lon2 * RADIANS_CONVERSION; double st1 = Math.cos(xlat); double ct1 = Math.sin(xlat); double sdlon = Math.sin(xlon - phi0); double cdlon = Math.cos(xlon - phi0); double cdelt = (st0 * st1 * cdlon) + (ct0 * ct1); double x = (st0 * ct1) - (st1 * ct0 * cdlon); double y = st1 * sdlon; double sdelt = Math.pow(((x * x) + (y * y)), .5); double delta = Math.atan2(sdelt, cdelt) / RADIANS_CONVERSION; delta = delta * D_COEFF; return delta; } /** * OLD METHOD */ private static double getApproxHorzDistanceOLD( Location loc1, Location loc2) { return getApproxHorzDistanceOLD( loc1.lat(), loc1.lon(), loc2.lat(), loc2.lon()); } /** * OLD METHOD * * This computes the approximate horizontal distance (in km) using the * standard cartesian coordinate transformation. Not implemented correctly is * lons straddle 360 or 0 degrees! */ private static double getApproxHorzDistanceOLD( double lat1, double lon1, double lat2, double lon2) { double d1 = (lat1 - lat2) * 111.111; double d2 = (lon1 - lon2) * 111.111 * Math.cos(((lat1 + lat2) / (2)) * Math.PI / 180); return Math.sqrt(d1 * d1 + d2 * d2); } /** * OLD METHOD * * Helper method that calculates the angle between two locations on the * earth.<p> * * @param loc1 location of first point * @param loc2 location of second point * @return angle between the two locations */ private static double getAzimuthOLD(Location loc1, Location loc2) { return getAzimuthOLD(loc1.lat(), loc1.lon(), loc2.lat(), loc2.lon()); } /** * OLD METHOD * * Helper method that calculates the angle between two locations (value * returned is between -180 and 180 degrees) on the earth.<p> * * @param lat1 latitude of first point * @param lon1 longitude of first point * @param lat2 latitude of second point * @param lon2 longitude of second point * @return angle between the two lat/lon locations */ private static double getAzimuthOLD( double lat1, double lon1, double lat2, double lon2) { double xlat = lat1 * RADIANS_CONVERSION; double xlon = lon1 * RADIANS_CONVERSION; double st0 = Math.cos(xlat); double ct0 = Math.sin(xlat); double phi0 = xlon; xlat = lat2 * RADIANS_CONVERSION; xlon = lon2 * RADIANS_CONVERSION; double st1 = Math.cos(xlat); double ct1 = Math.sin(xlat); double sdlon = Math.sin(xlon - phi0); double cdlon = Math.cos(xlon - phi0); double x = (st0 * ct1) - (st1 * ct0 * cdlon); double y = st1 * sdlon; double az = Math.atan2(y, x) / RADIANS_CONVERSION; return az; } /** * OLD METHOD * * Helper method that calculates the angle between two locations on the * earth.<p> * * Note: SWR: I'm not quite sure of the difference between azimuth and back * azimuth. Ned, you will have to fill in the details. * * @param lat1 latitude of first point * @param lon1 longitude of first point * @param lat2 latitude of second point * @param lon2 longitude of second point * @return angle between the two lat/lon locations */ private static double getBackAzimuthOLD( double lat1, double lon1, double lat2, double lon2) { double xlat = lat1 * RADIANS_CONVERSION; double xlon = lon1 * RADIANS_CONVERSION; double st0 = Math.cos(xlat); double ct0 = Math.sin(xlat); double phi0 = xlon; xlat = lat2 * RADIANS_CONVERSION; xlon = lon2 * RADIANS_CONVERSION; double st1 = Math.cos(xlat); double ct1 = Math.sin(xlat); double sdlon = Math.sin(xlon - phi0); double cdlon = Math.cos(xlon - phi0); double x = (st1 * ct0) - (st0 * ct1 * cdlon); double y = -sdlon * st0; double baz = Math.atan2(y, x) / RADIANS_CONVERSION; return baz; } /** * OLD METHOD * * This computes the total distance in km. */ private static double getTotalDistanceOLD(Location loc1, Location loc2) { double hDist = getHorzDistanceOLD(loc1, loc2); double vDist = vertDistance(loc1, loc2); return Math.sqrt(hDist * hDist + vDist * vDist); } /** * OLD METHOD * * Given a Location and a LocationVector object, this function calculates a * second Location the LocationVector points to (only the azimuth is used; * backAzimuth is ignored). The fields calculated for the second Location are: * * <uL> <li>Lat <li>Lon <li>Depth </ul> * * @param location1 First geographic location * @param direction LocationVector object pointing to second Location * @return location2 The second location * @exception UnsupportedOperationException Thrown if the Location or * LocationVector contain bad data such as invalid latitudes * @see Location to see the field definitions */ private static Location getLocationOLD( Location location, LocationVector direction) throws UnsupportedOperationException { double lat1 = location.lat(); double lon1 = location.lon(); double depth = location.depth(); double azimuth = direction.azimuth(); double horzDistance = direction.horizontal(); double vertDistance = direction.vertical(); double newLat = getLatitudeOLD(horzDistance, azimuth, lat1, lon1); double newLon = getLongitudeOLD(horzDistance, azimuth, lat1, lon1); // double newDepth = depth + -1*vertDistance; double newDepth = depth + vertDistance; Location newLoc = Location.create(newLat, newLon, newDepth); return newLoc; } /** * OLD METHOD * * By passing in two Locations this calculator will determine the Distance * object between them. The four fields calculated are: * * <uL> <li>horzDistance <li>azimuth <li>backAzimuth <li>vertDistance </ul> * * @param location1 First geographic location * @param location2 Second geographic location * @return The direction, decomposition of the vector between two locations * @exception UnsupportedOperationException Thrown if the Locations contain * bad data such as invalid latitudes * @see Distance to see the field definitions */ private static LocationVector getDirectionOLD( Location location1, Location location2) throws UnsupportedOperationException { double lat1 = location1.lat(); double lon1 = location1.lon(); double lat2 = location2.lat(); double lon2 = location2.lon(); double horzDistance = getHorzDistanceOLD(location1, location2); double azimuth = getAzimuthOLD(location1, location2); double vertDistance = location2.depth() - location1.depth(); return LocationVector.create(azimuth, horzDistance, vertDistance); } /** * OLD METHOD * * Internal helper method that calculates the latitude of a second location * given the input location and direction components * * @param delta Horizontal distance * @param azimuth angle towards new point * @param lat latitude of original point * @param lon longitude of original point * @return latitude of new point */ private static double getLatitudeOLD( double delta, double azimuth, double lat, double lon) { delta = (delta / D_COEFF) * RADIANS_CONVERSION; double sdelt = Math.sin(delta); double cdelt = Math.cos(delta); double xlat = lat * RADIANS_CONVERSION; // double xlon = lon * RADIANS_CONVERSION; double az2 = azimuth * RADIANS_CONVERSION; double st0 = Math.cos(xlat); double ct0 = Math.sin(xlat); // double phi0 = xlon; double cz0 = Math.cos(az2); double ct1 = (st0 * sdelt * cz0) + (ct0 * cdelt); double x = (st0 * cdelt) - (ct0 * sdelt * cz0); double y = sdelt * Math.sin(az2); double st1 = Math.pow(((x * x) + (y * y)), .5); // double dlon = Math.atan2( y, x ); double newLat = Math.atan2(ct1, st1) / RADIANS_CONVERSION; return newLat; } /** * OLD METHOD * * Internal helper method that calculates the longitude of a second location * given the input location and direction components * * @param delta Horizontal distance * @param azimuth angle towards new point * @param lat latitude of original point * @param lon longitude of original point * @return longitude of new point */ private static double getLongitudeOLD( double delta, double azimuth, double lat, double lon) { delta = (delta / D_COEFF) * RADIANS_CONVERSION; double sdelt = Math.sin(delta); double cdelt = Math.cos(delta); double xlat = lat * RADIANS_CONVERSION; double xlon = lon * RADIANS_CONVERSION; double az2 = azimuth * RADIANS_CONVERSION; double st0 = Math.cos(xlat); double ct0 = Math.sin(xlat); double phi0 = xlon; double cz0 = Math.cos(az2); // double ct1 = ( st0 * sdelt * cz0 ) + ( ct0 * cdelt ); double x = (st0 * cdelt) - (ct0 * sdelt * cz0); double y = sdelt * Math.sin(az2); // double st1 = Math.pow( ( ( x * x ) + ( y * y ) ), .5 ); double dlon = Math.atan2(y, x); double newLon = (phi0 + dlon) / RADIANS_CONVERSION; return newLon; } /** * * OLD METHOD - although fast, curent implementation is faster and not nearly * as complicated * * Computes the shortest distance between a point and a line. Both the line * and point are assumed to be at the earth's surface; the depth component of * each <code>Location</code> is ignored. This is a fast, geometric, cartesion * (flat-earth approximation) solution in which longitude is scaled by the * cosine of latitude; it is only appropriate for use over short distances * (e.g. <200 km).<br/> <br/> <b>Note:</b> This method does <i>NOT</i> * support values spanning ±180° and results for such input values * are not guaranteed. * * @param p1 the first <code>Location</code> point on the line * @param p2 the second <code>Location</code> point on the line * @param p3 the <code>Location</code> point for which distance will be * calculated * @return the shortest distance in km between the supplied point and line * @see #distanceToLine(Location, Location, Location) */ private static double distanceToLineFastOLD( Location p1, Location p2, Location p3) { double lat1 = p1.latRad(); double lat2 = p2.latRad(); double lat3 = p3.latRad(); double lon1 = p1.lonRad(); double lon2 = p2.lonRad(); double lon3 = p3.lonRad(); // use average latitude to scale longitude double lonScale = Math.cos(0.5 * lat3 + 0.25 * lat1 + 0.25 * lat2); // line-point corrdinates w/ loc transformed to the origin double x1 = (lon1 - lon3) * lonScale; double x2 = (lon2 - lon3) * lonScale; double y1 = lat1 - lat3; double y2 = lat2 - lat3; double dist; // check for values very close to zero if (Math.abs(x1 - x2) > 1e-8) { double m = (y2 - y1) / (x2 - x1); // slope double b = y2 - m * x2; // intercept double xT = -m * b / (1 + m * m); // x target double yT = m * xT + b; // y target // make sure the target point is in between the two endpoints boolean betweenPts = false; if (x2 > x1) { if (xT <= x2 && xT >= x1) { betweenPts = true; } } else { if (xT <= x1 && xT >= x2) { betweenPts = true; } } if (betweenPts) { dist = Math.sqrt(xT * xT + yT * yT); } else { double d1 = Math.sqrt(x1 * x1 + y1 * y1); double d2 = Math.sqrt(x2 * x2 + y2 * y2); dist = Math.min(d1, d2); } } else { // the x1 = x2 case if (y2 > y1) { if (y2 <= 0.0) { dist = Math.sqrt(x2 * x2 + y2 * y2); } else if (y1 >= 0) { dist = Math.sqrt(x1 * x1 + y1 * y1); } else { dist = Math.abs(x1); } } else { // (y1 > y2) if (y1 <= 0.0) { dist = Math.sqrt(x1 * x1 + y1 * y1); } else if (y2 >= 0) { dist = Math.sqrt(x2 * x2 + y2 * y2); } else { dist = Math.abs(x1); } } } return dist * EARTH_RADIUS_MEAN; } }