专栏名称: SegmentFault思否
SegmentFault (www.sf.gg)开发者社区,是中国年轻开发者喜爱的极客社区,我们为开发者提供最纯粹的技术交流和分享平台。
目录
相关文章推荐
程序员的那些事  ·  快!快!快!DeepSeek 满血版真是快 ·  昨天  
OSC开源社区  ·  RAG市场的2024:随需而变,从狂热到理性 ·  昨天  
程序员的那些事  ·  OpenAI ... ·  2 天前  
程序员小灰  ·  3个令人惊艳的DeepSeek项目,诞生了! ·  2 天前  
程序员的那些事  ·  成人玩偶 + ... ·  5 天前  
51好读  ›  专栏  ›  SegmentFault思否

前端小知识——地图坐标转换

SegmentFault思否  · 公众号  · 程序员  · 2018-04-01 08:00

正文

LBS,基于位置的服务(Location Based Service),近年来已经无处不在,尤其是我们前端,相信或多或少都有接触一些地图API服务,比如高德、百度啊、谷歌啊~ 但是用的时候可能看到下面这些字眼:比如BD09、火星坐标、WGS84……不由得还是蒙圈了啊😵

那么接下来,我们就来了解一下,关于当前用到的一些互联网地图的基础坐标转换知识~

0、首先给大家出一个题

地图上的经纬度 转换到平面坐标时,和 平面坐标的XY 的对应关系是什么,就是经度(longitude)和维度(latitude)分别给对应X,Y中的谁?

这是在实际中经常会用到的一个知识点,我之前没有想太多,反正就把数值往里尝试,因为位置差异很大,正确还是错误一眼就看出来了的,但是这样其实很不好,被师兄说了,我一个GISer的连这个都弄不明白不应该,哈哈哈。不求甚解是可以的,但是专业性还是要强化的。

来看看上面的图:经纬度大家都知道,地球上横线是纬度,纵线是经度。这也导致了我们下意识就会觉得,横线是X,纵线是Y。这样的认知显然是错误的。

但其实,横线是刻画了Y轴上的刻度,纵线是刻画了X轴上的刻度,这里要用到投影的角度来看问题。

所以大家要记住 经纬XY

  • 经度 (longitude) —— 对应 X

  • 维度 ( latitude ) —— 对应 Y

一、当前互联网地图的坐标系现状

1.地球坐标 (WGS84)

国际标准,从专业 GPS 设备中取出的数据的坐标系。国际地图提供商使用的坐标系。

2.火星坐标 (GCJ-02)也叫国测局坐标系

中国标准,从国行移动设备中定位获取的坐标数据使用这个坐标系。

国家规定: 国内出版的各种地图系统(包括电子形式),必须至少采用GCJ-02对地理位置进行首次加密。

3.百度坐标 (BD-09)

百度标准,百度 SDK,百度地图,Geocoding 使用(百度在火星坐标上的二次加密)。

二、互联网在线地图使用的坐标系

火星坐标系:

  • iOS 地图(其实是高德)

  • Gogole地图

  • 搜搜、阿里云、高德地图

百度坐标系:当然只有百度地图

WGS84坐标系:国际标准,谷歌国外地图、osm地图等国外的地图一般都是这个。

三、从设备获取经纬度(GPS)坐标

如果使用的是百度sdk那么可以获得百度坐标(bd09)或者火星坐标(GCJ02),默认是bd09。

如果使用的是ios的原生定位库,那么获得的坐标是WGS84。

如果使用的是高德sdk,那么获取的坐标是GCJ02。

四、坐标转换方法--JS版本

我在之前的一篇文章里,基于Ionic框架的使用讲到了地图定位:ionic2入门教程(六)地图服务(谷歌、高德、百度定位),现在重新写一个小demo来实现我们的坐标转换。

关于方法,我找到了应该是最通用的一种,源码地址——作者wandergis,大部分的转换方式应该都是基于他的这个版本,相关说明也是最清楚的。

实际中我们可能会用到不同的地图,那么就对应到不同坐标系的转换,比如说,你有一份wgs84的数据服务,你要展现在百度或者高德地图上,这时候你就需要转换了。

我这里的例子是,我用到百度搜索地名,得到经纬度,但是我要将它绘制在以84为坐标系的地图leaflet之上,这时候我就需要将返回的经纬度进行转换。

我们先用百度搜索广州塔,定位中心

基于我们选择的OpenStreetMap,未转换之前,我们用百度搜索广州塔返回的值画点,可以看出很明显是偏移了的:

  1.    

    百度地名搜索

  2.     type="text" id="searchVal">

  3.    

  4.    

    id="map1" >
  5.    

  • var searchBtn = document.getElementById('searchBtn');

  • var mymap = L.map('map1').setView([39.897445, 116.331398], 13);

  • L.tileLayer(

  • 'https://api.tiles.mapbox.com/v4/{id}/{z}/{x}/{y}.png?access_token=pk.eyJ1IjoibWFwYm94IiwiYSI6ImNpejY4NXVycTA2emYycXBndHRqcmZ3N3gifQ.rJcFIG214AriISLbB6B5aw', {

  • maxZoom: 18,

  • attribution: 'Map data © href = "http://openstreetmap.org" > OpenStreetMap contributors, ' +

  • ' href = "http://creativecommons.org/licenses/by-sa/2.0/" > CC-BY-SA , ' +

  • 'Imagery © href = "http://mapbox.com" > Mapbox ',

  • id: 'mapbox.streets'

  • }).addTo(mymap);

  • // 创建地址解析器实例

  • searchBtn.onclick = function () {

  • var searchVal = document.getElementById('searchVal').value;

  • var myGeo = new BMap.Geocoder();

  • // 将地址解析结果显示在地图上,并调整地图视野

  • myGeo.getPoint(searchVal, function (point) {

  • if (point) {

  • console.log(point);

  • L.marker([point.lat,point.lng]).addTo(mymap);

  • mymap.setView([point.lat,point.lng],15);

  • } else {

  • alert("您选择地址没有解析到结果!");

  • }

  • }, "广州市");

  • }

  • 转换代码如下:

    1. /**

    2. * copy from https://github.com/wandergis/coordtransform

    3. * Created by Wandergis on 2015/7/8.

    4. * 提供了百度坐标(BD09)、国测局坐标(火星坐标,GCJ02)、和WGS84坐标系之间的转换

    5. */

    6. //UMD魔法代码

    7. // if the module has no dependencies, the above pattern can be simplified to

    8. (function ( root, factory) {

    9.    if (typeof define === 'function' && define.amd) {

    10.      // AMD. Register as an anonymous module.

    11.      define([], factory);

    12.    } else if (typeof module === 'object' && module.exports) {

    13.      // Node. Does not work with strict CommonJS, but

    14.      // only CommonJS-like environments that support module.exports,

    15.      // like Node.

    16.      module.exports = factory();

    17.    } else {

    18.      // Browser globals (root is window)

    19.      root.coordtransform = factory();

    20.    }

    21.  }(this, function () {

    22.    //定义一些常量

    23.    var x_PI = 3.14159265358979324 * 3000.0 / 180.0;

    24.    var PI = 3.1415926535897932384626;

    25.    var a = 6378245.0;

    26.    var ee = 0.00669342162296594323;

    27.    /**

    28.     * 百度坐标系 (BD-09) 与 火星坐标系 (GCJ-02)的转换

    29.     * 即 百度 转 谷歌、高德

    30.     * @param bd_lon

    31.     * @param bd_lat

    32.     * @returns {*[]}

    33.     */

    34.    var bd09togcj02 = function bd09togcj02(bd_lon, bd_lat) {

    35.      var bd_lon = +bd_lon;

    36.      var bd_lat = +bd_lat;

    37.      var x = bd_lon - 0.0065;

    38.      var y = bd_lat - 0.006;

    39.      var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI);

    40.      var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI);

    41.      var gg_lng = z * Math.cos(theta);

    42.      var gg_lat = z * Math.sin(theta);

    43.      return [gg_lng, gg_lat]

    44.    };

    45.    /**

    46.     * 火星坐标系 (GCJ-02) 与百度坐标系 (BD-09) 的转换

    47.     * 即谷歌、高德 转 百度

    48.     * @param lng

    49.     * @param lat

    50.     * @returns {*[]}

    51.     */

    52.    var gcj02tobd09 = function gcj02tobd09(lng, lat) {

    53.       var lat = +lat;

    54.      var lng = +lng;

    55.      var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);

    56.      var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);

    57.      var bd_lng = z * Math.cos(theta) + 0.0065;

    58.      var bd_lat = z * Math.sin(theta) + 0.006;

    59.      return [bd_lng, bd_lat]

    60.    };

    61.    /**

    62.     * WGS84转GCj02

    63.     * @param lng

    64.     * @param lat

    65.     * @returns {*[]}

    66.     */

    67.    var wgs84togcj02 = function wgs84togcj02(lng, lat) {

    68.      var lat = +lat;

    69.      var lng = +lng;

    70.      if (out_of_china(lng, lat)) {

    71.        return [lng, lat]

    72.      } else {

    73.        var dlat = transformlat(lng - 105.0, lat - 35.0);

    74.        var dlng = transformlng(lng - 105.0, lat - 35.0);

    75.        var radlat = lat / 180.0 * PI;

    76.        var magic = Math.sin(radlat);

    77.        magic = 1 - ee * magic * magic;

    78.        var sqrtmagic = Math.sqrt (magic);

    79.        dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);

    80.        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);

    81.        var mglat = lat + dlat;

    82.        var mglng = lng + dlng;

    83.        return [mglng, mglat]

    84.      }

    85.     };

    86.    /**

    87.     * GCJ02 转换为 WGS84

    88.     * @param lng

    89.     * @param lat

    90.     * @returns {*[]}

    91.     */

    92.    var gcj02towgs84 = function gcj02towgs84(lng, lat) {

    93.      var lat = +lat;

    94.      var lng = +lng;

    95.      if (out_of_china(lng, lat)) {

    96.        return [lng, lat]

    97.      } else {

    98.        var dlat = transformlat(lng - 105.0, lat - 35.0);

    99.        var dlng = transformlng(lng - 105.0, lat - 35.0);

    100.        var radlat = lat / 180.0 * PI;

    101.        var magic = Math.sin(radlat);

    102.        magic = 1 - ee * magic * magic;

    103.        var sqrtmagic = Math.sqrt(magic);

    104.        dlat = (dlat * 180.0) / ((a * (1 - ee )) / (magic * sqrtmagic) * PI);

    105.        dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);

    106.        var mglat = lat + dlat;

    107.        var mglng = lng + dlng;

    108.        return [lng * 2 - mglng, lat * 2 - mglat]

    109.      }

    110.    };

    111.    var transformlat = function transformlat( lng, lat) {

    112.      var lat = +lat;

    113.      var lng = +lng;

    114.      var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));

    115.      ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin (2.0 * lng * PI)) * 2.0 / 3.0;

    116.      ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;

    117.      ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;

    118.      return ret

    119.    };

    120.    var transformlng = function transformlng(lng, lat) {

    121.      var lat = +lat;

    122.      var lng = +lng;

    123.      var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng *







    请到「今天看啥」查看全文