+ All Categories
Home > Documents > Calculate Distance Bearing - Movable Type Scripts

Calculate Distance Bearing - Movable Type Scripts

Date post: 03-Apr-2018
Category:
Upload: nuru-j-haule
View: 232 times
Download: 0 times
Share this document with a friend

of 24

Transcript
  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    1/24

    Movable Type Scripts

    Calculate distance, bearing and more between

    Latitude/Longitude points

    This page presents a variety of calculations for latitude/longitude points, with the formul

    and code fragments for implementing them.

    All these formul are for calculations on the basis of a spherical earth (ignoring ellipsoidal

    effects)which is accurate enough*for most purposes [In fact, the earth is very slightlyellipsoidal; using a spherical model gives errors typically up to 0.3%see notes for furtherdetails].

    Great-circle distance between two points

    Enter the co-ordinates into the text boxes to try out the calculations. A variety of formats areaccepted, principally:

    deg-min-sec suffixed with N/S/E/W (e.g. 404455N, 73 59 11W), or signed decimal degrees without compass direction, where negative indicates

    west/south (e.g. 40.7486, -73.9864):

    Point 1: 50 03 59N ,005 42 53

    Point 2: 58 38 38N ,003 04 12

    Distance: 968.9 kmInitial bearing: 0090711Final bearing: 0111631Midpoint: 542144N, 0043150W

    And you can see it on a map (arent those Google guys wonderful!)

    Distance

    This uses the haversine formula to calculate the great-circle distance between two points

    that is, the shortest distance over the earths surface giving an as-the-crow-flies distancebetween the points (ignoring any hills, of course!).

    http://www.movable-type.co.uk/http://www.movable-type.co.uk/scripts/latlong.html#ellipsoidhttp://www.movable-type.co.uk/scripts/latlong.html#ellipsoidhttp://www.movable-type.co.uk/scripts/latlong.html#ellipsoidhttp://www.movable-type.co.uk/
  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    2/24

    Haversine

    formula:

    a = sin(/2) + cos(1).cos(2).sin(/2)c = 2.atan2(a, (1a))d = R.c

    where is latitude, is longitude, R is earths radius (mean radius = 6,371km)

    note that angles need to be in radians to pass to trig functions!

    JavaScript:

    var R = 6371; // kmvar dLat = (lat2-lat1).toRad();var dLon = (lon2-lon1).toRad();var lat1 = lat1.toRad();var lat2 = lat2.toRad();

    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +Math.sin(dLon/2) * Math.sin(dLon/2) * Math.cos(lat1) *

    Math.cos(lat2);var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));var d = R * c;

    Thehaversineformula1remains particularly well-conditioned for numerical computationeven at small distances unlike calculations based on thespherical law of cosines. Theversed sine is 1-cos, and the half-versed-sine (1-cos)/2 = sin(/2) as used above. It was

    published by Roger Sinnott in Sky & Telescopemagazine in 1984 (Virtues of theHaversine), though known about for much longer by navigators. (For the curious, c is theangular distance in radians, and a is the square of half the chord length between the points). A

    (surprisingly marginal) performance improvement can be obtained, of course, by factoring

    out the terms which get squared.

    Spherical Law of Cosines

    In fact, when Sinnott published the haversine formula, computational precision was limited.

    Nowadays, JavaScript (and most modern computers & languages) use IEEE 754 64-bit

    floating-point numbers, which provide 15 significant figures of precision. With this precision,

    the simplespherical law of cosinesformula (cos c = cos a cos b + sin a sin b cos C) gives

    well-conditioned results down to distances as small as around 1 metre. (Note that the geodetic

    form of the law of cosines is rearranged from the canonical one so that the latitude can be

    used directly, rather than thecolatitude).

    This makes the simpler law of cosines a reasonable 1-line alternative to the haversine formula

    for many purposes. The choice may be driven by coding context, available trig functions (in

    different languages), etc.

    Spherical

    law of cosines:d = acos( sin(1).sin(2) + cos(1).cos(2).cos() ).R

    JavaScript:

    var R = 6371; // kmvar d = Math.acos(Math.sin(lat1)*Math.sin(lat2) +

    Math.cos(lat1)*Math.cos(lat2) *Math.cos(lon2-lon1)) * R;

    Excel: =ACOS(SIN(lat1)*SIN(lat2)+COS(lat1)*COS(lat2)*COS(lon2-lon1))*6371

    (Note that here and in all subsequent code fragments, for simplicity I do not show

    conversions from degrees to radians; see code below for complete versions).

    http://en.wikipedia.org/wiki/Haversine_formulahttp://en.wikipedia.org/wiki/Haversine_formulahttp://en.wikipedia.org/wiki/Haversine_formulahttp://mathforum.org/library/drmath/view/51879.htmlhttp://mathforum.org/library/drmath/view/51879.htmlhttp://mathworld.wolfram.com/SphericalTrigonometry.htmlhttp://mathworld.wolfram.com/SphericalTrigonometry.htmlhttp://mathworld.wolfram.com/SphericalTrigonometry.htmlhttp://mathworld.wolfram.com/Colatitude.htmlhttp://mathworld.wolfram.com/Colatitude.htmlhttp://mathworld.wolfram.com/Colatitude.htmlhttp://mathworld.wolfram.com/Colatitude.htmlhttp://mathworld.wolfram.com/SphericalTrigonometry.htmlhttp://mathforum.org/library/drmath/view/51879.htmlhttp://en.wikipedia.org/wiki/Haversine_formula
  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    3/24

    Equirectangular approximation

    If performance is an issue and accuracy less important, for small distancesPythagorastheoremcan be used on anequirectangular projection:*

    Formulax = .cos()y = d = R.x + y

    JavaScript:

    var x = (lon2-lon1) * Math.cos((lat1+lat2)/2);var y = (lat2-lat1);var d = Math.sqrt(x*x + y*y) * R;

    (lat/lon in radians!)

    This uses just one trig and one sqrt functionas against half-a-dozen trig functions for coslaw, and 7 trigs + 2 sqrts for haversine. Accuracy is somewhat complex: along meridians

    there are no errors, otherwise they depend on distance, bearing, and latitude, but are small

    enough for many purposes* (and often trivial compared with the spherical approximationitself).

    Bearing

    Baghdad to Osakanot a constant bearing!

    In general, your current heading will vary as you follow a great circle path (orthodrome); the

    final heading will differ from the initial heading by varying degrees according to distance and

    latitude (if you were to go from say 35N,45E (Baghdad) to 35N,135E (Osaka), you

    would start on a heading of 60 and end up on a heading of 120!).

    This formula is for the initial bearing (sometimes referred to as forward azimuth) which if

    followed in a straight line along a great-circle arc will take you from the start point to the end

    point:1

    Formula: = atan2( sin().cos(2), cos(1).sin(2) sin(1).cos(2).cos() )

    JavaScript:

    var y = Math.sin(dLon) * Math.cos(lat2);var x = Math.cos(lat1)*Math.sin(lat2) -

    Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);var brng = Math.atan2(y, x).toDeg();

    Excel:

    =ATAN2(COS(lat1)*SIN(lat2)-SIN(lat1)*COS(lat2)*COS(lon2-lon1),

    SIN(lon2-lon1)*COS(lat2))*note that Excel reverses the arguments to ATAN2see notes below

    http://en.wikipedia.org/wiki/Pythagorean_theoremhttp://en.wikipedia.org/wiki/Pythagorean_theoremhttp://en.wikipedia.org/wiki/Pythagorean_theoremhttp://en.wikipedia.org/wiki/Pythagorean_theoremhttp://en.wikipedia.org/wiki/Equirectangular_projectionhttp://en.wikipedia.org/wiki/Equirectangular_projectionhttp://en.wikipedia.org/wiki/Equirectangular_projectionhttp://mathforum.org/library/drmath/view/55417.htmlhttp://mathforum.org/library/drmath/view/55417.htmlhttp://mathforum.org/library/drmath/view/55417.htmlhttp://mathforum.org/library/drmath/view/55417.htmlhttp://en.wikipedia.org/wiki/Equirectangular_projectionhttp://en.wikipedia.org/wiki/Pythagorean_theoremhttp://en.wikipedia.org/wiki/Pythagorean_theorem
  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    4/24

    Since atan2 returns values in the range - ... + (that is, -180 ... +180), to normalise theresult to a compass bearing (in the range 0 ... 360, with ve values transformed into therange 180 ... 360), convert to degrees and then use (+360) % 360, where % is modulo.

    For final bearing, simply take the initialbearing from the endpoint to thestartpoint and

    reverse it (using = (+180) % 360).

    Midpoint

    This is the half-way point along a great circle path between the two points.1

    Formula:

    Bx= cos(2).cos()By= cos(2).sin()m= atan2( sin(1) + sin(2), ((cos(1)+Bx) + By) )m= 1 + atan2(By, cos(1)+Bx)

    JavaScript:

    var Bx = Math.cos(lat2) * Math.cos(dLon);var By = Math.cos(lat2) * Math.sin(dLon);var lat3 = Math.atan2(Math.sin(lat1)+Math.sin(lat2),

    Math.sqrt((Math.cos(lat1)+Bx)*(Math.cos(lat1)+Bx) + By*By ) );var lon3 = lon1 + Math.atan2(By, Math.cos(lat1) + Bx);

    Just as the initial bearing may vary from the final bearing, the midpoint may not be located

    half-way between latitudes/longitudes; the midpoint between 35N,45E and 35N,135E is

    around 45N,90E.

    Destination point given distance and bearing from start point

    Given a start point, initial bearing, and distance, this will calculate the destination point and

    final bearing travelling along a (shortest distance) great circle arc.

    Destination point along great-circle given distance and bearing from start point

    Start point: 5319'14?N,00143'47

    Bearing: 09601'18

    Distance: km

    Destination point: 531118N, 0000800EFinal bearing: 0973052view map

    Formula: 2= asin( sin(1)*cos(d/R) + cos(1)*sin(d/R)*cos() )2= 1+ atan2( sin()*sin(d/R)*cos(1), cos(d/R)sin(1)*sin(2) )

    where

    is latitude, is longitude, is the bearing (in radians, clockwise from north), d

    is the distance travelled, Ris the earths radius (d/Ris the angular distance, inradians)

    http://mathforum.org/library/drmath/view/51822.htmlhttp://mathforum.org/library/drmath/view/51822.htmlhttp://mathforum.org/library/drmath/view/51822.htmlhttp://mathforum.org/library/drmath/view/51822.html
  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    5/24

    JavaScript:

    var lat2 = Math.asin( Math.sin(lat1)*Math.cos(d/R) +Math.cos(lat1)*Math.sin(d/R)*Math.cos(brng) );

    var lon2 = lon1 +Math.atan2(Math.sin(brng)*Math.sin(d/R)*Math.cos(lat1),

    Math.cos(d/R)-Math.sin(lat1)*Math.sin(lat2));

    Excel:

    lat2: =ASIN(SIN(lat1)*COS(d/R) + COS(lat1)*SIN(d/R)*COS(brng))lon2: =lon1 + ATAN2(COS(d/R)-SIN(lat1)*SIN(lat2),

    SIN(brng)*SIN(d/R)*COS(lat1))

    * Remember that Excel reverses the arguments to ATAN2see notes below

    For final bearing, simply take the initialbearing from the endpoint to thestartpoint and

    reverse it (using = (+180) % 360).

    Intersection of two paths given start points and bearings

    This is a rather more complex calculation than most others on this page, but I've been asked

    for it a number of times. See below for the JavaScript.

    Intersection of two great-circle paths

    Point 1: 51.885 N ,0.235 E

    Brng 1:108.63

    Point 2: 49.008 N ,2.549 E

    Brng 2:32.72

    Intersection point: 505406N, 0042939E

    Formula:

    d12= 2.asin( (sin(/2) + cos(1).cos(2).sin(/2)) )1= acos( sin(2) sin(1).cos(d12) / sin(d12).cos(1) )2= acos( sin(1) sin(2).cos(d12) / sin(d12).cos(2) )

    if sin(21) > 012= 1, 21= 2. 2

    else

    12= 2. 1, 21= 2

    1= (1 12+ ) % 2. 2= (21 2+ ) % 2.

    3= acos( cos(1).cos(2) + sin(1).sin(2).cos(d12) )d13 = atan2( sin(d12).sin(1).sin(2), cos(2)+cos(1).cos(3) )3= asin( sin(1).cos(d13) + cos(1).sin(d13).cos(1) )13= atan2( sin(1).sin(d13).cos(1), cos(d13)sin(1).sin(3) )3= (1+13+) % 2.

    where

    1, 1, 1 : 1st point & bearing

    2, 2, 2 : 2nd point & bearing

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    6/24

    3, 3 : intersection point

    % = mod

    noteif sin(1)=0 and sin(2)=0: infinite solutionsif sin(1).sin(2) < 0: ambiguous solutionthis formulation is not always well-conditioned for meridional or equatorial lines

    Note this can also be solved using vectors rather than trigonometry:

    For each point , (lat=, lon=), we can define a unit vector pointing to it from thecentre of the earth: u{x,y,z} = [ coscos, cossin, sin ] (taking x=0, y=90,z=northnote that these formul depend on convention used for directions andhandedness)

    And for any great circle defined by two points, we can define a unit vector N normalto the plane of the circle: N(u1, u2) = (u1u2) / ||u1u2|| where is the vector cross

    product, and ||u|| the norm (length of the vector) The vector representing the intersection of the two great circles is then u i = N( N(u1,

    u2), N(u3, u4) )

    We can then get the latitude and longitude of Piby = atan2(uz, sqrt(ux + uy)), =atan2(uy, ux)

    The antipodal intersection point is (-, +)

    Cross-track distance

    Heres a new one: Ive sometimes been asked about distance of a point from a great-circlepath (sometimes called cross track error).

    Formula: dxt = asin(sin(d13/R)*sin(1312)) * R

    where

    d13 is distance from start point to third point

    13 is (initial) bearing from start point to third point

    12 is (initial) bearing from start point to end point

    R is the earths radius

    JavaScript: var dXt = Math.asin(Math.sin(d13/R)*Math.sin(brng13-brng12)) * R;

    Here, the great-circle path is identified by a start point and an end pointdepending on whatinitial data youre working from, you can use the formul above to obtain the relevantdistance and bearings. The sign of dxt tells you which side of the path the third point is on.

    The along-track distance, from the start point to the closest point on the path to the third

    point, is

    Formula: dat = acos(cos(d13/R)/cos(dxt/R)) * R

    where

    d13 is distance from start point to third point

    dxt is cross-track distance

    R is the earths radiusJavaScript: var dAt = Math.acos(Math.cos(d13/R)/Math.cos(dXt/R)) * R;

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    7/24

    Closest point to the poles

    And: Clairauts formula will give you the maximum latitude of a great circle path, given abearing and latitude on the great circle:

    Formula: max= acos(abs(sin()*cos()))JavaScript: var latMax = Math.acos(Math.abs(Math.sin(brng)*Math.cos(lat)));

    Rhumb lines

    A rhumb line (or loxodrome) is a path of constant bearing, which crosses all meridians atthe same angle.

    Sailors used to (and sometimes still) navigate along rhumb lines since it is easier to follow a

    constant compass bearing than to be continually adjusting the bearing, as is needed to follow

    a great circle. Rhumb lines are straight lines on a Mercator Projection map (also helpful for

    navigation).

    Rhumb lines are generally longer than great-circle (orthodrome) routes. For instance, London

    to New York is 4% longer along a rhumb line than along a great circleimportant foraviation fuel, but not particularly to sailing vessels. New York to Beijingclose to the mostextreme example possible (though not sailable!)is 30% longer along a rhumb line.

    Rhumb-line distance between two points

    Point 1: 50 21 50N ,004 09 25

    Point 2: 42 21 04N ,071 02 27

    Distance: 5196 km

    Bearing: 2600738

    Midpoint: 462127N, 0384939Wview mapDestination point along rhumb line given distance and bearing from start point

    Start point: 51 07 32N ,001 20 17

    Bearing: 11638'10

    Distance: km

    Destination point: 505748N, 0015109Eview map

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    8/24

    Distance/bearing

    These formul give the distance and (constant) bearing between two points.

    Formula: = ln( tan(/4+2/2)/tan(/4+1/2) )(the stretched latitudedifference)

    if E:W

    line,q = cos(1)

    otherwise, q = /d = ( + q.).R (pythagoras) = atan2(, )

    where is latitude, is longitude, is taking shortest route ( Math.PI) {

    dLon = dLon>0 ? -(2*Math.PI-dLon) : (2*Math.PI+dLon);}

    var d = Math.sqrt(dLat*dLat + q*q*dLon*dLon) * R;var brng = Math.atan2(dLon, dPhi);

    Destination

    Given a start point and a distance dalong constant bearing , this will calculate the

    destination point. If you maintain a constant bearing along a rhumb line, you will gradually

    spiral in towards one of the poles.

    Formula: = d/R (angular distance)2= 1+ .cos()

    = ln( tan(/4+2/2) / tan(/4+1/2) )(the stretched latitudedifference)

    if E:W line, q = cos(1)

    otherwise, q = / = .sin()/q2= (1++) % 2.

    where is latitude, is longitude, is taking shortest route (

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    9/24

    // check for some daft bugger going past the pole, normaliselatitude if soif (Math.abs(lat2) > Math.PI/2) lat2 = lat2>0 ? Math.PI-lat2 : -Math.PI-lat2;

    lon2 = (lon1+dLon+Math.PI)%(2*Math.PI) - Math.PI;

    Mid-point

    This formula for calculating the loxodromic midpoint, the point half-way along a rhumbline between two points, is due to Robert Hill and Clive Tooth1(thx Axel!).

    Formula: m= (1+2)/2f1= tan(/4+1/2)f2= tan(/4+2/2)fm= tan(/4+m/2)

    m= [ (21).ln(fm) + 1.ln(f2) 2.ln(f1) ] / ln(f2/f1)where is latitude, is longitude, ln is natural log

    JavaScript:

    if (Math.abs(lon2-lon1) > Math.PI) lon1 += 2*Math.PI; // crossinganti-meridian

    var lat3 = (lat1+lat2)/2;var f1 = Math.tan(Math.PI/4 + lat1/2);var f2 = Math.tan(Math.PI/4 + lat2/2);var f3 = Math.tan(Math.PI/4 + lat3/2);var lon3 = ( (lon2-lon1)*Math.log(f3) + lon1*Math.log(f2) -lon2*Math.log(f1) ) /

    Math.log(f2/f1);

    if (!isFinite(lon3)) lon3 = (lon1+lon2)/2; // parallel oflatitude

    lon3 = (lon3+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to-180..+180

    Using the scripts in web pages

    Using these scripts in web pages would be something like the following:

    /* Latitude/Longitude formulae *//* Geodesy representation conversions */...

    Lat1: Lon1:

    Lat2: Lon2:

    Calculate distance

    http://mathforum.org/kb/message.jspa?messageID=148837http://mathforum.org/kb/message.jspa?messageID=148837http://mathforum.org/kb/message.jspa?messageID=148837
  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    10/24

    If you use jQuery, the code can be separated from the HTML:

    /* Latitude/Longitude formulae */

    /* Geodesy representation conversions */

    $(document).ready(function() {$('#calc-dist').click(function() {

    var p1 = new LatLon(Geo.parseDMS($('#lat1').val()),Geo.parseDMS($('#lon1').val()));

    var p2 = new LatLon(Geo.parseDMS($('#lat2').val()),Geo.parseDMS($('#lon2').val()));

    $('#result-distance').html(p1.distanceTo(p2)+' km');});

    });...

    Lat1: Lon1: Lat2: Lon2: Calculate distance

    Convert between degrees-minutes-seconds & decimal degrees

    Latitude Longitude 1 111 km (110.57 eql 111.70 polar)5212'17.0

    00008'26.

    1 1.85 km (= 1 nm) 0.01 1.11 km

    52.20472

    0.14056

    1 30.9 m 0.0001 11.1 m

    No, Ive not included decimal minutes: a decimal system is easy, a sexagesimal system hasmerits, but mixing the two is a complete sows ear. Switch off the option on your GPS!

    Display calculation results as: deg/min/sec decimal degrees

    Notes:

    Accuracy: since the earth is not quite a sphere, there are small errors in usingspherical geometry; the earth is actually roughly ellipsoidal (or more precisely, oblate

    spheroidal) with a radius varying between about 6,378km (equatorial) and 6,357km

    (polar), and local radius of curvature varying from 6,336km (equatorial meridian) to

    6,399km (polar). 6,371 km is the generally accepted value for the Earths meanradius. This means that errors from assuming spherical geometry might be up to

    0.55% crossing the equator, though generally below 0.3%, depending on latitude and

    direction of travel. An accuracy of better than 3m in 1km is mostly good enough for

    me, but if you want greater accuracy, you could use theVincentyformula for

    http://www.movable-type.co.uk/scripts/latlong-vincenty.htmlhttp://www.movable-type.co.uk/scripts/latlong-vincenty.htmlhttp://www.movable-type.co.uk/scripts/latlong-vincenty.htmlhttp://www.movable-type.co.uk/scripts/latlong-vincenty.html
  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    11/24

    calculating geodesic distances on ellipsoids, which gives results accurate to within

    1mm. (Out of sheer perversityIve never needed such accuracyI looked up thisformula and discovered the JavaScript implementation was simpler than I expected).

    Trig functions take arguments in radians, so latitude, longitude, and bearings indegrees (either decimal or degrees/minutes/seconds) need to be converted to radians,

    rad = .deg/180. When converting radians back to degrees (deg = 180.rad/), West isnegative if using signed decimal degrees. For bearings, values in the range - to + [-180 to +180] need to be converted to 0 to +2 [0360]; this can be done by(brng+2.)%2. [or brng+360)%360] where % is the modulo operator.

    The atan2() function widely used here takes two arguments, atan2(y, x), andcomputes the arc tangent of the ratio y/x. It is more flexible than atan(y/x), since it

    handles x=0, and it also returns values in all 4 quadrants - to + (the atan functionreturns values in the range -/2 to +/2).

    All bearings are with respect to true north, 0=N, 90=E, etc; if you are workingfrom a compass, magnetic north varies from true north in a complex way around the

    earth, and the difference has to be compensated for by variances indicated on local

    maps. If you implement any formula involving atan2 in Microsoft Excel, you will need to

    reverse the arguments, asExcelhas them the opposite way around fromJavaScriptconventional order is atan2(y, x), but Excel uses atan2(x, y). To use atan2 in a (VBA)

    macro, you can use WorksheetFunction.Atan2().

    If you are using Google Maps, several of these functions are now provided in theGoogle Maps API V3 spherical library (computeDistanceBetween(),computeHeading(), computeOffset(), interpolate(), etc; note they use a default Earth

    radius of 6,378,137 meters).

    If you use Ordnance Survey Grid References, I have implemented a script forconverting between Lat/Long & OS Grid References.

    I learned a lot from the US Census BureauGIS FAQwhich is no longer available, soIve made a copy.

    Thanks to Ed WilliamsAviation Formularyfor many of the formul. Formiles, divide km by 1.609344 Fornautical miles, divide km by 1.852

    See below for the source code of the JavaScript implementation. These functions should be

    simple to translate into other languages if required.

    Update January 2010: I have revised the scripts to be structured as methods of a LatLon

    object. Of course, JavaScript is aprototype-based rather than class-basedlanguage, so this is

    only nominally a class, but isolating code into a separate namespace is good JavaScript

    practice, and this approach may also make it clearer to implement these functions in other

    languages. If youre not familiar with JavaScript syntax, LatLon.prototype.distanceTo =function(point) { ... }, for instance, defines a distanceTo method of the LatLonobject (/class) which takes a LatLon object as a parameter (and returns a number). The Geo

    namespace acts as a static class for geodesy formatting / parsing / conversion functions. I

    have extended (polluted, if you like) the base JavaScript object prototypes with trim(),

    toRad() toDeg(), and toPrecisionFixed() methods. Ive adopted JSDoc format for the

    descriptions.

    http://office.microsoft.com/en-gb/excel/HP052089911033.aspxhttp://office.microsoft.com/en-gb/excel/HP052089911033.aspxhttp://office.microsoft.com/en-gb/excel/HP052089911033.aspxhttps://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Math/atan2https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Math/atan2https://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Math/atan2http://www.movable-type.co.uk/scripts/latlong-gridref.htmlhttp://www.movable-type.co.uk/scripts/latlong-gridref.htmlhttp://www.movable-type.co.uk/scripts/gis-faq-5.1.htmlhttp://www.movable-type.co.uk/scripts/gis-faq-5.1.htmlhttp://www.movable-type.co.uk/scripts/gis-faq-5.1.htmlhttp://williams.best.vwh.net/avform.htmhttp://williams.best.vwh.net/avform.htmhttp://williams.best.vwh.net/avform.htmhttps://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Details_of_the_Object_Modelhttps://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Details_of_the_Object_Modelhttps://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Details_of_the_Object_Modelhttps://developer.mozilla.org/en/Core_JavaScript_1.5_Guide/Details_of_the_Object_Modelhttp://williams.best.vwh.net/avform.htmhttp://www.movable-type.co.uk/scripts/gis-faq-5.1.htmlhttp://www.movable-type.co.uk/scripts/latlong-gridref.htmlhttps://developer.mozilla.org/en/Core_JavaScript_1.5_Reference/Objects/Math/atan2http://office.microsoft.com/en-gb/excel/HP052089911033.aspx
  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    12/24

    I have also created a page illustrating the use of the spherical law of cosines forselecting

    points from a databasewithin a specified bounding circlethe example is based onMySQL+PDO, but should be extensible to other DBMS platforms.

    Several people have asked about example Excel spreadsheets, so I have implemented the

    distance & bearingand thedestination pointformul as spreadsheets, in a form which breaksdown the all stages involved to illustrate the operation.

    I offer these formul & scripts for free use and adaptation as my contribution to the

    open-source info-sphere from which I have received so much. You are welcome to re-use

    these scripts [under a simpleattributionlicense, without any warranty express or implied]

    provided solely that you retain my copyright notice and a reference to this page.

    If you would like to show your appreciation and support continued development of these

    scripts, I would most gratefully acceptdonations.

    If you need any advice or development work done, I am available for consultancy.

    If you have any queries or find any problems, contact me atku.oc.epyt-elbavom@oeg-stpircs.

    2002-2012 Chris Veness

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - */

    /* Latitude/longitude spherical geodesy formulae & scripts (c) ChrisVeness 2002-2012 *//* - www.movable-type.co.uk/scripts/latlong.html*//**//* Sample usage:*//* var p1 = new LatLon(51.5136, -0.0983);*//* var p2 = new LatLon(51.4778, -0.0015);*//* var dist = p1.distanceTo(p2); // in km

    *//* var brng = p1.bearingTo(p2); // in degrees clockwise fromnorth *//* ... etc*//* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - */

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - *//* Note that minimal error checking is performed in this example code!*//* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -

    - - - - - - - - - - - */

    http://www.movable-type.co.uk/scripts/latlong-db.htmlhttp://www.movable-type.co.uk/scripts/latlong-db.htmlhttp://www.movable-type.co.uk/scripts/latlong-db.htmlhttp://www.movable-type.co.uk/scripts/latlong-db.htmlhttp://www.movable-type.co.uk/scripts/latlong-distance+bearing.xlshttp://www.movable-type.co.uk/scripts/latlong-distance+bearing.xlshttp://www.movable-type.co.uk/scripts/latlong-dest-point.xlshttp://www.movable-type.co.uk/scripts/latlong-dest-point.xlshttp://www.movable-type.co.uk/scripts/latlong-dest-point.xlshttp://creativecommons.org/licenses/by/3.0/http://creativecommons.org/licenses/by/3.0/http://creativecommons.org/licenses/by/3.0/http://creativecommons.org/licenses/by/3.0/https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3737803https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3737803https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3737803https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3737803mailto:[email protected]:[email protected]:[email protected]:[email protected]://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3737803https://www.paypal.com/cgi-bin/webscr?cmd=_s-xclick&hosted_button_id=3737803http://creativecommons.org/licenses/by/3.0/http://creativecommons.org/licenses/by/3.0/http://www.movable-type.co.uk/scripts/latlong-dest-point.xlshttp://www.movable-type.co.uk/scripts/latlong-distance+bearing.xlshttp://www.movable-type.co.uk/scripts/latlong-db.htmlhttp://www.movable-type.co.uk/scripts/latlong-db.html
  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    13/24

    /*** @requires Geo*/

    /**

    * Creates a point on the earth's surface at the supplied latitude /longitude** @constructor* @param {Number} lat: latitude in numeric degrees* @param {Number} lon: longitude in numeric degrees* @param {Number} [rad=6371]: radius of earth if different value is

    required from standard 6,371km*/

    function LatLon(lat, lon, rad) {if (typeof(rad) == 'undefined') rad = 6371; // earth's mean radius in km// only accept numbers or valid numeric stringsthis._lat = typeof(lat)=='number' ? lat : typeof(lat)=='string' &&

    lat.trim()!='' ? +lat : NaN;this._lon = typeof(lon)=='number' ? lon : typeof(lon)=='string' &&lon.trim()!='' ? +lon : NaN;

    this._radius = typeof(rad)=='number' ? rad : typeof(rad)=='string' &&trim(lon)!='' ? +rad : NaN;}

    /*** Returns the distance from this point to the supplied point, in km* (using Haversine formula)** from: Haversine formula - R. W. Sinnott, "Virtues of the Haversine",* Sky and Telescope, vol 68, no 2, 1984** @param {LatLon} point: Latitude/longitude of destination point* @param {Number} [precision=4]: no of significant digits to use for

    returned value* @returns {Number} Distance in km between this point and destination

    point*/

    LatLon.prototype.distanceTo = function(point, precision) {// default 4 sig figs reflects typical 0.3% accuracy of spherical modelif (typeof precision == 'undefined') precision = 4;

    var R = this._radius;var lat1 = this._lat.toRad(), lon1 = this._lon.toRad();var lat2 = point._lat.toRad(), lon2 = point._lon.toRad();var dLat = lat2 - lat1;var dLon = lon2 - lon1;

    var a = Math.sin(dLat/2) * Math.sin(dLat/2) +Math.cos(lat1) * Math.cos(lat2) *Math.sin(dLon/2) * Math.sin(dLon/2);

    var c = 2 * Math.atan2(Math.sqrt(a), Math.sqrt(1-a));var d = R * c;return d.toPrecisionFixed(precision);

    }

    /**

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    14/24

    * Returns the (initial) bearing from this point to the supplied point, indegrees* see http://williams.best.vwh.net/avform.htm#Crs** @param {LatLon} point: Latitude/longitude of destination point* @returns {Number} Initial bearing in degrees from North

    */LatLon.prototype.bearingTo = function(point) {var lat1 = this._lat.toRad(), lat2 = point._lat.toRad();var dLon = (point._lon-this._lon).toRad();

    var y = Math.sin(dLon) * Math.cos(lat2);var x = Math.cos(lat1)*Math.sin(lat2) -

    Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);var brng = Math.atan2(y, x);

    return (brng.toDeg()+360) % 360;}

    /*** Returns final bearing arriving at supplied destination point from this

    point; the final bearing* will differ from the initial bearing by varying degrees according to

    distance and latitude** @param {LatLon} point: Latitude/longitude of destination point* @returns {Number} Final bearing in degrees from North*/

    LatLon.prototype.finalBearingTo = function(point) {// get initial bearing from supplied point back to this point...var lat1 = point._lat.toRad(), lat2 = this._lat.toRad();var dLon = (this._lon-point._lon).toRad();

    var y = Math.sin(dLon) * Math.cos(lat2);var x = Math.cos(lat1)*Math.sin(lat2) -

    Math.sin(lat1)*Math.cos(lat2)*Math.cos(dLon);var brng = Math.atan2(y, x);

    // ... & reverse it by adding 180return (brng.toDeg()+180) % 360;

    }

    /*** Returns the midpoint between this point and the supplied point.* see http://mathforum.org/library/drmath/view/51822.html for derivation** @param {LatLon} point: Latitude/longitude of destination point* @returns {LatLon} Midpoint between this point and the supplied point*/

    LatLon.prototype.midpointTo = function(point) {lat1 = this._lat.toRad(), lon1 = this._lon.toRad();lat2 = point._lat.toRad();var dLon = (point._lon-this._lon).toRad();

    var Bx = Math.cos(lat2) * Math.cos(dLon);var By = Math.cos(lat2) * Math.sin(dLon);

    lat3 = Math.atan2(Math.sin(lat1)+Math.sin(lat2),

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    15/24

    Math.sqrt( (Math.cos(lat1)+Bx)*(Math.cos(lat1)+Bx) +By*By) );

    lon3 = lon1 + Math.atan2(By, Math.cos(lat1) + Bx);lon3 = (lon3+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -

    180..+180

    return new LatLon(lat3.toDeg(), lon3.toDeg());}

    /*** Returns the destination point from this point having travelled the given

    distance (in km) on the* given initial bearing (bearing may vary before destination is reached)** see http://williams.best.vwh.net/avform.htm#LL** @param {Number} brng: Initial bearing in degrees* @param {Number} dist: Distance in km

    * @returns {LatLon} Destination point*/LatLon.prototype.destinationPoint = function(brng, dist) {

    dist = typeof(dist)=='number' ? dist : typeof(dist)=='string' &&dist.trim()!='' ? +dist : NaN;

    dist = dist/this._radius; // convert dist to angular distance in radiansbrng = brng.toRad(); //var lat1 = this._lat.toRad(), lon1 = this._lon.toRad();

    var lat2 = Math.asin( Math.sin(lat1)*Math.cos(dist) +Math.cos(lat1)*Math.sin(dist)*Math.cos(brng) );

    var lon2 = lon1 +Math.atan2(Math.sin(brng)*Math.sin(dist)*Math.cos(lat1),

    Math.cos(dist)-Math.sin(lat1)*Math.sin(lat2));

    lon2 = (lon2+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..+180

    return new LatLon(lat2.toDeg(), lon2.toDeg());}

    /*** Returns the point of intersection of two paths defined by point and

    bearing** see http://williams.best.vwh.net/avform.htm#Intersection** @param {LatLon} p1: First point* @param {Number} brng1: Initial bearing from first point* @param {LatLon} p2: Second point* @param {Number} brng2: Initial bearing from second point* @returns {LatLon} Destination point (null if no unique intersection

    defined)*/

    LatLon.intersection = function(p1, brng1, p2, brng2) {brng1 = typeof brng1 == 'number' ? brng1 : typeof brng1 == 'string' &&

    trim(brng1)!='' ? +brng1 : NaN;brng2 = typeof brng2 == 'number' ? brng2 : typeof brng2 == 'string' &&

    trim(brng2)!='' ? +brng2 : NaN;

    lat1 = p1._lat.toRad(), lon1 = p1._lon.toRad();lat2 = p2._lat.toRad(), lon2 = p2._lon.toRad();

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    16/24

    brng13 = brng1.toRad(), brng23 = brng2.toRad();dLat = lat2-lat1, dLon = lon2-lon1;

    dist12 = 2*Math.asin( Math.sqrt( Math.sin(dLat/2)*Math.sin(dLat/2) +Math.cos(lat1)*Math.cos(lat2)*Math.sin(dLon/2)*Math.sin(dLon/2) ) );

    if (dist12 == 0) return null;

    // initial/final bearings between pointsbrngA = Math.acos( ( Math.sin(lat2) - Math.sin(lat1)*Math.cos(dist12) ) /

    ( Math.sin(dist12)*Math.cos(lat1) ) );if (isNaN(brngA)) brngA = 0; // protect against roundingbrngB = Math.acos( ( Math.sin(lat1) - Math.sin(lat2)*Math.cos(dist12) ) /

    ( Math.sin(dist12)*Math.cos(lat2) ) );

    if (Math.sin(lon2-lon1) > 0) {brng12 = brngA;brng21 = 2*Math.PI - brngB;

    } else {brng12 = 2*Math.PI - brngA;

    brng21 = brngB;}

    alpha1 = (brng13 - brng12 + Math.PI) % (2*Math.PI) - Math.PI; // angle2-1-3

    alpha2 = (brng21 - brng23 + Math.PI) % (2*Math.PI) - Math.PI; // angle1-2-3

    if (Math.sin(alpha1)==0 && Math.sin(alpha2)==0) return null; // infiniteintersections

    if (Math.sin(alpha1)*Math.sin(alpha2) < 0) return null; //ambiguous intersection

    //alpha1 = Math.abs(alpha1);//alpha2 = Math.abs(alpha2);// ... Ed Williams takes abs of alpha1/alpha2, but seems to break

    calculation?

    alpha3 = Math.acos( -Math.cos(alpha1)*Math.cos(alpha2) +Math.sin(alpha1)*Math.sin(alpha2)*Math.cos(dist12)

    );dist13 = Math.atan2( Math.sin(dist12)*Math.sin(alpha1)*Math.sin(alpha2),

    Math.cos(alpha2)+Math.cos(alpha1)*Math.cos(alpha3) )lat3 = Math.asin( Math.sin(lat1)*Math.cos(dist13) +

    Math.cos(lat1)*Math.sin(dist13)*Math.cos(brng13) );dLon13 = Math.atan2( Math.sin(brng13)*Math.sin(dist13)*Math.cos(lat1),

    Math.cos(dist13)-Math.sin(lat1)*Math.sin(lat3) );lon3 = lon1+dLon13;lon3 = (lon3+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -

    180..+180

    return new LatLon(lat3.toDeg(), lon3.toDeg());}

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - */

    /*** Returns the distance from this point to the supplied point, in km,

    travelling along a rhumb line*

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    17/24

    * see http://williams.best.vwh.net/avform.htm#Rhumb** @param {LatLon} point: Latitude/longitude of destination point* @returns {Number} Distance in km between this point and destination

    point*/

    LatLon.prototype.rhumbDistanceTo = function(point) {var R = this._radius;var lat1 = this._lat.toRad(), lat2 = point._lat.toRad();var dLat = (point._lat-this._lat).toRad();var dLon = Math.abs(point._lon-this._lon).toRad();

    var dPhi =Math.log(Math.tan(lat2/2+Math.PI/4)/Math.tan(lat1/2+Math.PI/4));

    var q = (isFinite(dLat/dPhi)) ? dLat/dPhi : Math.cos(lat1); // E-W linegives dPhi=0

    // if dLon over 180 take shorter rhumb across anti-meridian:if (Math.abs(dLon) > Math.PI) {

    dLon = dLon>0 ? -(2*Math.PI-dLon) : (2*Math.PI+dLon);}

    var dist = Math.sqrt(dLat*dLat + q*q*dLon*dLon) * R;

    return dist.toPrecisionFixed(4); // 4 sig figs reflects typical 0.3%accuracy of spherical model}

    /*** Returns the bearing from this point to the supplied point along a rhumb

    line, in degrees** @param {LatLon} point: Latitude/longitude of destination point* @returns {Number} Bearing in degrees from North*/

    LatLon.prototype.rhumbBearingTo = function(point) {var lat1 = this._lat.toRad(), lat2 = point._lat.toRad();var dLon = (point._lon-this._lon).toRad();

    var dPhi =Math.log(Math.tan(lat2/2+Math.PI/4)/Math.tan(lat1/2+Math.PI/4));

    if (Math.abs(dLon) > Math.PI) dLon = dLon>0 ? -(2*Math.PI-dLon) :(2*Math.PI+dLon);

    var brng = Math.atan2(dLon, dPhi);

    return (brng.toDeg()+360) % 360;}

    /*** Returns the destination point from this point having travelled the given

    distance (in km) on the* given bearing along a rhumb line** @param {Number} brng: Bearing in degrees from North* @param {Number} dist: Distance in km* @returns {LatLon} Destination point*/

    LatLon.prototype.rhumbDestinationPoint = function(brng, dist) {var R = this._radius;

    var d = parseFloat(dist)/R; // d = angular distance covered on earthssurface

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    18/24

    var lat1 = this._lat.toRad(), lon1 = this._lon.toRad();brng = brng.toRad();

    var dLat = d*Math.cos(brng);// nasty kludge to overcome ill-conditioned results around parallels of

    latitude:

    if (Math.abs(dLat) < 1e-10) dLat = 0; // dLat < 1 mm

    var lat2 = lat1 + dLat;var dPhi =

    Math.log(Math.tan(lat2/2+Math.PI/4)/Math.tan(lat1/2+Math.PI/4));var q = (isFinite(dLat/dPhi)) ? dLat/dPhi : Math.cos(lat1); // E-W line

    gives dPhi=0var dLon = d*Math.sin(brng)/q;

    // check for some daft bugger going past the pole, normalise latitude ifso

    if (Math.abs(lat2) > Math.PI/2) lat2 = lat2>0 ? Math.PI-lat2 : -Math.PI-lat2;

    lon2 = (lon1+dLon+3*Math.PI)%(2*Math.PI) - Math.PI;

    return new LatLon(lat2.toDeg(), lon2.toDeg());}

    /*** Returns the loxodromic midpoint (along a rhumb line) between this point

    and the supplied point.* see http://mathforum.org/kb/message.jspa?messageID=148837** @param {LatLon} point: Latitude/longitude of destination point* @returns {LatLon} Midpoint between this point and the supplied point*/

    LatLon.prototype.rhumbMidpointTo = function(point) {lat1 = this._lat.toRad(), lon1 = this._lon.toRad();lat2 = point._lat.toRad(), lon2 = point._lon.toRad();

    if (Math.abs(lon2-lon1) > Math.PI) lon1 += 2*Math.PI; // crossing anti-meridian

    var lat3 = (lat1+lat2)/2;var f1 = Math.tan(Math.PI/4 + lat1/2);var f2 = Math.tan(Math.PI/4 + lat2/2);var f3 = Math.tan(Math.PI/4 + lat3/2);var lon3 = ( (lon2-lon1)*Math.log(f3) + lon1*Math.log(f2) -

    lon2*Math.log(f1) ) / Math.log(f2/f1);

    if (!isFinite(lon3)) lon3 = (lon1+lon2)/2; // parallel of latitude

    lon3 = (lon3+3*Math.PI) % (2*Math.PI) - Math.PI; // normalise to -180..+180

    return new LatLon(lat3.toDeg(), lon3.toDeg());}

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - */

    /**

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    19/24

    * Returns the latitude of this point; signed numeric degrees if no format,otherwise format & dp* as per Geo.toLat()** @param {String} [format]: Return value as 'd', 'dm', 'dms'* @param {Number} [dp=0|2|4]: No of decimal places to display

    * @returns {Number|String} Numeric degrees if no format specified,otherwise deg/min/sec*/

    LatLon.prototype.lat = function(format, dp) {if (typeof format == 'undefined') return this._lat;

    return Geo.toLat(this._lat, format, dp);}

    /*** Returns the longitude of this point; signed numeric degrees if no

    format, otherwise format & dp* as per Geo.toLon()

    ** @param {String} [format]: Return value as 'd', 'dm', 'dms'* @param {Number} [dp=0|2|4]: No of decimal places to display* @returns {Number|String} Numeric degrees if no format specified,

    otherwise deg/min/sec*/

    LatLon.prototype.lon = function(format, dp) {if (typeof format == 'undefined') return this._lon;

    return Geo.toLon(this._lon, format, dp);}

    /*** Returns a string representation of this point; format and dp as per

    lat()/lon()** @param {String} [format]: Return value as 'd', 'dm', 'dms'* @param {Number} [dp=0|2|4]: No of decimal places to display* @returns {String} Comma-separated latitude/longitude*/

    LatLon.prototype.toString = function(format, dp) {if (typeof format == 'undefined') format = 'dms';

    return Geo.toLat(this._lat, format, dp) + ', ' + Geo.toLon(this._lon,format, dp);}

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - */

    // ---- extend Number object with methods for converting degrees/radians

    /** Converts numeric degrees to radians */if (typeof Number.prototype.toRad == 'undefined') {

    Number.prototype.toRad = function() {return this * Math.PI / 180;

    }}

    /** Converts radians to numeric (signed) degrees */

    if (typeof Number.prototype.toDeg == 'undefined') {Number.prototype.toDeg = function() {

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    20/24

    return this * 180 / Math.PI;}

    }

    /*** Formats the significant digits of a number, using only fixed-point

    notation (no exponential)** @param {Number} precision: Number of significant digits to appear in

    the returned string* @returns {String} A string representation of number which contains

    precision significant digits*/

    if (typeof Number.prototype.toPrecisionFixed == 'undefined') {Number.prototype.toPrecisionFixed = function(precision) {

    // use standard toPrecision methodvar n = this.toPrecision(precision);

    // ... but replace +ve exponential format with trailing zerosn = n.replace(/(.+)e\+(.+)/, function(n, sig, exp) {sig = sig.replace(/\./, ''); // remove decimal from significandl = sig.length - 1;while (exp-- > l) sig = sig + '0'; // append zeros from exponentreturn sig;

    });

    // ... and replace -ve exponential format with leading zerosn = n.replace(/(.+)e-(.+)/, function(n, sig, exp) {

    sig = sig.replace(/\./, ''); // remove decimal from significandwhile (exp-- > 1) sig = '0' + sig; // prepend zeros from exponentreturn '0.' + sig;

    });

    return n;}

    }

    /** Trims whitespace from string (q.v.blog.stevenlevithan.com/archives/faster-trim-javascript) */if (typeof String.prototype.trim == 'undefined') {

    String.prototype.trim = function() {return String(this).replace(/^\s\s*/, '').replace(/\s\s*$/, '');

    }}

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - */if (!window.console) window.console = { log: function() {} };

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - *//* Geodesy representation conversion functions (c) Chris Veness 2002-2012*//* - www.movable-type.co.uk/scripts/latlong.html*//**//* Sample usage:*/

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    21/24

    /* var lat = Geo.parseDMS('51 28 40.12 N');*//* var lon = Geo.parseDMS('000 00 05.31 W');*//* var p1 = new LatLon(lat, lon);*/

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - */

    var Geo = {}; // Geo namespace, representing static class

    /*** Parses string representing degrees/minutes/seconds into numeric degrees** This is very flexible on formats, allowing signed decimal degrees, or

    deg-min-sec optionally* suffixed by compass direction (NSEW). A variety of separators are

    accepted (eg 3 37' 09"W)* or fixed-width format without separators (eg 0033709W). Seconds andminutes may be omitted.* (Note minimal validation is done).** @param {String|Number} dmsStr: Degrees or deg/min/sec in variety of

    formats* @returns {Number} Degrees as decimal number* @throws {TypeError} dmsStr is an object, perhaps DOM object without

    .value?*/

    Geo.parseDMS = function(dmsStr) {if (typeof deg == 'object') throw new TypeError('Geo.parseDMS - dmsStr is

    [DOM?] object');

    // check for signed decimal degrees without NSEW, if so return itdirectly

    if (typeof dmsStr === 'number' && isFinite(dmsStr)) returnNumber(dmsStr);

    // strip off any sign or compass dir'n & split out separate d/m/svar dms = String(dmsStr).trim().replace(/^-

    /,'').replace(/[NSEW]$/i,'').split(/[^0-9.,]+/);if (dms[dms.length-1]=='') dms.splice(dms.length-1); // from trailing

    symbol

    if (dms == '') return NaN;

    // and convert to decimal degrees...switch (dms.length) {

    case 3: // interpret 3-part result as d/m/svar deg = dms[0]/1 + dms[1]/60 + dms[2]/3600;break;

    case 2: // interpret 2-part result as d/mvar deg = dms[0]/1 + dms[1]/60;break;

    case 1: // just d (possibly decimal) or non-separated dddmmssvar deg = dms[0];// check for fixed-width unseparated format eg 0033709W//if (/[NS]/i.test(dmsStr)) deg = '0' + deg; // - normalise N/S to

    3-digit degrees

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    22/24

    //if (/[0-9]{7}/.test(deg)) deg = deg.slice(0,3)/1 +deg.slice(3,5)/60 + deg.slice(5)/3600;

    break;default:

    return NaN;}

    if (/^-|[WS]$/i.test(dmsStr.trim())) deg = -deg; // take '-', west andsouth as -vereturn Number(deg);

    }

    /*** Convert decimal degrees to deg/min/sec format* - degree, prime, double-prime symbols are added, but sign is discarded,

    though no compass* direction is added** @private

    * @param {Number} deg: Degrees* @param {String} [format=dms]: Return value as 'd', 'dm', 'dms'* @param {Number} [dp=0|2|4]: No of decimal places to use - default 0

    for dms, 2 for dm, 4 for d* @returns {String} deg formatted as deg/min/secs according to specified

    format* @throws {TypeError} deg is an object, perhaps DOM object without

    .value?*/

    Geo.toDMS = function(deg, format, dp) {if (typeof deg == 'object') throw new TypeError('Geo.toDMS - deg is

    [DOM?] object');if (isNaN(deg)) return null; // give up here if we can't make a number

    from deg

    // default valuesif (typeof format == 'undefined') format = 'dms';if (typeof dp == 'undefined') {

    switch (format) {case 'd': dp = 4; break;case 'dm': dp = 2; break;case 'dms': dp = 0; break;default: format = 'dms'; dp = 0; // be forgiving on invalid format

    }}

    deg = Math.abs(deg); // (unsigned result ready for appending compassdir'n)

    switch (format) {case 'd':

    d = deg.toFixed(dp); // round degreesif (d

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    23/24

    if (d

  • 7/28/2019 Calculate Distance Bearing - Movable Type Scripts

    24/24

    * @returns {String} Deg/min/seconds*/

    Geo.toBrng = function(deg, format, dp) {deg = (Number(deg)+360) % 360; // normalise -ve values to 180..360var brng = Geo.toDMS(deg, format, dp);return brng==null ? '' : brng.replace('360', '0'); // just in case

    rounding took us up to 360!}

    /* - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -- - - - - - - - - - - */if (!window.console) window.console = { log: function() {} };


Recommended