JavaScript中将十进制度数转换为度分秒

17

我正在尝试编写一个函数,将我的十进制度数(纬度或经度)转换为DMS度分秒。我知道我应该把小数点后的数字乘以60,然后再次得到一个小数。但是我是个新手。我应该将这个数字拆分吗?

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);
}

我走得正确吗?


作业任务?提示:使用 Math.floor。 - Austin Taylor
被接受的答案的Javascript实现对于负值无效。 - Jaco Briers
1
更好的答案:https://dev59.com/WloU5IYBdhLWcg3wCjvx#37893239 - devuxer
12个回答

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,
  };
}

以上代码会返回一个对象 {deg, min, sec, dir},其中秒数被截断为两位小数(例如 3.14),而方向则根据你是否将参数 lng(经度)设置为 true 而确定,可能是 NESW。例如:
ConvertDDToDMS(-18.213, true) == {
   deg : 18,
   min : 12,
   sec : 46.79,
   dir : 'W'
}

或者如果你只需要基本字符串:

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"`

[2019年6月修订] -- 修复了一个8年前的错误,当转换精确分钟时,由于浮点数计算,有时会导致结果相差1分钟,例如ConvertDDToDMS(4 + 20/60)

[2021年12月修订] -- 糟糕。修复#2。回到原始代码并添加1e-9到值中,a)将任何稍微低的浮点误差提升到下一个最高数字,b)小于.01秒,因此对输出没有影响。在“字符串”版本中添加1e-4,这是相同的修复,但也四舍五入秒数(接近1/2秒)。


你可以传递一个数组 ['N', 'S'] 或者 ['E','W'] 或者 ['','-'],这样会更容易/更通用一些,而不是使用 true/false。 - Nigel Johnson
@NigelJohnson - 你不需要传递那些参数。虽然这是相当老的代码,但我只会这样做:ConvertLatToDMS = (d) => ConvertDDToDMS(d, false)ConvertLngToDMS = (d) => ConvertDDToDMS(d, true) - user578895
1
@MarkKahn,你在2019年6月9日的编辑是错误的。负数输入值将导致负的“min”和“sec”,以及不正确的“sec”值。 - Nick
@MarkKahn,Nick指出了这个错误,我也遇到了。 - gromain
现在应该已经修复了。 - user578895

13

不清楚您需要什么输出。这里是返回所有3个值作为字符串的版本:

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 + "\"";
}

小注:在极端情况下,例如46.99992秒(应为47分0秒),这将返回60秒。添加一个类似于Aleadam的回答的检查(考虑负数和纬度/经度边界)可以解决这个问题。 - Paul Richter

7
更新:我删除了没有意义的部分(感谢cwolves!)。
这里是另一种实现。它可能不像之前的实现那样简短和高效,但希望更容易理解。
要做正确,首先需要理解如何进行计算,然后再尝试实现它们。为此,伪代码是一个很好的选择,因为你可以用简单易懂的英语或简化的语法写下步骤,然后将其翻译成所选编程语言。
我希望这对你有用!
/* 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);
}

负数当然是允许的...而且你的方向完全错了。你不能根据数字确定经度和纬度。你似乎把极坐标和DMS混淆了(它们完全不同...)。换句话说,你不能仅凭一个数字区分NE之间的差异。 - user578895
@cwolves 感谢您的评论。再读一些后,它没有任何意义...我应该有时间阅读自己的建议:“了解计算是如何完成的,然后才尝试实现它们”:/ - Aleadam
出现错误:如果由于四舍五入而使您的d++度超过了180度经度限制怎么办?如果它超过了90度纬度限制怎么办? - AlexWien
3
JavaScript 实现不适用于负数度数值。 - Jaco Briers
1
它不仅对负值无效,而且会给出错误的值。 - psiphi75
易于克服负值。将正数或负数存储在另一个变量(h,表示半球)中,然后运行Math.abs(deg)将其归一化为正数。然后在返回值中,包括h变量。这样,您的接收者可以查看h值,并确定它是纬度的N / S还是经度的E / W。这是一个很好的函数,只需要进行小的调整就可以变得更好。 - Michael

2

提供一种解决方案,可选指定输出秒数的小数位,并纠正由于四舍五入导致的秒和分钟边缘情况。

// @ 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"


2

尝试这个,完美运行!!!

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 

2
这个在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}`
  }

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);

}

输入:22.4229541515 输出:22:25:22.63


0
我很惊讶所有解决方案都使用了一些额外的逻辑来处理“四舍五入到60”这种情况(如果他们真正意识到它的存在),但是没有人想到过用另一种方式,从(四舍五入的)秒开始,然后使用mod和int-div,就不必担心所有这些问题了。
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
}

抱歉,这个没有包含N/S和E/W部分,需要一些额外的方法来调用它。

如果你想要秒级别的精度,可以使用以下代码:

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 + '"'
}

0

根据上面的答案,我已经将它们写成了JavaScript和PHP风格。

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;
}

0

无法让上面的脚本运行,在一段时间后得出了这个解决方案;直接给脚本提供DMS。

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;
}

反之亦然,只需将度数提供给脚本,并为纬度(lat)提供真或假的值

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;
}

希望这能帮助到大家。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接