11

Im trying to write a function that takes my decimal degrees (lat or long) and converts them to DMS degrees minutes seconds. I know I am meant to times the decimal point number by 60 then it's decimal again. But am a noob. Would I split the number?

function ConvertDDToDMS(DD) {
    eg. DD =-42.4
    D= 42;
    M= 4*60;
    S= .M * 60;
    var DMS =

    return DMS //append Direction (N, S, E, W);
}

Am I on the right track?

Gabe
  • 79,868
  • 10
  • 131
  • 226
Christopher
  • 8,739
  • 8
  • 28
  • 37

10 Answers10

32
function ConvertDDToDMS(D, lng){
    const M=0|(D%1)*60e7;

    return {
        dir : D<0?lng?'W':'S':lng?'E':'N',
        deg : 0|(D<0?D=-D:D),
        min : 0|M/1e7,
        sec : (0|M/1e6%1*6e4)/100
    };
}

The above gives you an object {deg:, min:, sec:, dir:} with sec truncated to two digits (e.g. 3.14) and dir being one of N, E, S, W depending on whether you set the lng (longitude) parameter to true. e.g.:

ConvertDDToDMS(-18.213, true) == {
   deg : 18,
   min : 12,
   sec : 46.79,
   dir : 'W'
}

Or if you just want the basic string:

function ConvertDDToDMS(D){
    return [0|D, 'd ', 0|(D<0?D=-D:D)%1*60, "' ", 0|D*60%1*60, '"'].join('');
}

ConvertDDToDMS(-18.213) == `-18d 12' 46"`

[edit June 2019] -- fixing an 8 year old bug that would sometimes cause the result to be 1 minute off due to floating point math when converting an exact minute, e.g. ConvertDDToDMS(4 + 20/60). Updated code still has a slight issue, but will round to 19min 59.99 sec in this case, which is fine and not fixable due to the way floating points work

Mark Kahn
  • 81,115
  • 25
  • 161
  • 212
  • You could possibly pass an array of ['N', 'S'] or ['E','W'] or ['','-'] to make it a bit easier/more generic instead of a true/false??? – Nigel Johnson Dec 26 '16 at 07:15
  • @NigelJohnson - You shouldn't need to pass those. This is pretty old code, but I'd just do: `ConvertLatToDMS = (d) => ConvertDDToDMS(d, false)` and `ConvertLngToDMS = (d) => ConvertDDToDMS(d, true)` – Mark Kahn Dec 26 '16 at 07:56
  • 1
    @MarkKahn, your edit from 9 June 2019 is incorrect. A negative input value will result in negative `min` and `sec` as well as an incorrect `sec` value. – Nick Oct 09 '19 at 14:06
  • @MarkKahn, Nick is correct in pointing out this error, happened for me too. – gromain Sep 17 '20 at 22:03
12

It's not clear how you need the output. Here's a version that returns all 3 values as a string:

function ConvertDDToDMS(dd)
{
    var deg = dd | 0; // truncate dd to get degrees
    var frac = Math.abs(dd - deg); // get fractional part
    var min = (frac * 60) | 0; // multiply fraction by 60 and truncate
    var sec = frac * 3600 - min * 60;
    return deg + "d " + min + "' " + sec + "\"";
}
Gabe
  • 79,868
  • 10
  • 131
  • 226
  • Minor note, this will return 60 seconds in edge cases such as 46.99992 (which should be 47 0' 0"). Adding a check similar to [Aleadam's answer](http://stackoverflow.com/a/5786627/877472) (accounting for negatives and lat/lon boundaries) solves this. – Paul Richter Oct 01 '15 at 19:49
7

Update: I remove the part that did not make any sense (thanks cwolves!).

Here you have yet another implementation. It won't be as short nor efficient as the previous ones, but hopefully much easier to understand.

To get it right, first you need to understand how the calculations are done and only then attempt to implement them. For that, pseudocode is a great option, since you write down the steps in plain English or a simplified syntax that is easy to understand, and then translate it onto the programming language of choice.

I hope it's useful!

/* This is the pseudocode you need to follow:
 * It's a modified version from 
 * http://en.wikipedia.org/wiki/Geographic_coordinate_conversion#Conversion_from_Decimal_Degree_to_DMS

function deg_to_dms ( degfloat )
   Compute degrees, minutes and seconds:
   deg ← integerpart ( degfloat )
   minfloat ← 60 * ( degfloat - deg )
   min ← integerpart ( minfloat )
   secfloat ← 60 * ( minfloat - min )
   Round seconds to desired accuracy:
   secfloat ← round( secfloat, digits )
   After rounding, the seconds might become 60. These two
   if-tests are not necessary if no rounding is done.
   if secfloat = 60
      min ← min + 1
      secfloat ← 0
   end if
   if min = 60
      deg ← deg + 1
      min ← 0
   end if
   Return output:
   return ( deg, min, secfloat )
end function
*/

function deg_to_dms (deg) {
   var d = Math.floor (deg);
   var minfloat = (deg-d)*60;
   var m = Math.floor(minfloat);
   var secfloat = (minfloat-m)*60;
   var s = Math.round(secfloat);
   // After rounding, the seconds might become 60. These two
   // if-tests are not necessary if no rounding is done.
   if (s==60) {
     m++;
     s=0;
   }
   if (m==60) {
     d++;
     m=0;
   }
   return ("" + d + ":" + m + ":" + s);
}
Aleadam
  • 39,361
  • 9
  • 84
  • 108
  • Negatives are most certainly allowed... and your direction is completely off. You can't ascertain longitude vs latitude based on the number. You seem to be confusing polar co-ordinates with DMS (they are completely different...). In other words, you can't tell the difference between `N` & `E` with just a number. – Mark Kahn Apr 26 '11 at 06:25
  • @cwolves Thanks for the comment. Reading a little more it didn't make any sense... I should read my own suggestions some time: "understand how the calculations are done and only then attempt to implement them" :/ – Aleadam Apr 26 '11 at 13:51
  • there is an error: what if due rounding and your d++ degrees exceed the 180 degrees longitude limit?, what if it exceed the 90 degrees latitude limit? – AlexWien May 08 '13 at 10:12
  • 3
    The Javascript implementation does not work for negative values of deg – Jaco Briers Jan 26 '15 at 11:53
  • 1
    It's not just that it does not work for negative values, it gives the wrong value. – psiphi75 Jun 16 '16 at 01:47
  • Easy to overcome a negative value. store positive or negative value in another variable (h, for hemisphere) and then run Math.abs(deg) to normalize it to positive. Then in the return value, include the h variable. This way your receiver can look at the h value and determine if it's N/S for lat or E/W for lon. This is a good function, that can be great with just a small tweak. – Michael Sep 19 '17 at 03:30
1
private static DecimalFormat DecimalFormat = new DecimalFormat(".##");
public static void main(String[] args){
    double decimal_degrees = 22.4229541515;

    System.out.println(getDMS(decimal_degrees));
}
public static String getDMS(double decimal_degrees) {
    double degree =  Math.floor(decimal_degrees);
    double minutes = ((decimal_degrees - Math.floor(decimal_degrees)) * 60.0); 
    double seconds = (minutes - Math.floor(minutes)) * 60.0;
    return ((int)degree)+":"+((int)minutes)+":"+decimalFormat.format(seconds);

}

INPUT : 22.4229541515 OUTPUT: 22:25:22.63

0

Based on above answer, i've written them into javascript and php style.

JS-

function convertDDToDMS(deg, lng){
    var d = parseInt(deg);
    var minfloat  = Math.abs((deg-d) * 60); 
    var m = Math.floor(minfloat);
    var secfloat = (minfloat-m)*60;
    var s = Math.round(secfloat); 
    d = Math.abs(d);

    if (s==60) {
        m++;
        s=0;
    }
    if (m==60) {
        d++;
        m=0;
    }

    return {
        dir : deg<0?lng?'W':'S':lng?'E':'N',
        deg : d,
        min : m,
        sec : s
    };
}

PHP-

function convertDDtoDMS($deg, $lng){
    $dd = intval($deg);
    $minfloat = abs(($deg - $dd) * 60);
    $mm = floor($minfloat);
    $secfloat = ($minfloat - $mm) * 60;
    $ss = round($secfloat);
    $dd = abs($dd);

    if($ss == 60){
        $mm++;
        $ss = 0;
    }

    if($mm == 60){
        $dd++;
        $mm = 0;
    }

    $dd = array(
        'dir' => $deg < 0 ? ($lng ? 'W' : 'S') : ($lng ? 'E' : 'N'),
        'deg' => abs($dd),
        'min' => $mm,
        'sec' => $ss,
    );

    return $dd;
}
Ralph
  • 51
  • 2
0

couldnt get the script above working, after some time came up with this; just give the dms to the script

function ConvertDMSToDEG(dms) {   
    var dms_Array = dms.split(/[^\d\w\.]+/); 
    var degrees = dms_Array[0];
    var minutes = dms_Array[1];
    var seconds = dms_Array[2];
    var direction = dms_Array[3];

    var deg = (Number(degrees) + Number(minutes)/60 + Number(seconds)/3600).toFixed(6);

    if (direction == "S" || direction == "W") {
        deg = deg * -1;
    } // Don't do anything for N or E
    return deg;
}

and visa versa just give the degrees to the script, and true of false for lat (latitude)

function ConvertDEGToDMS(deg, lat) {
    var absolute = Math.abs(deg);

    var degrees = Math.floor(absolute);
    var minutesNotTruncated = (absolute - degrees) * 60;
    var minutes = Math.floor(minutesNotTruncated);
    var seconds = ((minutesNotTruncated - minutes) * 60).toFixed(2);

    if (lat) {
        var direction = deg >= 0 ? "N" : "S";
    } else {
        var direction = deg >= 0 ? "E" : "W";
    }

    return degrees + "°" + minutes + "'" + seconds + "\"" + direction;
}

hope this helps people..

0

Try this working perfect!!!

function truncate(n) {
    return n > 0 ? Math.floor(n) : Math.ceil(n);
}

function getDMS(dd, longOrLat) {
    let hemisphere = /^[WE]|(?:lon)/i.test(longOrLat)
    ? dd < 0
      ? "W"
      : "E"
    : dd < 0
      ? "S"
      : "N";

    const absDD = Math.abs(dd);
    const degrees = truncate(absDD);
    const minutes = truncate((absDD - degrees) * 60);
    const seconds = ((absDD - degrees - minutes / 60) * Math.pow(60, 2)).toFixed(2);

    let dmsArray = [degrees, minutes, seconds, hemisphere];
    return `${dmsArray[0]}°${dmsArray[1]}'${dmsArray[2]}" ${dmsArray[3]}`;
}

var lat = 13.041107;
var lon = 80.233232;

var latDMS = getDMS(lat, 'lat'); 
var lonDMS = getDMS(lon, 'long');
console.log('latDMS: '+ latDMS);
console.log('lonDMS: '+ lonDMS);

Output:
latDMS: 13°2'27.99" N
lonDMS: 80°13'59.64" E 
siva.picky
  • 193
  • 2
  • 12
0

A solution with the option for specifying the decimal places in output seconds and correction of any edge cases due to rounding seconds and minutes.

// @ input {deg}     Numeric; degrees number to convert
// @ input {dplaces} Decimal places to use for output seconds
//                   Default 0 places
// @ return {DMS} string degrees (°) minutes (') seconds (")
//
function degToDMS (deg, dplaces=0) {
  var d = Math.floor (deg);          // make degrees
  var m = Math.floor((deg-d)*60);    // make minutes
  var s = Math.round(((deg-d)*60-m)*60*Math.pow(10,dplaces))/Math.pow(10,dplaces); // Make sec rounded
  s == 60 && (m++, s=0 );            // if seconds rounds to 60 then increment minutes, reset seconds
  m == 60 && (d++, m=0 );            // if minutes rounds to 60 then increment degress, reset minutes
  return (d + "° " + m + "' " + s+'"');   // create output DMS string
}

// ----- tests ------
console.log(degToDMS(55.23456));         // 55° 14' 4"
console.log(degToDMS(55.23456   ,3));    // 55° 14' 4.416"
console.log(degToDMS(4 + 20/60  ,2));    // 4° 20' 0"
console.log(degToDMS(89.64789   ,2));    // 89° 38' 52.4"
console.log(degToDMS(-23.1234567,3));    // -24° 52' 35.556"
Mohsen Alyafei
  • 2,476
  • 3
  • 13
  • 26
0

This one works %100 in TypeScript:

    ConvertDDToDMS(deg: number, lng: boolean): string {

    var d = parseInt(deg.toString());
    var minfloat = Math.abs((deg - d) * 60);
    var m = Math.floor(minfloat);
    var secfloat = (minfloat - m) * 60;
    var s = Math.round((secfloat + Number.EPSILON) * 100) / 100
    d = Math.abs(d);

    if (s == 60) {
      m++;
      s = 0;
    }
    if (m == 60) {
      d++;
      m = 0;
    }

    let dms = {
      dir: deg < 0 ? lng ? 'W' : 'S' : lng ? 'E' : 'N',
      deg: d,
      min: m,
      sec: s
    };
    return `${dms.deg}\u00B0 ${dms.min}' ${dms.sec}" ${dms.dir}`
  }
M.Reza
  • 761
  • 6
  • 14
-1

just for remark, the answer

function ConvertDDToDMS(D){
return [0|D, 'd ', 0|(D<0?D=-D:D)%1*60, "' ", 0|D*60%1*60, '"'].join('');

}

does not work for angles between -1° and 0°. Bad luck! hc