简易GIS度分秒与经纬度转换算法

2,074 阅读3分钟

在GIS项目中,时常遇见客户提供手持GPS设备记录的度分秒数据导入项目中,目前成熟第三方GIS平台大量使用EPSG:3857(墨卡托平台坐标),如果遇见客户手持GPS设备度分秒数据导入现有项目,肯定会有大量换算工作,本章公开度分秒与经纬度转换简易算法,帮助类似需求的程序员工作中如鱼得水。

首先我们先看原理,首先以南极和北极的垂直线作为圆心,地球则分为360°,以本初子午线向西和向东各180°,每一度的距离111公里左右,实际距离根据实地椭球体不同和纬度不同变化而变化,这里不讨论具体距离,每1°之间的距离则以分(')和秒('')划分,以赤道的水平线为起始线,向北和向南各90°, 明白这个道理,我们以北京天坛的祈年殿(116.512885, 39.847469)作为实验对象。

var lon = 116.512885;
var lat = 39.847469;

function transformLonlatToDD(coordinate) {
     var d = Math.floor(coordinate); //116.512885 转换成度(°)实则是取整
     var m = Math.floor(coordinate % 1 * 60); //0.512885 转换成分(') 实则是0.512885 * 60 后取整。
     var s = Math.floor(coordinate % 1 * 60 % 1 * 60); //0.512885 转换成秒('') 实则是m 值的小数值 * 60后取整。
     var ms = coordinate % 1 * 60 % 1 * 60 % 1 * 1000; //0.512885 秒('')值后面如有小数值不能丢舍。
     return [d, m, s, ms];
}
/**
* 上述算法看似没有问题,但是我们参考的数值是北京祈年殿的经度(116.512885)坐标值,
* 因为中国坐标几乎都处于正经纬度值,如赤道以南的坐标则会出现-28.847469这种情况,
* 如果我们还采用Math.floor()函数向下取整,Math.floor(-28.847469),则取值是-29!!!
* 按上述转换规则,-28.847469应该是-28度,这将会是算法上严重bug。
* 所以我们可以优化算法。
*/

function transformLonlatToDD(coordinate) {
    //利用 >> 位运算符取整, 如果值是 -28.847469 >> 0, 输出则是-28,Perfect!
    var d = coordinate >> 0;
    var m = coordinate % 1 * 60 >> 0;
    var s = coordinate % 1 * 60 % 1 * 60 >> 0;
    var ms = coordinate % 1 * 60 % 1 * 60 % 1 * 1000;
    return [d, m, s, ms];
}
/**
* 上述算法完美解决了我们的经度转换成度分秒能力
* 下面我们在来处理度分秒转换经纬度
**/

function transformDDToLonlat(d, m, s, ms = 0.0) {
    var lonlat = 0.0;
    //按transformLonlatToDD的运算结果ms应该0.xxxxx放大了1000倍123.xxxxx, 所以 ms / 1000,保证原值
    lonlat = ms / 1000;
    lonlat = (lonlat + s) / 60;
    lonlat = (lonlat + m) / 60;
    lonlat += d;
    return lonlat;
}
/**
* 为了方便解释和利于阅读,将度分秒转换经纬度的简易算法逐步拆开
* 下面将利用优化两个函数,锦上添花的工作!
**/

function transformLonlatToDD(c) {
    return [
        c >> 0,
        c % 1 * 60 >> 0,
        c % 1 * 60 % 1 * 60 >> 0,
        c % 1 * 60 % 1 * 60 % 1 * 1000
    ]
}

function transformDDToLonlat(d, m, s, ms = 0.0) {
    return ((ms / 1000 + s) / 60 + m) / 60 + d;
}

最后,相信大家已明白经纬度转换的原理,同时大家应该也发现了精度的问题,这是所有开发语言在 double & float 上使用/运算符导致精度丢失的问题,本文章就不处理了,这得八仙过海,各显神通了!