前言
文章的內(nèi)容主要是對(duì)當(dāng)前國(guó)內(nèi)和國(guó)外主要使用到的坐標(biāo)體系進(jìn)行介紹术辐,同時(shí)對(duì)不同坐標(biāo)格式的坐標(biāo)進(jìn)行轉(zhuǎn)換的方式進(jìn)行講解笨使,并以百度地圖和谷歌地圖為例子美澳,說明一下二者使用上的區(qū)別。文章可能會(huì)涉及到一些地理上的概念昔善,請(qǐng)讀者耐心閱讀元潘。
一、常見的坐標(biāo)介紹
實(shí)際上君仆,當(dāng)前使用的坐標(biāo)類型主要有三種翩概,分別是WGS-84牲距、GCJ-02以及第三方坐標(biāo)
WGS-84
WGS-84,也可以寫成是WGS84钥庇,是國(guó)外通用的GPS坐標(biāo)格式牍鞠,一般來說在國(guó)外我們使用手機(jī)或者其他設(shè)備獲取到的GPS坐標(biāo),都是WGS84格式的评姨,而且我們將獲取到的GPS數(shù)據(jù)來直接調(diào)用百度或者是谷歌地圖的API难述,也是完全沒有問題的,不存在多應(yīng)用間的坐標(biāo)轉(zhuǎn)換問題吐句。
GCJ-02(火星坐標(biāo))
GCJ-02是由中國(guó)國(guó)家測(cè)繪局(G表示Guojia國(guó)家胁后,C表示Cehui測(cè)繪,J表示Ju局)制訂的地理信息系統(tǒng)的坐標(biāo)系統(tǒng)嗦枢,出于地理位置的保密需要攀芯,我們國(guó)家要求所有在國(guó)內(nèi)使用的地圖軟件,都不得使用原始的WGS-84格式數(shù)據(jù)直接進(jìn)行計(jì)算文虏,而是需要將原坐標(biāo)轉(zhuǎn)換成GCJ-02才可以使用侣诺。
其他第三方的坐標(biāo)格式
有些地圖廠商也會(huì)不直接使用上述兩種坐標(biāo)格式,而是使用自定義的坐標(biāo)格式氧秘,比如天地圖(使用CGCS2000)年鸳、百度地圖(bd09II)等,我們平時(shí)可能更加熟悉的會(huì)是百度地圖丸相,百度地圖所使用的bd09II其實(shí)是基于我們國(guó)內(nèi)的GCJ-02坐標(biāo)再進(jìn)一步加密形成的
二搔确、百度地圖和谷歌地圖的比較
百度地圖
百度地圖由于其API簡(jiǎn)單易用,精度較高灭忠,是國(guó)內(nèi)很多項(xiàng)目的進(jìn)行地圖相關(guān)功能開發(fā)時(shí)的首選方案之一妥箕,不過對(duì)于國(guó)外項(xiàng)目來說,百度地圖由于部分API的語言兼容性不好(部分API結(jié)果有中文更舞,外國(guó)用戶可能看不懂),所以對(duì)于海外項(xiàng)目來說坎吻,谷歌地圖可能在客戶體驗(yàn)度上會(huì)更好一些缆蝉。
谷歌地圖
再來說說谷歌地圖,谷歌地圖在國(guó)內(nèi)的使用需要翻墻瘦真,所以國(guó)內(nèi)項(xiàng)目一般來說也不推薦使用刊头。同時(shí),雖然谷歌地圖的API功能也十分強(qiáng)大诸尽,但是谷歌地圖是收費(fèi)的原杂,不同類別的API收費(fèi)標(biāo)準(zhǔn)各不相同(比如說動(dòng)態(tài)地圖展示和地址逆向解析這兩類API價(jià)格就不一樣),且需要谷歌賬號(hào)有對(duì)應(yīng)海外支付能力的信用卡才可以您机。所以穿肄,如果對(duì)于精度要求較高年局,且資金較為充裕的海外項(xiàng)目來說,就比較推薦使用谷歌地圖了咸产。
PS:由于谷歌地圖國(guó)內(nèi)是不支持直接訪問的矢否,所以如果想要在項(xiàng)目中使用谷歌地圖的話,需要翻墻脑溢。
需要注意的是僵朗,谷歌地圖和百度地圖使用的坐標(biāo)格式并不是完全相同的,具體可以看下面的表格:
地圖類型 | 國(guó)內(nèi)坐標(biāo)格式 | 國(guó)外坐標(biāo)格式 |
---|---|---|
百度地圖 | bd09II | WGS-84 |
谷歌地圖 | GCJ02 | WGS-84 |
需要清楚的一點(diǎn)是屑彻,無論是WGS-84
验庙、bd09II
還是GCJ02
的坐標(biāo)格式,表達(dá)形式其實(shí)都是經(jīng)緯度社牲,區(qū)別只在于偏移量而已粪薛。
三、百度地圖和谷歌地圖的使用
(一)驗(yàn)證國(guó)內(nèi)坐標(biāo)
我們?cè)谏厦娼榻B了百度地圖和谷歌地圖在國(guó)內(nèi)使用并不能直接用WGS-84
格式的坐標(biāo)膳沽,否則會(huì)出現(xiàn)坐標(biāo)漂移
(也就是在地圖上的精確度會(huì)降低)汗菜。我們可以通過下面的例子來進(jìn)行演示,我們隨機(jī)取了一個(gè)經(jīng)緯度坐標(biāo)(格式是GCJ02)挑社,然后做了三種場(chǎng)景的測(cè)試陨界。
- 直接作為最終坐標(biāo)傳遞給百度地圖
- 調(diào)用百度地圖的API,將坐標(biāo)轉(zhuǎn)為bd09II 格式后痛阻,再傳遞給百度地圖
- 直接作為最終坐標(biāo)傳遞給谷歌地圖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style type="text/css">
#container2,
#container1,
#container3 {
height: 300px;
width: 100%;
}
</style>
<script type="text/javascript"
src="https://api.map.baidu.com/api?v=3.0&ak=xxx"></script>
<script async
src="https://maps.googleapis.com/maps/api/js?key=xxx&callback=initGMap3"></script>
<title>百度地圖和谷歌地圖對(duì)比</title>
</head>
<body>
<h2>百度地圖(未做坐標(biāo)轉(zhuǎn)換前):</h2>
<div id="container1"></div>
<h2>百度地圖(做坐標(biāo)轉(zhuǎn)換后):</h2>
<div id="container2"></div>
<h2>谷歌地圖(直接使用安卓定位)</h2>
<div id="container3"></div>
<script>
// 測(cè)試1菌瘪,使用同一個(gè)國(guó)內(nèi)GPS坐標(biāo)
let map1 = new BMap.Map("container1");
let map2 = new BMap.Map("container2");
function initBMap1() {
let point = new BMap.Point(113.403298, 23.118273);
map1.centerAndZoom(point, 18);
let marker = new BMap.Marker(point); // 創(chuàng)建標(biāo)注
map1.addOverlay(marker);
map1.addControl(new BMap.NavigationControl());
}
function initBMap2() {
let point = new BMap.Point(113.403298, 23.118273);
let convertor = new BMap.Convertor();
let pointArr = [point];
convertor.translate(pointArr, 3, 5, translateCallback);
map2.centerAndZoom(point, 18);
let marker = new BMap.Marker(point); // 創(chuàng)建標(biāo)注
map2.addOverlay(marker);
map2.addControl(new BMap.NavigationControl());
}
function translateCallback(data) {
if (data.status === 0) {
console.log(data);
var marker = new BMap.Marker(data.points[0]);
map2.addOverlay(marker);
map2.setCenter(data.points[0]);
}
}
function initGMap3() {
let point = { lat: 23.118273, lng: 113.403298 };
map = new google.maps.Map(document.getElementById("container3"), {
center: point,
zoom: 18,
});
const marker = new google.maps.Marker({
position: point,
map: map,
});
}
initBMap1();
initBMap2();
/*
就對(duì)比結(jié)果來看,首先百度地圖確確實(shí)實(shí)是需要借助坐標(biāo)轉(zhuǎn)換工具才可以實(shí)現(xiàn)精準(zhǔn)定位
其次阱当,安卓手機(jī)似乎自帶的是gcj02俏扩,而不是wgs84,這點(diǎn)還有點(diǎn)商榷
第三點(diǎn)弊添,蘋果手機(jī)使用gcj02坐標(biāo)后录淡,是可以正常顯示的,精度和百度地圖差不多油坝,這也驗(yàn)證了谷歌地圖在國(guó)內(nèi)是使用gcj02坐標(biāo)的事實(shí)
*/
</script>
</body>
</html>
其實(shí)從結(jié)果可以看到嫉戚,由于百度地圖使用的坐標(biāo)是bd09II
格式的,所以如果直接將原始坐標(biāo)傳遞給百度地圖的話澈圈,那么雖然地圖可以成功顯示彬檀,但是明顯地圖的位置是不準(zhǔn)確的。而在使用百度地圖坐標(biāo)轉(zhuǎn)換的接口后瞬女,此時(shí)的地圖上的坐標(biāo)點(diǎn)就基本上是和實(shí)際位置相符的窍帝。
再回過頭來看看谷歌地圖,可以看到雖然谷歌地圖和百度地圖的默認(rèn)地圖類型不同诽偷,但是二者最終的定位其實(shí)是差不多的坤学,這也就側(cè)面驗(yàn)證了之前的結(jié)論疯坤。谷歌地圖在國(guó)內(nèi)使用的是GCJ02
格式的坐標(biāo),而百度地圖使用的是bd09II
格式坐標(biāo)拥峦。
(二)驗(yàn)證國(guó)外坐標(biāo)
此處和上一個(gè)驗(yàn)證案例的代碼基本相同贴膘,區(qū)別只在于我們使用了國(guó)外的坐標(biāo)進(jìn)行測(cè)試,需要注意的是略号,國(guó)外統(tǒng)一用的坐標(biāo)格式是WGS-84
刑峡,對(duì)應(yīng)的三個(gè)地圖樣本分別是如下幾種場(chǎng)景:
- 直接將坐標(biāo)傳遞給百度地圖
- 將坐標(biāo)轉(zhuǎn)換為百度地圖格式,再調(diào)用百度地圖進(jìn)行顯示
- 直接將坐標(biāo)傳遞給谷歌地圖
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style type="text/css">
#container2,
#container1,
#container3 {
height: 300px;
width: 100%;
}
</style>
<script type="text/javascript"
src="https://api.map.baidu.com/api?v=3.0&ak=xxx"></script>
<script async
src="https://maps.googleapis.com/maps/api/js?key=xxx&callback=initGMap3"></script>
<title>百度地圖和谷歌地圖對(duì)比</title>
</head>
<body>
<h2>百度地圖(未做坐標(biāo)轉(zhuǎn)換前):</h2>
<div id="container1"></div>
<h2>百度地圖(做坐標(biāo)轉(zhuǎn)換后):</h2>
<div id="container2"></div>
<h2>谷歌地圖(直接使用安卓定位)</h2>
<div id="container3"></div>
<script>
// 測(cè)試1玄柠,使用同一個(gè)國(guó)內(nèi)GPS坐標(biāo)
let map1 = new BMap.Map("container1");
let map2 = new BMap.Map("container2");
function initBMap1() {
let point = new BMap.Point(140.7388437698104,-27.7443318142725);
map1.centerAndZoom(point, 18);
let marker = new BMap.Marker(point); // 創(chuàng)建標(biāo)注
map1.addOverlay(marker);
map1.addControl(new BMap.NavigationControl());
}
function initBMap2() {
let point = new BMap.Point(140.7388437698104,-27.7443318142725);
let convertor = new BMap.Convertor();
let pointArr = [point];
convertor.translate(pointArr, 1, 5, translateCallback);
map2.centerAndZoom(point, 18);
let marker = new BMap.Marker(point); // 創(chuàng)建標(biāo)注
map2.addOverlay(marker);
map2.addControl(new BMap.NavigationControl());
}
function translateCallback(data) {
if (data.status === 0) {
console.log(data);
var marker = new BMap.Marker(data.points[0]);
map2.addOverlay(marker);
map2.setCenter(data.points[0]);
}
}
function initGMap3() {
let point = { "lat": -27.7443318142725, "lng": 140.7388437698104 };
map = new google.maps.Map(document.getElementById("container3"), {
center: point,
zoom: 18,
});
const marker = new google.maps.Marker({
position: point,
map: map,
});
}
initBMap1();
initBMap2();
/*
就對(duì)比結(jié)果來看突梦,首先可以確定在國(guó)外地區(qū),百度地圖使用的坐標(biāo)格式是wgs84羽利,因?yàn)槭褂米鴺?biāo)轉(zhuǎn)換后宫患,實(shí)際的圖像并沒有發(fā)生偏移
其次再來看谷歌地圖,可以看出雖然百度地圖和谷歌地圖的精度上略有差異这弧,但圖像大致的位置還是一致的娃闲,也就是說谷歌地圖在國(guó)外也是直接使用的wgs84
所以在國(guó)外地區(qū),直接把原始的GPS數(shù)據(jù)交由這兩個(gè)應(yīng)用顯示匾浪,其結(jié)果基本上是差不多的皇帮,也不用考慮坐標(biāo)的轉(zhuǎn)換
*/
</script>
</body>
</html>
我們可以從結(jié)果中看到,最終三個(gè)測(cè)試的用例顯示的結(jié)果基本上都是一致的蛋辈,其中谷歌地圖在清晰度上是要比百度地圖高出許多的属拾。同時(shí)也可以驗(yàn)證出兩個(gè)結(jié)論:
(1)無論是百度地圖還是谷歌地圖,在國(guó)外使用的坐標(biāo)都是WGS-84
格式的冷溶,也就是說我們要使用的話渐白,可以直接傳遞手機(jī)GPS獲取的原始坐標(biāo)進(jìn)去即可。
(2)即使將原始坐標(biāo)轉(zhuǎn)換為百度坐標(biāo)逞频,那么在國(guó)外也是可以正常顯示的(目測(cè)是百度地圖會(huì)根據(jù)你的坐標(biāo)來判斷是否是國(guó)外纯衍,如果是的話就直接原坐標(biāo)返回了)
三、百度地圖到谷歌地圖的切換
如果項(xiàng)目已經(jīng)使用百度地圖一段時(shí)間了苗胀,但又有切換成谷歌地圖的需求的話托酸,此時(shí)除了評(píng)估代碼上的改造量之外,我們還需要額外考慮兩個(gè)問題:
(1)數(shù)據(jù)庫歷史數(shù)據(jù)中已有的百度坐標(biāo)柒巫,可以如何轉(zhuǎn)換為谷歌坐標(biāo)
(2)以后的新數(shù)據(jù),應(yīng)該怎么保存
對(duì)于第二個(gè)問題其實(shí)很好解決谷丸,如果是在國(guó)外堡掏,那么手機(jī)獲取到的坐標(biāo)格式一般來說都是WGS-84
格式的,此時(shí)我們可以直接保存刨疼,而對(duì)于國(guó)內(nèi)來說泉唁,情況相對(duì)復(fù)雜一些鹅龄,一般來說是建議數(shù)據(jù)庫直接存儲(chǔ)GCJ02
格式的坐標(biāo)。因?yàn)椴糠职沧渴謾C(jī)在制造的過程中亭畜,其GPS功能獲取到的原始坐標(biāo)就是已經(jīng)加密過的GCJ02
格式扮休,而IOS手機(jī)一般來說在國(guó)內(nèi)調(diào)用GPS獲取到的原始坐標(biāo)是WGS-84
,也就是說拴鸵,我們可能需要針對(duì)不同的手機(jī)型號(hào)來做一下不同的處理玷坠。對(duì)于安卓手機(jī),可以考慮直接保存其原始坐標(biāo)劲藐,而IOS手機(jī)我們可以通過調(diào)用百度地圖的坐標(biāo)轉(zhuǎn)換接口或者是其他手段八堡,來實(shí)現(xiàn)坐標(biāo)從WGS-84
到GCJ02
格式的轉(zhuǎn)變。
而對(duì)于問題一的話聘芜,百度地圖提供了bd09II
到GCJ02
格式轉(zhuǎn)變的接口的兄渺,所以我們可以很方便地實(shí)現(xiàn)對(duì)國(guó)內(nèi)坐標(biāo)的正常話。但問題主要在于汰现,我們的數(shù)據(jù)庫可能有部分歷史數(shù)據(jù)的坐標(biāo)是國(guó)外坐標(biāo)挂谍,那么如果我們統(tǒng)一使用上面這個(gè)接口做轉(zhuǎn)換的話,那么國(guó)外的歷史數(shù)據(jù)坐標(biāo)是否會(huì)受到影響瞎饲。
我們可以做一個(gè)小案例來驗(yàn)證一下口叙,在這個(gè)案例中,我們的原始坐標(biāo)是百度格式坐標(biāo)企软,地點(diǎn)是國(guó)外庐扫,然后我們調(diào)用百度地圖的坐標(biāo)轉(zhuǎn)換接口,將坐標(biāo)從bd09II
格式轉(zhuǎn)為GCJ02
格式仗哨。
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<style type="text/css">
#container2,
#container1,
#container3 {
height: 300px;
width: 100%;
}
</style>
<script type="text/javascript" src="https://api.map.baidu.com/api?v=3.0&ak=xxx"></script>
<title>百度地圖和谷歌地圖對(duì)比</title>
</head>
<body>
<h2>百度地圖(未做坐標(biāo)轉(zhuǎn)換前):</h2>
<div id="container1"></div>
<h2>百度地圖(做坐標(biāo)轉(zhuǎn)換后):</h2>
<div id="container2"></div>
<script>
// 測(cè)試1形庭,使用同一個(gè)國(guó)內(nèi)GPS坐標(biāo)
let map1 = new BMap.Map("container1");
let map2 = new BMap.Map("container2");
function initBMap1() {
let point = new BMap.Point(140.7388437698104,-27.7443318142725);
map1.centerAndZoom(point, 18);
let marker = new BMap.Marker(point); // 創(chuàng)建標(biāo)注
map1.addOverlay(marker);
map1.addControl(new BMap.NavigationControl());
}
function initBMap2() {
let point = new BMap.Point(140.7388437698104,-27.7443318142725);
let convertor = new BMap.Convertor();
let pointArr = [point];
convertor.translate(pointArr, 5, 3, translateCallback);
map2.centerAndZoom(point, 18);
let marker = new BMap.Marker(point); // 創(chuàng)建標(biāo)注
map2.addOverlay(marker);
map2.addControl(new BMap.NavigationControl());
}
function translateCallback(data) {
if (data.status === 0) {
console.log(data);
var marker = new BMap.Marker(data.points[0]);
map2.addOverlay(marker);
map2.setCenter(data.points[0]);
}
}
initBMap1();
initBMap2();
/*
就對(duì)比結(jié)果來看,首先可以確定在國(guó)外地區(qū)厌漂,百度地圖使用的坐標(biāo)格式是wgs84萨醒,因?yàn)槭褂米鴺?biāo)轉(zhuǎn)換后,實(shí)際的圖像并沒有發(fā)生偏移
其次再來看谷歌地圖苇倡,可以看出雖然百度地圖和谷歌地圖的精度上略有差異富纸,但圖像大致的位置還是一致的,也就是說谷歌地圖在國(guó)外也是直接使用的wgs84
所以在國(guó)外地區(qū)旨椒,直接把原始的GPS數(shù)據(jù)交由這兩個(gè)應(yīng)用顯示晓褪,其結(jié)果基本上是差不多的,也不用考慮坐標(biāo)的轉(zhuǎn)換
*/
</script>
</body>
</html>
我們可以看到综慎,經(jīng)過轉(zhuǎn)換后涣仿,國(guó)外的坐標(biāo)并沒有收到任何影響。從而我們可以得到如下的可行方案:
(1)對(duì)于歷史數(shù)據(jù),可以直接調(diào)用百度地圖的坐標(biāo)轉(zhuǎn)換接口進(jìn)行轉(zhuǎn)換好港,轉(zhuǎn)換后國(guó)外的坐標(biāo)不變(還是WGS84
)愉镰,而國(guó)內(nèi)的坐標(biāo)則改為GCJ02
。
(2)對(duì)于新數(shù)據(jù)钧汹,國(guó)外數(shù)據(jù)可以直接保存丈探,國(guó)內(nèi)數(shù)據(jù)則建議統(tǒng)一保存為GCJ02
格式
說在最后
實(shí)際上,目前對(duì)于百度地圖和谷歌地圖這類的地圖應(yīng)用坐標(biāo)的介紹文章不少拔莱,但是感覺很多都只是泛泛而談碗降,沒有用案例去驗(yàn)證,所以本篇文章也是想著對(duì)這一塊的內(nèi)容做一下補(bǔ)充辨宠,方便后面其他開發(fā)者的需要稿饰。同時(shí)被啼,由于國(guó)內(nèi)不可以直接使用谷歌地圖,所以直接用我案例的代碼在本地跑的話可以谷歌地圖也是出不來的,各位更多的是參考案例中的結(jié)論即可烂叔。文章若有錯(cuò)誤的地方毛雇,也歡迎指正~