Decimal Degrees to Degrees Minutes And Seconds in Javascript
Asked Answered
M

12

17

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?

Manufactory answered 26/4, 2011 at 4:43 Comment(3)
Homework assignment? Hint: use Math.floorAutostability
The accepted answer's Javascript implementation does not work for negative valuesPalladous
Better answer: https://mcmap.net/q/629782/-how-to-convert-lat-long-from-decimal-degrees-to-dms-formatPepita
G
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);
}
Gross answered 26/4, 2011 at 6:10 Comment(6)
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.Pistol
@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" :/Gross
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?Homolographic
The Javascript implementation does not work for negative values of degPalladous
It's not just that it does not work for negative values, it gives the wrong value.Rockfish
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.Muddlehead
P
38
function ConvertDDToDMS(D, lng) {
  return {
    dir: D < 0 ? (lng ? "W" : "S") : lng ? "E" : "N",
    deg: 0 | (D < 0 ? (D = -D) : D),
    min: 0 | (((D += 1e-9) % 1) * 60),
    sec: (0 | (((D * 60) % 1) * 6000)) / 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=(D<0?-D:D)+1e-4)%1*60, "' ", 0|D*60%1*60, '"'].join('');
}

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

[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).

[edit Dec 2021] -- Whoops. Fix #2. Went back to the original code and added 1e-9 to the value which a) bumps any slightly low floating point errors to the next highest number and b) is less than .01 sec, so has no effect on the output. Added 1e-4 to the "string" version which is the same fix, but also rounds seconds (it's close to 1/2 sec).

Pistol answered 26/4, 2011 at 5:23 Comment(5)
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???Ling
@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)Pistol
@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.Carmencita
@MarkKahn, Nick is correct in pointing out this error, happened for me too.Childers
Should be fixed nowPistol
S
13

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 + "\"";
}
Sniffle answered 26/4, 2011 at 5:16 Comment(1)
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 (accounting for negatives and lat/lon boundaries) solves this.Exert
G
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);
}
Gross answered 26/4, 2011 at 6:10 Comment(6)
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.Pistol
@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" :/Gross
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?Homolographic
The Javascript implementation does not work for negative values of degPalladous
It's not just that it does not work for negative values, it gives the wrong value.Rockfish
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.Muddlehead
S
3

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}`
  }
Southdown answered 16/11, 2020 at 22:32 Comment(0)
I
2

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 
Immuno answered 28/11, 2019 at 6:16 Comment(0)
M
2

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"
Moneyer answered 22/5, 2020 at 18:12 Comment(0)
E
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

Exception answered 7/7, 2018 at 10:52 Comment(0)
H
1

I'm surprised all solutions are using some additional logic to handle the "rounds to 60" cases (if they're aware of it at all), but nobody thought of doing it the other way round, starting with (rounded) seconds and then using mod and int-div and not have to worry about all that:

function coordToStr(coord)
{   
    let seconds = Math.round(Math.abs(coord) * 3600)
    let sec = Math.floor(seconds % 60)
    let minutes = Math.floor(seconds / 60)
    let min = minutes % 60
    let deg = Math.floor(minutes / 60)
    return deg + "°" + ((min < 10) ? "0" : "") + min + "'" + ((sec < 10) ? "0" : "") + sec
}

Sorry, this is without the N/S, E/W part, would need some additional method calling it.

If you want second-fractions, you could use this:

function coordToStrWithDecimals(coord)
{   
    let centiSecs = Math.round(Math.abs(coord) * 360000)
    let frac = Math.floor(centiSecs % 100)
    let seconds = Math.floor(centiSecs / 100)
    let sec = Math.floor(seconds % 60)
    let minutes = Math.floor(seconds / 60)
    let min = minutes % 60
    let deg = Math.floor(minutes / 60)
    return deg + "°" + ((min < 10) ? "0" : "") + min + "'" + ((sec < 10) ? "0" : "") + sec + "." + ((frac < 10) ? "0" : "") + frac + '"'
}
Hodgepodge answered 27/1, 2022 at 9:10 Comment(0)
A
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;
}
Athey answered 13/12, 2016 at 10:37 Comment(0)
V
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..

Viv answered 15/8, 2019 at 18:41 Comment(0)
R
0

Good answers here but to get the DMS in the standard format.

function padZero(num, targetLength) {
  return String(num).padStart(targetLength, "0");
}

function ddToDms(deg, latOrlon) {
  var absolute = Math.abs(deg);
  var degrees = Math.floor(absolute);
  var minNt = (absolute - degrees) * 60;
  var minutes = Math.floor(minNt);
  var seconds = ((minNt - minutes) * 60).toFixed(2);
  var secs = Math.floor(seconds);

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

  // Ensure 60 minutes add 1 to degree and 60 seconds add 1 to minutes
  if (seconds == 60) {
    minutes++;
    seconds = 0;
  }
  if (minutes == 60) {
    degrees++;
    minutes = 0;
  }

  // Pad with zero
  if (
    (degrees < 10 && latOrlon == "lat") ||
    (degrees > 10 && degrees < 100 && latOrlon == "lon")
  ) {
    var degrees = padZero(degrees, String(degrees).length + 1);
  } else if (degrees < 10 && latOrlon == "lon") {
    var degrees = padZero(degrees, String(degrees).length + 2);
  }
  if (minutes < 10) {
    var minutes = padZero(minutes, String(minutes).length + 1);
  }
  if (secs < 10) {
    var seconds = padZero(seconds, String(seconds).length + 1);
  }

  // Validate lat and lon
  if (deg > 90 && latOrlon == "lat") {
    alert("LATITUDE CANNOT BE MORE THAN 90");
  } else if (deg > 180 && latOrlon == "lon") {
    alert("LONGITUDE CANNOT BE MORE THAN 180");
  } else {
    return degrees + "°" + minutes + "'" + seconds + '"' + direction;
  }
}

// Example
var lat = 9.129438;
var lon = -4.233587;

var latDMS = ddToDms(lat, 'lat'); 
var lonDMS = ddToDms(lon, 'lon');

console.log('latDMS: '+ latDMS);
console.log('lonDMS: '+ lonDMS);

/*
Output
latDMS: 09°07'45.98"N
lonDMS: 004°14'00.91"W
*/
Roose answered 21/6, 2023 at 11:42 Comment(0)
C
-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

Cycling answered 24/10, 2018 at 22:45 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.