前端小知識——地圖坐標轉換

2020-11-29 長沙課工場實力

LBS,基於位置的服務(Location Based Service),近年來已經無處不在,尤其是我們前端,相信或多或少都有接觸一些地圖API服務,比如高德、百度啊、谷歌啊~

但是用的時候可能看到下面這些字眼:比如BD09、火星坐標、WGS84……不由得還是蒙圈了啊

那麼接下來,我們就來了解一下,關於當前用到的一些網際網路地圖的基礎坐標轉換知識~

1、首先給大家出一個題

地圖上的經緯度轉換到平面坐標時,和平面坐標的XY的對應關係是什麼,就是經度(longitude)和維度(latitude)分別給對應X,Y中的誰?

這是在實際中經常會用到的一個知識點,我之前沒有想太多,反正就把數值往裡嘗試,因為位置差異很大,正確還是錯誤一眼就看出來了的,但是這樣其實很不好,被師兄說了,我一個GISer的連這個都弄不明白不應該,哈哈哈。

不求甚解是可以的,但是專業性還是要強化的。

來看看上面的圖:經緯度大家都知道,地球上橫線是緯度,縱線是經度。這也導致了我們下意識就會覺得,橫線是X,縱線是Y。這樣的認知顯然是錯誤的。

但其實,橫線是刻畫了Y軸上的刻度,縱線是刻畫了X軸上的刻度,這裡要用到投影的角度來看問題。

所以大家要記住經緯XY:

經度 (longitude) —— 對應 X

維度 ( latitude ) —— 對應 Y

2、當前網際網路地圖的坐標系現狀

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,未轉換之前,我們用百度搜索廣州塔返回的值畫點,可以看出很明顯是偏移了的:

<h1>百度地名搜索</h1> <input type="text" id="searchVal"> <button id="searchBtn">廣州市內搜索</button> <div id="map1"> </div> <script> 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 &copy; <a href="http://openstreetmap.org">OpenStreetMap</a> contributors, ' + '<a href="http://creativecommons.org/licenses/by-sa/2.0/">CC-BY-SA</a>, ' + 'Imagery <a href="http://mapbox.com">Mapbox</a>', 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("您選擇地址沒有解析到結果!"); } }, "廣州市"); }

轉換代碼如下:

/** * copy from https://github.com/wandergis/coordtransform * Created by Wandergis on 2015/7/8. * 提供了百度坐標(BD09)、國測局坐標(火星坐標,GCJ02)、和WGS84坐標系之間的轉換 *///UMD魔法代碼// if the module has no dependencies, the above pattern can be simplified to(function (root, factory) { if (typeof define === 'function' && define.amd) { // AMD. Register as an anonymous module. define([], factory); } else if (typeof module === 'object' && module.exports) { // Node. Does not work with strict CommonJS, but // only CommonJS-like environments that support module.exports, // like Node. module.exports = factory(); } else { // Browser globals (root is window) root.coordtransform = factory(); } }(this, function () { //定義一些常量 var x_PI = 3.14159265358979324 * 3000.0 / 180.0; var PI = 3.1415926535897932384626; var a = 6378245.0; var ee = 0.00669342162296594323; /** * 百度坐標系 (BD-09) 與 火星坐標系 (GCJ-02)的轉換 * 即 百度 轉 谷歌、高德 * @param bd_lon * @param bd_lat * @returns {*[]} */ var bd09togcj02 = function bd09togcj02(bd_lon, bd_lat) { var bd_lon = +bd_lon; var bd_lat = +bd_lat; var x = bd_lon - 0.0065; var y = bd_lat - 0.006; var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_PI); var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_PI); var gg_lng = z * Math.cos(theta); var gg_lat = z * Math.sin(theta); return [gg_lng, gg_lat] }; /** * 火星坐標系 (GCJ-02) 與百度坐標系 (BD-09) 的轉換 * 即谷歌、高德 轉 百度 * @param lng * @param lat * @returns {*[]} */ var gcj02tobd09 = function gcj02tobd09(lng, lat) { var lat = +lat; var lng = +lng; var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI); var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI); var bd_lng = z * Math.cos(theta) + 0.0065; var bd_lat = z * Math.sin(theta) + 0.006; return [bd_lng, bd_lat] }; /** * WGS84轉GCj02 * @param lng * @param lat * @returns {*[]} */ var wgs84togcj02 = function wgs84togcj02(lng, lat) { var lat = +lat; var lng = +lng; if (out_of_china(lng, lat)) { return [lng, lat] } else { var dlat = transformlat(lng - 105.0, lat - 35.0); var dlng = transformlng(lng - 105.0, lat - 35.0); var radlat = lat / 180.0 * PI; var magic = Math.sin(radlat); magic = 1 - ee * magic * magic; var sqrtmagic = Math.sqrt(magic); dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); var mglat = lat + dlat; var mglng = lng + dlng; return [mglng, mglat] } }; /** * GCJ02 轉換為 WGS84 * @param lng * @param lat * @returns {*[]} */ var gcj02towgs84 = function gcj02towgs84(lng, lat) { var lat = +lat; var lng = +lng; if (out_of_china(lng, lat)) { return [lng, lat] } else { var dlat = transformlat(lng - 105.0, lat - 35.0); var dlng = transformlng(lng - 105.0, lat - 35.0); var radlat = lat / 180.0 * PI; var magic = Math.sin(radlat); magic = 1 - ee * magic * magic; var sqrtmagic = Math.sqrt(magic); dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI); dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI); var mglat = lat + dlat; var mglng = lng + dlng; return [lng * 2 - mglng, lat * 2 - mglat] } }; var transformlat = function transformlat(lng, lat) { var lat = +lat; var lng = +lng; 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)); 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 }; var transformlng = function transformlng(lng, lat) { var lat = +lat; var lng = +lng; var 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} */ var out_of_china = function out_of_china(lng, lat) { var lat = +lat; var lng = +lng; // 緯度3.86~53.55,經度73.66~135.05 return !(lng > 73.66 && lng < 135.05 && lat > 3.86 && lat < 53.55); }; return { bd09togcj02: bd09togcj02, gcj02tobd09: gcj02tobd09, wgs84togcj02: wgs84togcj02, gcj02towgs84: gcj02towgs84 } }));

坐標轉換後顯示如下,我們需要將百度坐標轉成火星坐標再轉成wgs84,因為我們用的地圖是openstreet,是wgs84坐標系。

可以看到,下面中三個點中,有一個已經是正確了的。

myGeo.getPoint(searchVal, function (point) {

if (point) {

console.log(point);

// bd09->gcj02

var myPoint = coordtransform.bd09togcj02(point.lng, point.lat);

console.log(myPoint);

// gcj02->wgs84

var myPoint2 = coordtransform.gcj02towgs84(myPoint[0], myPoint[1]);

console.log(myPoint2);

var latlng = L.latLng([myPoint[1], myPoint[0]]);

var latlng2 = L.latLng([myPoint2[1], myPoint2[0]]);

// 畫點

L.marker(point).addTo(mymap);

L.marker(latlng).addTo(mymap);

L.marker(latlng2).addTo(mymap);

// 設置中心

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

} else {

alert("您選擇地址沒有解析到結果!");

}

}, "廣州市");

3、EPSG:3857

如果你用到了leaflet/openlayers/arcgis jsAPI的話,應該還有一個點需要了解。

這個算是題外話,因為一般都是學gis的才會用到這些,一般情況下百度高德這些大概都能夠滿足需求了。

像用到這些地圖的情況,經常會涉及到EPSG:3857 或者OpenLayers:900913,acrgis: 102100(3857)。

EPSG:3857 其實是EPSG協會(European Petroleum Survey Group)為 Web Wercator 最終設立的WKID,也就是現在我們常用的Web 地圖的坐標系,並且給定官方命名 「WGS 84 / Pseudo-Mercator「。

Web Mercator 是一個投影坐標系統,其基準面是 WGS 1984 。

WGS 1984 是一個長半軸(a)為6378137,短半軸(b)為6356752.314245179 的橢球體,扁率(f)為298.257223563,f=(a-b)/a 。

但是,Web Mercator 坐標系使用的投影方法不是嚴格意義的墨卡託投影而是一個被 EPSG稱為偽墨卡託的投影方法,這個偽墨卡託投影方法的大名是 Popular Visualization Pseudo Mercator,PVPM。

Google 最先發明了這套系統,在投影過程中,將表示地球的參考橢球體近似的作為正球體處理(正球體半徑 R = 橢球體半長軸 a)。

後來,Web Mercator 在 Web 地圖領域被廣泛使用,這個坐標系就名聲大噪。儘管這個坐標系由於精度問題一度不被GIS專業人士接受,但最終 EPSG 還是給了 WKID:3857。

所以其實看到EPSG:3857,就知道,當前的坐標系是wgs84,而這個屬性,通常在地圖的默認設置中。就是說,如果你不改,這些地圖就應該是wgs84坐標系。

實力小編說

如果對你有幫助的話,能否考慮打賞a cup of coffee,筆芯喲~

相關焦點

  • 解放軍啟用新版地圖 用地心坐標系解決轉換時間
    原標題:解放軍啟用新版地圖 用地心坐標系解決轉換時間   日前有媒體報導,我軍已開始啟用新版作戰地圖,這是我軍近30年來首次對單一圖種進行全面更換。作戰地圖對軍事行動來說至關重要,伴隨各大單位新版地圖發放工作的完成,我軍作戰人員的訓練水平將會得到進一步提升。
  • 高德,百度,Google地圖定位偏移以及坐標系轉換
    一、在進行地圖開發過程中,我們一般能接觸到以下三種類型的地圖坐標系:     1.WGS-84原始坐標系       一般用國際GPS
  • 【技巧】利用谷歌地圖,將經緯度轉換為XY坐標,導入CAD製圖
    這次來講講如何將GOOGLE地圖製作成CAD地圖。1、打開谷歌地圖,找到要製作的區域,比如這個水庫:2、找到標尺圖標,因為是製作一個封閉的水庫,所以選擇「多邊形」,如果是製作道路,就選擇「路徑」:14、這樣就得到了分行好的各點大地坐標,方面我們批量轉換成平面坐標。
  • 利用谷歌地圖,將經緯度轉換為xy坐標,導入CAD製圖
    ,將經緯度轉換為xy坐標,導入CAD製圖。1、打開谷歌地圖,找到要製作的區域,比如這個水庫:14、這樣就得到了分行好的各點大地坐標,方面我們批量轉換成平面坐標。18、打開coord4.0,源格式選擇大地坐標(度),選擇「文件轉換」
  • Google地圖經緯度與平面坐標的相互轉換、簡單地圖Demo
    接到公司派下來的一個任務,讓我做一個簡單的地圖Demo,要求是輸入城市名,然後在地圖上標記出該城市的位置,而且必須是在一張靜態圖片上標記,不可調用現有的地圖成品控制項
  • WGS84坐標和HK80坐標之間的相互轉換
    不過,生態學研究中最常用的坐標係為WGS84(EPSG:4326),例如GPS一般就是直接給出WGS84的經緯度,Google Earth等也用WGS84坐標系。那麼HK80坐標如何轉換為WGS84坐標呢?
  • Unity遊戲開發——3D坐標轉換UGUI坐標
    0.前言實際開發中,我們經常會遇到需要3D與2D坐標轉換的問題,比如血條同步跟隨人物移動、傷害數字在人物頭上出現
  • 「2000坐標」成了標配,你還不懂坐標系定義和轉換嗎?
    國土空間規劃明確要求「統一採用2000國家大地坐標系」和「1985年國家高程基準」作為空間定位基礎,可是很多時候我們拿到的數據坐標並不是「2000國家大地坐標系」,需要進行坐標轉換,這可就犯了難?本課,我們來學習下ArcGIS中坐標定義與轉換的相關知識。
  • 地圖坐標常識和ArcGIS中坐標系統小議
    投影坐標系統,實質上便是平面坐標系統,其地圖單位通常為米。那麼為什麼投影坐標系統中要存在坐標系統的參數呢?這時候,又要說明一下投影的意義:將球面坐標轉化為平面坐標的過程便稱為投影。其中包括我們常見的「非地球投影坐標系統」。):地圖坐標常識1、橢球面    地圖坐標系由大地基準面和地圖投影確定,大地基準面是利用特定橢球體對特定地區地球表面的逼近,因此每個國家或地區均有各自的大地基準面,我們通常稱謂的北京54坐標系、西安80坐標系實際上指的是我國的兩個大地基準面。
  • 你應該知道的前端小知識
    10.前端工程化一提到前端工程化很多人想到的都是webpack,這是不對的,webpack僅僅是前端工程化中的一環。在整個工程化過程中他幫我們解決了絕大多數的問題,但並沒有解決所有問題。前端工程化是通過工具提升效率,降低成本的一種手段。
  • 工程測量坐標系入門——怎麼計算坐標正算反算、坐標轉換詳細演示
    對於一個剛剛從學校畢業沒有真正從事過工程測量工作的人員,或者以前從事其他工作現在剛剛轉行從事工程測量工作的初學者來說,對於「認識工程測量坐標系」中的計算常常難以理解,於是今天東英時代培訓就用一個房間軸線交點坐標計算為例,專門給大家演示一下,坐標正算、反算、坐標轉換等工程測量中的入門計算
  • ArcGIS中將WGS84坐標轉換為北京54或者西安80坐標系
    然而在這些數據與國內其他來源的地理數據進行疊合分析時,都面臨著坐標系統轉換的難題。美國GPS採用的是WGS84坐標系統,而國內的地圖數據多是北京54,西安80坐標系以及地方自己定義的坐標系統。不同坐標系統之間的轉換有嚴格的數學定義和轉換方程。如3參數法,7參數法,10參數(arcgis),只有獲得當地的精確地參考坐標,才能將這些參數求出來,進行精確的轉換。
  • 手持GPS坐標系統轉換參數的求解方法
    【關鍵詞】 手持GPS 坐標系統 轉換參數  概述  目前,市面上出售的手持GPS所使用的坐標系統基本都是WGS-84坐標系統,而我們使用的地圖資料大部分都屬於1954年北京坐標系或1980年西安國家大地坐標系。
  • 《魔獸世界懷舊服》地圖坐標怎麼查看 地圖坐標查看方法分享
    導 讀 在魔獸世界懷舊服中,許多NPC和怪物的地點標註都非常模糊,如果沒有清晰的地圖坐標便很難找到他們的位置
  • 坐標轉換+經緯度導航 任我遊R600升級版全新超越
    合眾思壯推出的專業戶外導航產品任我遊R600升級版,在具備其除了卓越的野外性能外還增加了獨特的坐標系統轉換、經緯度導航功能,相信能夠給廣大戶外愛好者帶來一個更加美好的夏季。劍走偏鋒坐標轉換解煩惱  目前,國內使用的導航坐標系一般都是1954年北京坐標系或
  • ArcGIS坐標轉換及投影詳解
    地理數據的坐標主要分為兩種方式:地理坐標和投影坐標。地理坐標是球面坐標,簡單來說就是使用經緯度來表示位置坐標,投影是按照一定的數學模型將球面坐標投影到幾何體後,用平面坐標(x和y)來表示位置信息。工作中我們經常會用到地理坐標與投影坐標的轉換。
  • 坐標轉換的計算公式
    參心大地坐標與參心空間直角坐標轉換1名詞解釋:A:參心空間直角坐標系:a)以參心0為坐標原點;b)Z軸與參考橢球的短軸(旋轉軸)相重合;c)X軸與起始子午面和赤道的交線重合;d)Y軸在赤道面上與X軸垂直,構成右手直角坐標系0-XYZ;e)地面點P的點位用(X,Y,Z)表示;B:參心大地坐標系:a)以參考橢球的中心為坐標原點,橢球的短軸與參考橢球旋轉軸重合;b)大地緯度B:以過地面點的橢球法線與橢球赤道面的夾角為大地緯度
  • 關於坐標轉換的一些基本概念
    每個項目收集到的資料並不一定都是一致的,如坐標類型不同:大地經緯度坐標,平面坐標等,也有可能採用的橢球體不同(坐標系不同)或投影方式不同等等。所以坐標系的相互轉換在項目中使用非常普遍,如大地坐標轉平面坐標,平面坐標轉空間直角坐標,平面坐標轉大地坐標等等。下面是關於坐標轉換的一些基本概念。
  • 坐標轉換
    進行GPS,北京54,西安80的經緯度和平面坐標之間的轉換,可以計算帶號
  • 經緯度、平面坐標系轉換方法
    使用工具:經緯度與我國54、80大地坐標轉換的小工具我們經常需要進行坐標系之間、經緯度和XY之間的轉換,我們使用這個小工具,做一個介紹