0

I need a function that returns a longitude value given a lat/lon coordinate, a distance in miles, and an intersecting latitude. To do that I need to use Halversine, like discussed here: https://stackoverflow.com/a/7179026/78202. I realize that there will be two longitudes that intersect a given latitude a given distance from another ordered pair, I'd just like to get the point where I have a function that correctly returns one of them and I'll decide how to break the tie then.

I casually solved Holversine for lon1, and here's what I have. This is partly a math question, partly a programming question - what is wrong with this? There's no syntax error, I'm just not getting what I expect (see below).

function toRad(Value) {
    /** Converts numeric degrees to radians */
    return Value * Math.PI / 180;
}

/** returns the longitude a certain number of miles from another point given a latitude. **/
function getLon(miles, lat1, lat2, lon2) {
  // see http://www.movable-type.co.uk/scripts/latlong.html
  //Radius of the earth in:  1.609344 miles,  6371 km  | var R = (6371 / 1.609344);
    var R = 3958.7558657440545; // Radius of earth in Miles 
    miles = (typeof miles === "undefined") ? 1.46628357399041 : miles;
    lat1 = (typeof lat1 === "undefined") ? 42.34769 : lat1;
    lat2 = (typeof lat2 === "undefined") ? 42.367137 : lat2;
    lon2 = (typeof lon2 === "undefined") ? -71.124383 : lon2;
    var dLat =   toRad( lat2-lat1 );
    var sinInsideN1 = Math.sin(dLat);
    var sinInsideN2 = Math.sin(miles/2*R);
    var n1 = Math.pow(sinInsideN1,2);
    var n2 = Math.pow(sinInsideN2,2);
    var d1 = Math.cos(lat1)*Math.cos(lat2);
    var inArcsin = Math.sqrt((n2-n1)/d1);
    var translation = inArcsin-Math.floor(inArcsin);
    var ret = -(lat1+2*Math.asin(translation))
    return ret; // should be 42.34769
}

I'm getting 42.242513701215, which forms a coordinate with 42.34769 that is 8.63065661614176 mi from (42.367137,-71.124383), not 1.46628357399041 mi as expected.

Community
  • 1
  • 1
Myer
  • 3,670
  • 2
  • 39
  • 51
  • 1
    "I realize that there will be two longitudes that intersect a given latitude a given distance from another ordered pair" - there can be two, one or zero :) – Jens Schwarzer Mar 05 '12 at 14:22

2 Answers2

1

I found an C-implementation of Haversine here http://code.google.com/p/siklon/source/browse/trunk/source/Haversine.c?r=11, which I have then rewritten wrt lon1:

#include <math.h>

/*Earth Radius in Kilometers.*/
/* static const double R = 6372.797560856; */
/*Earth Radius in Miles.*/
static const double R = 3958.7558657440545;
/*Degree vs. Radian conservation variables*/
static const double DEG_TO_RAD = M_PI/180.0;
static const double RAD_TO_DEG = 180.0/M_PI;

double Haversine_Distance(double lat1,double lon1, double lat2, double lon2)
    {
    double dlon = (lon2 - lon1) * DEG_TO_RAD;
    double dlat = (lat2 - lat1) * DEG_TO_RAD;
    double a = pow(sin(dlat * 0.5),2) + cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD) * pow(sin(dlon * 0.5),2);
    double c = 2.0 * atan2(sqrt(a), sqrt(1-a));
    return R * c;
    }

double inverseHaversine_Distance_lon1(double lat1, double dist, double lat2, double lon2)
    {
    /* Rewrite Haversine_Distance wrt lon1: */
    /* dist = R * c = R * 2.0 * atan2(sqrt(a), sqrt(1-a)) */
    /* dist / R / 2.0 = atan2(sqrt(a), sqrt(1-a)) */
    /* sqrt(a) = sin(dist / R / 2.0); sqrt(1-a) = cos(dist / R / 2.0) */
    /* a = (sin(dist / R / 2.0))^2; 1 - a = (cos(dist / R / 2.0))^2 */
    /* pow(sin(dlat * 0.5),2) + cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD) * pow(sin(dlon * 0.5),2) = (sin(dist / R / 2.0))^2 */
    /* cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD) * pow(sin(dlon * 0.5),2) = (sin(dist / R / 2.0))^2 - pow(sin(dlat * 0.5),2) */
    /* pow(sin(dlon * 0.5),2) = (pow(sin(dist / R / 2.0), 2) - pow(sin(dlat * 0.5), 2)) / (cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD))  */
    /* sin(dlon * 0.5) = sqrt((pow(sin(dist / R / 2.0), 2) - pow(sin(dlat * 0.5), 2)) / (cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD))) */
    /* dlon = (lon2 - lon1) * DEG_TO_RAD = asin(sqrt((pow(sin(dist / R / 2.0), 2) - pow(sin(dlat * 0.5), 2)) / (cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD)))) * 2.0 */
    /* lon2 - lon1 = asin(sqrt((pow(sin(dist / R / 2.0), 2) - pow(sin(dlat * 0.5), 2)) / (cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD)))) * 2.0 / DEG_TO_RAD*/
    /* lon1 = lon2 - asin(sqrt((pow(sin(dist / R / 2.0), 2) - pow(sin(dlat * 0.5), 2)) / (cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD)))) * 2.0 / DEG_TO_RAD*/
    double dlat = (lat2 - lat1) * DEG_TO_RAD;
    return lon2 - asin(sqrt((pow(sin(dist / R / 2.0), 2) - pow(sin(dlat * 0.5), 2)) / (cos(lat1*DEG_TO_RAD) * cos(lat2*DEG_TO_RAD)))) * 2.0 * RAD_TO_DEG;
    }

int main()
    {
    double lat1 = 42.34769;
    double dist = 1.46628357399041;
    double lat2 = 42.367137;
    double lon2 = -71.124383;
    double lon1 = inverseHaversine_Distance_lon1(lat1, dist, lat2, lon2);

    printf("lon1 %f\n", lon1);
    printf("dist %f\n", Haversine_Distance(lat1, lon1, lat2, lon2));
    }

The result:
gcc inverse_haversine.c -lm
./a.out
lon1 -71.135880
dist 1.466284

It may be possible to reduce the expression...

Jens Schwarzer
  • 2,840
  • 1
  • 22
  • 35
0

At least lat1, lat2 and lon2 has to be converted into radians before calling the trigonometric functions! But maybe there are more problems... :)

Example: Using the simple version I got this code i C:

#include <math.h>

#define METERS_PER_DEGREE_EQUATOR 111319.5
#define MILES_PER_DEGREE_EQUATOR (METERS_PER_DEGREE_EQUATOR / 1000.0 / 1.609344)

/* Select preferred unit: */
#define UNITS_PER_DEGREE_EQUATOR MILES_PER_DEGREE_EQUATOR

double horDist(double lat1, double lon1, double lat2, double lon2)
    {
    /* From "Note on conversion from decimal degrees to meters"
     * (http://southport.jpl.nasa.gov/GRFM/cdrom/2a/DOCS/HTML/GEOLOC/METERS.HTM)
     * NOTE: BELOW IS ONLY PRECISE IF THE TWO LATITUDES ARE NOT TOO DISTANT! */
    double latDelta = UNITS_PER_DEGREE_EQUATOR * (lat1 - lat2);
    double lonDelta = UNITS_PER_DEGREE_EQUATOR * (lon1 - lon2) * cos(lat1 * M_PI / 180);
    return sqrt(latDelta * latDelta + lonDelta * lonDelta);
    }

double invHorDist_lon1(double lat1, double dist, double lat2, double lon2)
    {
    /* Rewrite horDist wrt lon1: */
    /* (dist * dist) = (latDelta * latDelta) + (lonDelta * lonDelta); */
    /* (dist * dist) - (latDelta * latDelta) = (lonDelta * lonDelta); */
    /* sqrt((dist * dist) - (latDelta * latDelta)) = lonDelta = UNITS_PER_DEGREE_EQUATOR * (lon1 - lon2) * cos(lat1 * M_PI / 180); */
    /* sqrt((dist * dist) - (latDelta * latDelta)) / UNITS_PER_DEGREE_EQUATOR / cos(lat1 * M_PI / 180) = (lon1 - lon2); */
    double latDelta = UNITS_PER_DEGREE_EQUATOR * (lat1 - lat2);
    return sqrt((dist * dist) - (latDelta * latDelta)) / UNITS_PER_DEGREE_EQUATOR / cos(lat1 * M_PI / 180) + lon2;
    }

int main()
    {
    double lon1 = invHorDist_lon1(42.34769, 1.46628357399041, 42.367137, -71.124383);
    printf("lon1 %f\n", lon1);
    printf("dist %f\n", horDist(42.34769, lon1, 42.367137, -71.124383));
    }

And the result is:
gcc haversine.c -lm
./a.out
lon1 -71.112968
dist 1.466284

But again this simple version does not fit if the two latitudes are too distant. But try rewrite Haversine again and convert to radians whenever you use trigonometric functions.

Jens Schwarzer
  • 2,840
  • 1
  • 22
  • 35
  • A simpler function than halversine can be found here http://southport.jpl.nasa.gov/GRFM/cdrom/2a/DOCS/HTML/GEOLOC/METERS.HTM. This can be used if the two latitudes are not too distant! – Jens Schwarzer Mar 05 '12 at 14:27