Skip to content

坐标系转换

约 1352 字大约 5 分钟

MapsWGS84GCJ02BD09

2025-01-13

介绍

地球上同一个地理位置的经纬度,在不同的坐标系中,会有少许偏移,国内目前常见的坐标系主要分为三种:

  • 地球坐标系——WGS84:常见于 GPS 设备,Google 地图等国际标准的坐标体系。
  • 火星坐标系——GCJ-02:中国国内使用的被强制加密后的坐标体系,高德坐标就属于该种坐标体系。
  • 百度坐标系——BD-09:百度地图所使用的坐标体系,是在火星坐标系的基础上又进行了一次加密处理。

注意 虽然偏差不多,但是不转换的话,还是影响高精度使用的。实测WGS84GCJ-02差距至少小几十米的偏差。

坐标系转换

高德地图

高德提供了将其他坐标系经纬度转换为高德坐标系的接口或SDK API,但是没有提供将高德坐标系转换为其他坐标系的接口。因为国家测绘局规定,中国互联网地图必须使用至少加密一次的GCJ-02坐标

高德开放平台服务用的是国测局规定的GCJ-02坐标系。为国内的标准坐标体系。区别于GPS坐标(WGS-84坐标系),是在其基础上进行了加密。我们可以点击使用高德坐标拾取器,查看高德坐标。

其他坐标系坐标与高德坐标的偏差

可以查看JS API 其他坐标系坐标转高德坐标示例示例和JS API 其他坐标系坐标转高德坐标(批量)示例示例,来查看其他坐标系坐标与高德坐标的偏差。并且可以在这里测试。

其他坐标系坐标转高德坐标

因此在使用高德坐标系前,我们需要使用 AMap.convertFrom() 方法将这些非高德坐标系转换为高德坐标系。

使用AMap.convertFrom() 方法将其他坐标系经纬度转换为高德坐标系,需要传入两个参数,第一个参数是经纬度数组,第二个参数是源坐标系类型。

var gps = [116.3, 39.9];
AMap.convertFrom(gps, 'gps', function (status, result) {
  if (result.info === 'ok') {
    var lnglats = result.locations; // Array.<LngLat>
  }
});

使用WEB API将其他坐标系经纬度转成高德坐标系经纬度。

注意 API使用调用量限制的。

高德坐标转其他坐标系坐标

虽然高德没有提供将高德坐标系转换为其他坐标系的接口,但社区有一些非官方的解决方案。

社区的转换算法有多种,下面是一种社区提供的: GCJ02 转换为 WGS84WGS84 转换为 GCJ02转换算法如下:

/*
 * @Author: matiastang
 * @Date: 2024-12-12 16:57:44
 * @LastEditors: matiastang
 * @LastEditTime: 2024-12-12 16:59:29
 * @FilePath: /ReactNativeMapbox/src/utils/convert.ts
 * @Description: convert
 */

/**
 * 高德地图坐标转GPS坐标算法
 */

//定义一些常量
const PI = 3.1415926535897932384626;
const a = 6378245.0;  //长半轴
const ee = 0.00669342162296594323; //扁率

/**
 * GCJ02 转换为 WGS84
 * @param lng
 * @param lat
 * @returns {*[]}
 */
function gcj02towgs84(lng: number, lat: number) {
  lat = +lat;
  lng = +lng;
  if (out_of_china(lng, lat)) {
    return [lng, lat];
  } else {
    let dlat = transformlat(lng - 105.0, lat - 35.0);
    let dlng = transformlng(lng - 105.0, lat - 35.0);
    let radlat = lat / 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);
    dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
    let mglat = lat + dlat;
    let mglng = lng + dlng;
    return [lng * 2 - mglng, lat * 2 - mglat];
  }
}

/**
 * WGS84 转换为 GCJ02
 * @param lng
 * @param lat
 * @returns {*[]}
 */
function wgs84togcj02(lng: number, lat: number) {
  lat = +lat;
  lng = +lng;
  if (out_of_china(lng, lat)) {
    return [lng, lat];
  } else {
    let dlat = transformlat(lng - 105.0, lat - 35.0);
    let dlng = transformlng(lng - 105.0, lat - 35.0);
    let radlat = lat / 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);
    dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
    return [lng + dlng, lat + dlat];
  }
}

/**
 * 判断是否在国内,不在国内则不做偏移
 * @param lng
 * @param lat
 * @returns {boolean}
 */
function out_of_china(lng: number, lat: number) {
  lat = +lat;
  lng = +lng;
  // 纬度3.86~53.55,经度73.66~135.05
  return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55);
}

function transformlat(lng: number, lat: number) {
  lat = +lat;
  lng = +lng;
  let ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
  ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
  ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
  ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
  return ret;
}

function transformlng(lng: number, lat: number) {
  lat = +lat;
  lng = +lng;
  let ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
  ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
  ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
  ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
  return ret;
}

export {
  gcj02towgs84,
  wgs84togcj02,
};

高德坐标转换为WGS84坐标的方法gcj02towgs84。我测试过,效果还不错,挺准的。

注意 这个转换方式,有一个明显的问题,就是判断是否在国内的方法,用的是一个包含中国全境的矩形来粗略估计,这是个明显的问题。

参考

JS API 其他坐标系坐标转高德坐标示例

JS API 其他坐标系坐标转高德坐标(批量)示例

高德开放平台坐标系转换API

高德开放平台坐标系转换SDK

高德坐标拾取器

高德开放平台

百度坐标拾取器