各种坐标之间的转换方法汇总

  • 因项目需要,但网上一直没有找到合适的坐标转换方法,特别是js转换的,故咬咬牙,自己改写了,以备不时之需。
  • 其中js版是根据java版改的,如有问题,还请指正,目前js版亲测过百度坐标转原始坐标OK。

1、Java版:

package com.maple.util;
import java.math.BigDecimal;

/**
 * 功能:提供坐标偏移公共类
 */
public class CoordinateUtil {
    static double a = 6378245.0;
    static double ee = 0.00669342162296594323;
    static double pi = 3.14159265358979324;
    static double x_pi = 3.14159265358979324 * 3000.0 / 180.0;

    /**
     * GCJ02(国家局、高德、谷歌)转换为百度坐标
     *
     * @param x
     * @param y
     * @return double[2] xy
     */
    public static double[] bd_encrypt(double x, double y) {
        double z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
        double[] xy = new double[2];
        xy[0] = new BigDecimal(z * Math.cos(theta) + 0.0065).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
        xy[1] = new BigDecimal(z * Math.sin(theta) + 0.006).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
        return xy;
    }

    /**
     * 百度坐标转换为GCJ02(国家局、高德、谷歌)
     *
     * @param x
     * @param y
     * @return double[2] xy
     */
    public static double[] bd_decrypt(double x, double y) {
        x = x - 0.0065;
        y = y - 0.006;
        double z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        double theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        double[] xy = new double[2];
        xy[0] = new BigDecimal(z * Math.cos(theta)).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
        xy[1] = new BigDecimal(z * Math.sin(theta)).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
        return xy;
    }

    private static double transLat(double x, double y) {
        double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(x > 0 ? x : -x);
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
        return ret;
    }

    private static double transLon(double x, double y) {
        double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(x > 0 ? x : -x);
        ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
        return ret;
    }

    private static boolean outOfChina(double x, double y) {
        if (x < 72.004 || x > 137.8347)
            return true;
        if (y < 0.8293 || y > 55.8271)
            return true;
        return false;
    }

    /**
     * 原始坐标(WGS84)转换为GCJ02(国家局、高德、谷歌)
     *
     * @param x
     * @param y
     * @return double[2] xy
     */
    public static double[] WgsToGcj(double x, double y) {
        double[] gcjLoc = new double[2];
        if (outOfChina(x, y)) {
            gcjLoc[0] = x;
            gcjLoc[1] = y;
            return gcjLoc;
        }
        double dLat = transLat(x - 105.0, y - 35.0);
        double dLon = transLon(x - 105.0, y - 35.0);
        double radLat = y / 180.0 * pi;
        double magic = Math.sin(radLat);
        magic = 1 - ee * magic * magic;
        double sqrtMagic = Math.sqrt(magic);
        dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
        dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
        gcjLoc[1] = new BigDecimal(y + dLat).setScale(14, BigDecimal.ROUND_HALF_UP).doubleValue();
        gcjLoc[0] = new BigDecimal(x + dLon).setScale(14, BigDecimal.ROUND_HALF_UP).doubleValue();

        return gcjLoc;
    }

    /**
     * GCJ02(国家局、高德、谷歌)转换为原始坐标(WGS84)
     *
     * @param x
     * @param y
     * @return double[2] xy
     */
    public static double[] GcjToWgs(double x, double y) {
        double[] wgLoc = new double[2];
        double wgX = x, wgY = y;
        double dX, dY;
        double[] currGcLoc = new double[2];
        int maxCount = 100;
        int count = 0;
        while (true) {
            currGcLoc = WgsToGcj(wgX, wgY);
            dX = x - currGcLoc[0];
            dY = y - currGcLoc[1];
            if (Math.abs(dY) < 1e-5 && Math.abs(dX) < 1e-5) {  // 1e-6 ~ centimeter level accuracy
                // Result of experiment:
                //   Most of the time 2 iterations would be enough for an 1e-8 accuracy (milimeter level).
                //
                wgLoc[0] = new BigDecimal(wgX).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
                wgLoc[1] = new BigDecimal(wgY).setScale(6, BigDecimal.ROUND_HALF_UP).doubleValue();
                break;
            }
            wgX += dX;
            wgY += dY;
            if (count > maxCount) {//超过100次计算未满足条件,返回0
                wgLoc[0] = 0;
                wgLoc[1] = 0;
                break;
            }
            count++;
        }
        return wgLoc;
    }

    public static double EARTH_RADIUS = 6378137;

    private static double rad(double d) {
        return d * Math.PI / 180.0;
    }

    /**
     * 根据两点间经纬度坐标(double值),计算两点间距离,单位为米
     *
     * @param lng1
     * @param lat1
     * @param lng2
     * @param lat2
     * @return
     */
    public static double getDistance(double lng1, double lat1, double lng2, double lat2) {
        double radLat1 = rad(lat1);
        double radLat2 = rad(lat2);
        double a = radLat1 - radLat2;
        double b = rad(lng1) - rad(lng2);
        double s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
                Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
        s = s * EARTH_RADIUS;
        s = Math.round(s * 10000) / 10000;
        return s;
    }
}

2、js版:

/**
 * @author lyr
 * @date 2019/6/24
 * @Description: 坐标转换公共方法
 */
const a = 6378245.0;
const ee = 0.00669342162296594323;
const pi = 3.14159265358979324;
const x_pi = 3.14159265358979324 * 3000.0 / 180.0;
const EARTH_RADIUS = 6378137;

/**
 * GCJ02(国家局、高德、谷歌)转换为百度坐标
 */
export function bd_encrypt(x,y) {
    let z = Math.sqrt(x * x + y * y) + 0.00002 * Math.sin(y * x_pi);
    let theta = Math.atan2(y, x) + 0.000003 * Math.cos(x * x_pi);
    let xy = [];
    xy[0] = z * Math.cos(theta) + 0.0065;
    xy[1] = z * Math.sin(theta) + 0.006;
    return xy;
}

/**
 * 百度坐标转换为GCJ02(国家局、高德、谷歌)
 */
export function bd_decrypt(x,y) {
    x = x - 0.0065;
    y = y - 0.006;
    let z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
    let theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
    let xy = [];
    xy[0] = z * Math.cos(theta);
    xy[1] = z * Math.sin(theta);
    return xy;
}

/**
 * GCJ02(国家局、高德、谷歌)转换为原始坐标(WGS84)
 */
export function GcjToWgs(x,y) {
    let wgLoc = [];
    let wgX = x, wgY = y;
    let dX, dY;
    let currGcLoc = [];
    let maxCount = 100;
    let count = 0;
    while (true) {
        currGcLoc = WgsToGcj(wgX, wgY);
        dX = x - currGcLoc[0];
        dY = y - currGcLoc[1];
        if (Math.abs(dY) < 1e-5 && Math.abs(dX) < 1e-5) {  // 1e-6 ~ centimeter level accuracy
            wgLoc[0] = wgX;
            wgLoc[1] = wgY;
            break;
        }
        wgX += dX;
        wgY += dY;
        if (count > maxCount) {//超过100次计算未满足条件,返回0
            wgLoc[0] = 0;
            wgLoc[1] = 0;
            break;
        }
        count++;
    }
    return wgLoc;
}

/**
 * 原始坐标(WGS84)转换为GCJ02(国家局、高德、谷歌)
 */
export function WgsToGcj(x,y) {
    let gcjLoc = [];
    if (outOfChina(x, y)) {
        gcjLoc[0] = x;
        gcjLoc[1] = y;
        return gcjLoc;
    }
    let dLat = transLat(x - 105.0, y - 35.0);
    let dLon = transLon(x - 105.0, y - 35.0);
    let radLat = y / 180.0 * pi;
    let magic = Math.sin(radLat);
    magic = 1 - ee * magic * magic;
    let sqrtMagic = Math.sqrt(magic);
    dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);
    dLon = (dLon * 180.0) / (a / sqrtMagic * Math.cos(radLat) * pi);
    gcjLoc[1] = y + dLat;
    gcjLoc[0] = x + dLon;
    return gcjLoc;
}

export function transLat(x,y) {
    let ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.sqrt(x > 0 ? x : -x);
    ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(y * pi) + 40.0 * Math.sin(y / 3.0 * pi)) * 2.0 / 3.0;
    ret += (160.0 * Math.sin(y / 12.0 * pi) + 320 * Math.sin(y * pi / 30.0)) * 2.0 / 3.0;
    return ret;
}

export function transLon(x,y) {
    let ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.sqrt(x > 0 ? x : -x);
    ret += (20.0 * Math.sin(6.0 * x * pi) + 20.0 * Math.sin(2.0 * x * pi)) * 2.0 / 3.0;
    ret += (20.0 * Math.sin(x * pi) + 40.0 * Math.sin(x / 3.0 * pi)) * 2.0 / 3.0;
    ret += (150.0 * Math.sin(x / 12.0 * pi) + 300.0 * Math.sin(x / 30.0 * pi)) * 2.0 / 3.0;
    return ret;
}

export function outOfChina(x,y) {
    if (x < 72.004 || x > 137.8347)
        return true;
    if (y < 0.8293 || y > 55.8271)
        return true;
    return false;
}


export function rad(d) {
    return d * Math.PI / 180.0;
}

/**
 * 根据两点间经纬度坐标(double值),计算两点间距离,单位为米
 */
export function getDistance(lng1,lat1,lng2,lat2) {
    let radLat1 = rad(lat1);
    let radLat2 = rad(lat2);
    let a = radLat1 - radLat2;
    let b = rad(lng1) - rad(lng2);
    let s = 2 * Math.asin(Math.sqrt(Math.pow(Math.sin(a / 2), 2) +
        Math.cos(radLat1) * Math.cos(radLat2) * Math.pow(Math.sin(b / 2), 2)));
    s = s * EARTH_RADIUS;
    s = Math.round(s * 10000) / 10000;
    return s;
}

3、npm方式

安装:
npm install coordtransform
使用:
let coordtransform=require('coordtransform');
//国测局坐标(火星坐标,比如高德地图在用),百度坐标,wgs84坐标(谷歌国外以及绝大部分国外在线地图使用的坐标)
//百度经纬度坐标转国测局坐标
var bd09togcj02 = coordtransform.bd09togcj02(116.404, 39.915);
//国测局坐标转百度经纬度坐标
var gcj02tobd09 = coordtransform.gcj02tobd09(116.404, 39.915);
//wgs84转国测局坐标
var wgs84togcj02 = coordtransform.wgs84togcj02(116.404, 39.915);
//国测局坐标转wgs84坐标
var gcj02towgs84 = coordtransform.gcj02towgs84(116.404, 39.915);
console.log(bd09togcj02);
console.log(gcj02tobd09);
console.log(wgs84togcj02);
console.log(gcj02towgs84);
//result
//bd09togcj02:   [ 116.39762729119315, 39.90865673957631 ]
//gcj02tobd09:   [ 116.41036949371029, 39.92133699351021 ]
//wgs84togcj02:  [ 116.41024449916938, 39.91640428150164 ]
//gcj02towgs84:  [ 116.39775550083061, 39.91359571849836 ]
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值