地图坐标系及相互转换

5,220 阅读5分钟

一、坐标系种类

1、WGS84

为一种大地坐标系,也是目前广泛使用的GPS全球卫星定位系统使用的坐标系。法律规定,不支持将任何一种坐标系坐标转换为WGS84类型

使用者:高德、谷歌、腾讯等海外地区、警用地理信息系统(Police Geographic Information System)

2、GCJ02

又称火星坐标系,是由中国国家测绘局制订的地理信息系统的坐标系统。由WGS84坐标系经加密后的坐标系。

使用者:高德、腾讯、谷歌

3、BD09

百度坐标系,在GCJ02坐标系基础上再次加密。其中bd09ll表示百度经纬度坐标,bd09mc表示百度墨卡托米制坐标。

使用者:百度

4、CGCS2000坐标系:

国家大地坐标系,该坐标系是通过中国GPS 连续运行基准站、 空间大地控制网以及天文大地网与空间地网联合平差建立的地心大地坐标系统。

二、坐标系转换

1、百度地图

境内:使用BD09(BD09LL百度经纬度坐标),支持WGS84、GCJ02转换成BD09,反向不支持,并且批量转换一次有条数限制(单次请求可批量解析100个坐标)

境外:使用WGS-84

转换API文档

lbsyun.baidu.com/index.php?t…

demo

lbsyun.baidu.com/jsdemo.htm#…

2、高德地图

境内:GCJ02, 支持WGS84转换过成GCJ02

境外:WGS84

转换API文档

lbs.amap.com/api/webserv…

3、腾讯地图

境内:GCJ02,支持其他坐标系批量转换批量转换

境外:WGS84

4、GPS坐标转换API(需要外网并且收费)

www.gpsspg.com/api/convert…

5、openlayer5

EPSG:4326,等同于WGS84坐标系

EPSG:3857(默认),又名球形墨卡托或Web墨卡托,是投影坐标系

转换API文档

openlayers.org/en/latest/a…

三、什么是PGIS

警用地理信息系统(Police Geographic Information System,公安部项目初始建设是始于2003年。PGIS使用WGS84坐标系

四、PGIS(WGS84)转换为百度坐标

转换API文档

lbsyun.baidu.com/index.php?t…

demo

lbsyun.baidu.com/jsdemo.htm#…

五、坐标系间相互转换方法

1、百度地图例子
import React from 'react'
import logo from './logo.svg'
import mapStyle from './custom_map_config.json'
import Coordtransform from './coordtransform'
import './App.css'

const { BMap } = window

class App extends React.Component {
  constructor(props) {
    super(props)

    this.loadMap = this.loadMap.bind(this)
    // WGS84坐标
    this.wgs = {lng: 116.39117368075213, lat: 39.9073767845022}
    // BD09坐标
    this.bd = {lng: 116.40387397, lat: 39.91488908}
  }
  componentDidMount() {
    this.loadMap()
  }

  converCallback = (map) => (data) => {
    if(data.status === 0) {
      var marker = new BMap.Marker(data.points[0])
      map.addOverlay(marker)
      var label = new BMap.Label("百度API转换(正确)",{offset:new BMap.Size(20,-10)})
      marker.setLabel(label) //添加百度label
      map.setCenter(data.points[0])
    }
  }

  loadMap() {
    // 加载地图
    let map = new BMap.Map("map-container")
    let centerPoint = new BMap.Point(116.404, 39.920)
    map.centerAndZoom(centerPoint, 16)
    map.enableScrollWheelZoom(true)
    
    // 设置样式
    // map.setMapStyle({ styleJson: mapStyle })
    
    // 移动和缩放控件
    map.addControl(new BMap.NavigationControl())

    // -------------- 转换坐标开始------------
    
    // 添加WGS点位(对应百度天安门位置)
    let pointWGS = new BMap.Point(this.wgs.lng,this.wgs.lat)
    let markerWGS = new BMap.Marker(pointWGS)
    map.addOverlay(markerWGS)
    let labelWGS = new BMap.Label("原始WGS84坐标",{offset:new BMap.Size(20,-10)})
    markerWGS.setLabel(labelWGS)

    // ----------------自定义方法 WGS84->GCJ02->BD09开始 -------------
    const coord = Coordtransform.wgs84ToGcj02(this.wgs.lng,this.wgs.lat)
    const coord1 = Coordtransform.gcj02ToBd09(coord[0], coord[1])
    let pointWGS1 = new BMap.Point(coord1[0],coord1[1])
    let markerWGS1 = new BMap.Marker(pointWGS1)
    map.addOverlay(markerWGS1)
    let labelWGS1 = new BMap.Label("方法转换已转换的GPS坐标(正确)------------前面被挡住了---方法转换WGS84->GCJ02->BD09坐标(正确)",{offset:new BMap.Size(20,-10)})
    markerWGS1.setLabel(labelWGS1)
    // ----------------自定义方法 WGS84->GCJ02->BD09结束 -------------


    // ----------------自定义方法 BD09开始->GCJ02->WGS84开始 -------------
    const coordbw = Coordtransform.bd09ToGcj02(this.bd.lng,this.bd.lat)
    const coordbw1 = Coordtransform.gcj02ToWgs84(coordbw[0], coordbw[1])
    let point2 = new BMap.Point(coordbw1[0],coordbw1[1])
    let marker2 = new BMap.Marker(point2)
    map.addOverlay(marker2)
    let label2 = new BMap.Label("BD09开始->GCJ02->WGS84(正确)------------前面被挡住了---方法转换BD09->GCJ02->WGS84坐标(正确但是有偏移)",{offset:new BMap.Size(20,-10)})
    marker2.setLabel(label2)
    // ----------------自定义方法 BD09开始->GCJ02->WGS84结束 -------------

    // ----------------百度API WGS84->GCJ02->BD09开始 -------------
    const convertor = new BMap.Convertor()
    const pointArr = []
    pointArr.push(pointWGS)
    convertor.translate(pointArr, 1, 5, this.converCallback(map))

     // ----------------百度API WGS84->GCJ02->BD09结束 -------------

    // ---------------转换坐标结束------------


    // 交通情况
    // var traffic = new BMap.TrafficLayer()
    // map.addTileLayer(traffic)
  }

  render() {
    return (
      <div className="App">
        <h2>Baidu Map Demo</h2>
        <button onClick={this.loadMap}>Load Map</button>
        <div id="map-container"></div>
      </div>
    )
  }
}

export default App

2、自定义方法

测试下面代码

WGS84->GCJ02->BD09准确

BD09->GCJ02->WGS84稍有偏移

/*
 * 功能: 提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换
 * 日期: 2019-08-23 10:19:02 
 * 参考:https://www.cnblogs.com/telwanggs/p/10410578.html
 */

// 定义一些常量
 const x_PI = 3.14159265358979324 * 3000.0 / 180.0
 const PI = 3.1415926535897932384626
const a = 6378245.0
const ee = 0.00669342162296594323

class Coordtransform {
    /**
   * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换
   * 即 百度 转 谷歌、高德
   * @param bd_lng
   * @param bd_lat
   * @returns {*[]}
   */
  static bd09ToGcj02(bd_lngString, bd_latString) {
        const bd_lng = +bd_lngString
        const bd_lat = +bd_latString
        const x = bd_lng - 0.0065
        const y = bd_lat - 0.006
        const z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI)
        const theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI)
        const gg_lng = z * Math.cos(theta)
        const gg_lat = z * Math.sin(theta)
        return [gg_lng, gg_lat]
    }

    /**
   * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换
   * 即谷歌、高德 转 百度
   * @param lng
   * @param lat
   * @returns {*[]}
   */
  static gcj02ToBd09(lngString, latString) {
        const lng = Number(lngString)
        const lat = Number(latString)
        const z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI)
        const theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI)
        const bd_lng = z * Math.cos(theta) + 0.0065
        const bd_lat = z * Math.sin(theta) + 0.006
        return [bd_lng, bd_lat]
    }

    /**
   * WGS84转GCj02
   * @param lng
   * @param lat
   * @returns {*[]}
   */
    static wgs84ToGcj02(lngString, latString) {
        const lng = Number(lngString)
        const lat = Number(latString)
        if (this.outOfChina(lng, lat)) {
            return [lng, lat]
        } 
        let dlat = this.transformLat(lng - 105.0, lat - 35.0)
        let dlng = this.transformLng(lng - 105.0, lat - 35.0)
        const radlat = lat / 180.0 * PI
        let magic = Math.sin(radlat)
        magic = 1 - ee * magic * magic
        const sqrtmagic = Math.sqrt(magic)
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI)
        const mglat = lat + dlat
        const mglng = lng + dlng
        return [mglng, mglat]
    }

    /**
   * GCJ02 转换为 WGS84
   * @param lng
   * @param lat
   * @returns {*[]}
   */
  static gcj02ToWgs84(lngString, latString) {
        const lng = Number(lngString)
        const lat = Number(latString)
        if (this.outOfChina(lng, lat)) {
            return [lng, lat]
        } 
        let dlat = this.transformLat(lng - 105.0, lat - 35.0)
        let dlng = this.transformLng(lng - 105.0, lat - 35.0)
        const radlat = lat / 180.0 * PI
        let magic = Math.sin(radlat)
        magic = 1 - ee * magic * magic
        const sqrtmagic = Math.sqrt(magic)
        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI)
        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI)
        const mglat = lat + dlat
        const mglng = lng + dlng
        return [lng * 2 - mglng, lat * 2 - mglat]
    }

    static transformLat(lngString, latString) {
        const lng = Number(lngString)
        const lat = Number(latString)
        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
    }

    static transformLng(lngString, latString) {
        const lng = Number(lngString)
        const lat = Number(latString)
        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
    }

    /**
   * 判断是否在国内,不在国内则不做偏移
   * @param lng
   * @param lat
   * @returns {boolean}
   */
  static outOfChina(lngString, latString) {
    const lng = Number(lngString)
    const lat = Number(latString)
        // 纬度3.86~53.55,经度73.66~135.05
        return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55)
    }
}

export default Coordtransform

本文参考代码链接: www.cnblogs.com/telwanggs/p…