常见的坐标体系转换 + 百度地图和谷歌地图的坐标转换思路
前言
文章的内容主要是对当前国内和国外主要使用到的坐标体系进行介绍,同时对不同坐标格式的坐标进行转换的方式进行讲解,并以百度地图和谷歌地图为例子,说明一下二者使用上的区别。文章可能会涉及到一些地理上的概念,请读者耐心阅读。
一、常见的坐标介绍
实际上,当前使用的坐标类型主要有三种,分别是WGS-84、GCJ-02以及第三方坐标
WGS-84
WGS-84,也可以写成是WGS84,是国外通用的GPS坐标格式,一般来说在国外我们使用手机或者其他设备获取到的GPS坐标,都是WGS84格式的,而且我们将获取到的GPS数据来直接调用百度或者是谷歌地图的API,也是完全没有问题的,不存在多应用间的坐标转换问题。
GCJ-02(火星坐标)
GCJ-02是由中国国家测绘局(G表示Guojia国家,C表示Cehui测绘,J表示Ju局)制订的地理信息系统的坐标系统,出于地理位置的保密需要,我们国家要求所有在国内使用的地图软件,都不得使用原始的WGS-84格式数据直接进行计算,而是需要将原坐标转换成GCJ-02才可以使用。
其他第三方的坐标格式
有些地图厂商也会不直接使用上述两种坐标格式,而是使用自定义的坐标格式,比如天地图(使用CGCS2000)、百度地图(bd09II)等,我们平时可能更加熟悉的会是百度地图,百度地图所使用的bd09II其实是基于我们国内的GCJ-02坐标再进一步加密形成的
二、百度地图和谷歌地图的比较
百度地图
百度地图由于其API简单易用,精度较高,是国内很多项目的进行地图相关功能开发时的首选方案之一,不过对于国外项目来说,百度地图由于部分API的语言兼容性不好(部分API结果有中文,外国用户可能看不懂),所以对于海外项目来说,谷歌地图可能在客户体验度上会更好一些。
谷歌地图
再来说说谷歌地图,谷歌地图在国内的使用需要FQ,所以国内项目一般来说也不推荐使用。同时,虽然谷歌地图的API功能也十分强大,但是谷歌地图是收费的,不同类别的API收费标准各不相同(比如说动态地图展示和地址逆向解析这两类API价格就不一样),且需要谷歌账号有对应海外支付能力的信用卡才可以。所以,如果对于精度要求较高,且资金较为充裕的海外项目来说,就比较推荐使用谷歌地图了。
PS:由于谷歌地图国内是不支持直接访问的,所以如果想要在项目中使用谷歌地图的话,需要FQ。
需要注意的是,谷歌地图和百度地图使用的坐标格式并不是完全相同的,具体可以看下面的表格:
地图类型 | 国内坐标格式 | 国外坐标格式 |
---|---|---|
百度地图 | bd09II | WGS-84 |
谷歌地图 | GCJ02 | WGS-84 |
需要清楚的一点是,无论是WGS-84
、bd09II
还是GCJ02
的坐标格式,表达形式其实都是经纬度,区别只在于偏移量而已。
三、百度地图和谷歌地图的使用
(一)验证国内坐标
我们在上面介绍了百度地图和谷歌地图在国内使用并不能直接用WGS-84
格式的坐标,否则会出现坐标漂移
(也就是在地图上的精确度会降低)。我们可以通过下面的例子来进行演示,我们随机取了一个经纬度坐标(格式是GCJ02),然后做了三种场景的测试。
- 直接作为最终坐标传递给百度地图
- 调用百度地图的API,将坐标转为bd09II 格式后,再传递给百度地图
- 直接作为最终坐标传递给谷歌地图
<!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>百度地图和谷歌地图对比</title>
</head>
<body>
<h2>百度地图(未做坐标转换前):</h2>
<div id="container1"></div>
<h2>百度地图(做坐标转换后):</h2>
<div id="container2"></div>
<h2>谷歌地图(直接使用安卓定位)</h2>
<div id="container3"></div>
<script>
// 测试1,使用同一个国内GPS坐标
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); // 创建标注
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); // 创建标注
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();
/*
就对比结果来看,首先百度地图确确实实是需要借助坐标转换工具才可以实现精准定位
其次,安卓手机似乎自带的是gcj02,而不是wgs84,这点还有点商榷
第三点,苹果手机使用gcj02坐标后,是可以正常显示的,精度和百度地图差不多,这也验证了谷歌地图在国内是使用gcj02坐标的事实
*/
</script>
</body>
</html>
其实从结果可以看到,由于百度地图使用的坐标是bd09II
格式的,所以如果直接将原始坐标传递给百度地图的话,那么虽然地图可以成功显示,但是明显地图的位置是不准确的。而在使用百度地图坐标转换的接口后,此时的地图上的坐标点就基本上是和实际位置相符的。
再回过头来看看谷歌地图,可以看到虽然谷歌地图和百度地图的默认地图类型不同,但是二者最终的定位其实是差不多的,这也就侧面验证了之前的结论。谷歌地图在国内使用的是GCJ02
格式的坐标,而百度地图使用的是bd09II
格式坐标。
(二)验证国外坐标
此处和上一个验证案例的代码基本相同,区别只在于我们使用了国外的坐标进行测试,需要注意的是,国外统一用的坐标格式是WGS-84
,对应的三个地图样本分别是如下几种场景:
- 直接将坐标传递给百度地图
- 将坐标转换为百度地图格式,再调用百度地图进行显示
- 直接将坐标传递给谷歌地图
<!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>百度地图和谷歌地图对比</title>
</head>
<body>
<h2>百度地图(未做坐标转换前):</h2>
<div id="container1"></div>
<h2>百度地图(做坐标转换后):</h2>
<div id="container2"></div>
<h2>谷歌地图(直接使用安卓定位)</h2>
<div id="container3"></div>
<script>
// 测试1,使用同一个国内GPS坐标
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); // 创建标注
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); // 创建标注
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();
/*
就对比结果来看,首先可以确定在国外地区,百度地图使用的坐标格式是wgs84,因为使用坐标转换后,实际的图像并没有发生偏移
其次再来看谷歌地图,可以看出虽然百度地图和谷歌地图的精度上略有差异,但图像大致的位置还是一致的,也就是说谷歌地图在国外也是直接使用的wgs84
所以在国外地区,直接把原始的GPS数据交由这两个应用显示,其结果基本上是差不多的,也不用考虑坐标的转换
*/
</script>
</body>
</html>
我们可以从结果中看到,最终三个测试的用例显示的结果基本上都是一致的,其中谷歌地图在清晰度上是要比百度地图高出许多的。同时也可以验证出两个结论:
(1)无论是百度地图还是谷歌地图,在国外使用的坐标都是WGS-84
格式的,也就是说我们要使用的话,可以直接传递手机GPS获取的原始坐标进去即可。
(2)即使将原始坐标转换为百度坐标,那么在国外也是可以正常显示的(目测是百度地图会根据你的坐标来判断是否是国外,如果是的话就直接原坐标返回了)
三、百度地图到谷歌地图的切换
如果项目已经使用百度地图一段时间了,但又有切换成谷歌地图的需求的话,此时除了评估代码上的改造量之外,我们还需要额外考虑两个问题:
(1)数据库历史数据中已有的百度坐标,可以如何转换为谷歌坐标
(2)以后的新数据,应该怎么保存
对于第二个问题其实很好解决,如果是在国外,那么手机获取到的坐标格式一般来说都是WGS-84
格式的,此时我们可以直接保存,而对于国内来说,情况相对复杂一些,一般来说是建议数据库直接存储GCJ02
格式的坐标。因为部分安卓手机在制造的过程中,其GPS功能获取到的原始坐标就是已经加密过的GCJ02
格式,而IOS手机一般来说在国内调用GPS获取到的原始坐标是WGS-84
,也就是说,我们可能需要针对不同的手机型号来做一下不同的处理。对于安卓手机,可以考虑直接保存其原始坐标,而IOS手机我们可以通过调用百度地图的坐标转换接口或者是其他手段,来实现坐标从WGS-84
到GCJ02
格式的转变。
而对于问题一的话,百度地图提供了bd09II
到GCJ02
格式转变的接口的,所以我们可以很方便地实现对国内坐标的正常话。但问题主要在于,我们的数据库可能有部分历史数据的坐标是国外坐标,那么如果我们统一使用上面这个接口做转换的话,那么国外的历史数据坐标是否会受到影响。
我们可以做一个小案例来验证一下,在这个案例中,我们的原始坐标是百度格式坐标,地点是国外,然后我们调用百度地图的坐标转换接口,将坐标从bd09II
格式转为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>百度地图和谷歌地图对比</title>
</head>
<body>
<h2>百度地图(未做坐标转换前):</h2>
<div id="container1"></div>
<h2>百度地图(做坐标转换后):</h2>
<div id="container2"></div>
<script>
// 测试1,使用同一个国内GPS坐标
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); // 创建标注
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); // 创建标注
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();
/*
就对比结果来看,首先可以确定在国外地区,百度地图使用的坐标格式是wgs84,因为使用坐标转换后,实际的图像并没有发生偏移
其次再来看谷歌地图,可以看出虽然百度地图和谷歌地图的精度上略有差异,但图像大致的位置还是一致的,也就是说谷歌地图在国外也是直接使用的wgs84
所以在国外地区,直接把原始的GPS数据交由这两个应用显示,其结果基本上是差不多的,也不用考虑坐标的转换
*/
</script>
</body>
</html>
我们可以看到,经过转换后,国外的坐标并没有收到任何影响。从而我们可以得到如下的可行方案:
(1)对于历史数据,可以直接调用百度地图的坐标转换接口进行转换,转换后国外的坐标不变(还是WGS84
),而国内的坐标则改为GCJ02
。
(2)对于新数据,国外数据可以直接保存,国内数据则建议统一保存为GCJ02
格式
说在最后
实际上,目前对于百度地图和谷歌地图这类的地图应用坐标的介绍文章不少,但是感觉很多都只是泛泛而谈,没有用案例去验证,所以本篇文章也是想着对这一块的内容做一下补充,方便后面其他开发者的需要。同时,由于国内不可以直接使用谷歌地图,所以直接用我案例的代码在本地跑的话可以谷歌地图也是出不来的,各位更多的是参考案例中的结论即可。文章若有错误的地方,也欢迎指正~