Decimal Degrees to Degrees Minutes And Seconds in Javascript
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 开发者_JS百科track?
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).
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 + "\"";
}
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);
}
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
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"
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}`
}
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
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;
}
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..
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 + '"'
}
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
精彩评论