电子地图坐标系统研究整理总汇。
本文由文末的参考链接整理、整合、修改而成
1. 电子地图坐标系统简介
- WGS-84 坐标系:即地球坐标系,国际经纬度坐标标准
- GCJ-02 坐标系:即火星坐标系,WGS84 坐标系经加密后的坐标系,国测局制定。
- BD-09 坐标系:即百度坐标系,GCJ02 坐标系经加密后的坐标系。
GCJ-02 坐标系统,就是在标准的 WGS-84 坐标系统上进行了人为的偏移,比如 Google 地图、腾讯 SOSO 地图等就是直接使用了国家 GCJ-02 坐标系统,我们有一个不成文的说法,前者叫地球坐标,后者叫火星坐标,并且,火星坐标是无法转换成地球坐标的「网上虽然有一定的方法,但基本上都是基于偏移数据库,精度较高的数据库需要购买,当然这都是一种破解手段」。
所有的电子地图、导航设备,都需要加入国家保密插件。第一步,地图公司测绘地图,测绘完成后,送到国家测绘局,将真实坐标的电子地图,加密成“火星坐标”,这样的地图才是可以出版和发布的,然后才可以让 GPS 公司处理。第二步,所有的 GPS 公司,只要需要汽车导航的,需要用到导航电子地图的,都需要在软件中加入国家保密算法,将 COM 口读出来的真实的坐标信号,加密转换成国家要求的保密的坐标。这样,GPS 导航仪和导航电子地图就可以完全匹配,GPS 也就可以正常工作了。
API |
坐标系 |
百度地图 API |
百度坐标 |
腾讯搜搜地图 API |
火星坐标 |
搜狐搜狗地图 API |
搜狗坐标 |
阿里云地图 API |
火星坐标 |
图吧 MapBar 地图 API |
图吧坐标 |
高德 MapABC 地图 API |
火星坐标 |
灵图 51ditu 地图 API |
火星坐标 |
- 注1:百度地图使用百度坐标,支持从地球坐标和火星坐标导入成百度坐标,但无法导出。并且批量坐标转换一次只能转换20个(待验证)。
- 注2:搜狗地图支持直接显示地球坐标,支持地球坐标、火星坐标、百度坐标导入成搜狗坐标,同样,搜狗坐标也无法导出。
说完坐标系统,我们自然能够知道这里的问题,通过以下问题,来说说我对此的研究。
2、各个坐标系的相互转换
2.1 火星坐标系「GCJ-02」与百度坐标系「BD-09」的转换算法
其中 bd_encrypt 将 GCJ-02 坐标转换成 BD-09 坐标, bd_decrypt 反之。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16
| void bd_encrypt(double gg_lat, double gg_lon, double &bd_lat, double &bd_lon) { double x = gg_lon, y = gg_lat; double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi); double theta = atan2(y, x) + 0.000003 * cos(x * x_pi); bd_lon = z * cos(theta) + 0.0065; bd_lat = z * sin(theta) + 0.006; } void bd_decrypt(double bd_lat, double bd_lon, double &gg_lat, double &gg_lon) { double x = bd_lon - 0.0065, y = bd_lat - 0.006; double z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi); double theta = atan2(y, x) - 0.000003 * cos(x * x_pi); gg_lon = z * cos(theta); gg_lat = z * sin(theta); }
|
2.2 地球坐标系「WGS-84」到火星坐标系「GCJ-02」的转换算法
WGS-84 到 GCJ-02 的转换「即 GPS 加偏」算法
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61
| using System; namespace Navi { class EvilTransform { const double pi = 3.14159265358979324; const double a = 6378245.0; const double ee = 0.00669342162296594323; public static void transform(double wgLat, double wgLon, out double mgLat, out double mgLon) { if (outOfChina(wgLat, wgLon)) { mgLat = wgLat; mgLon = wgLon; return; } double dLat = transformLat(wgLon - 105.0, wgLat - 35.0); double dLon = transformLon(wgLon - 105.0, wgLat - 35.0); double radLat = wgLat / 180.0 * pi; double magic = Math.Sin(radLat); magic = 1 - ee * magic * magic; double sqrtMagic = Math.Sqrt(magic); dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi); dLon = (dLon * 180.0) / (a / sqrtMagic * Math.Cos(radLat) * pi); mgLat = wgLat + dLat; mgLon = wgLon + dLon; } static bool outOfChina(double lat, double lon) { if (lon < 72.004 || lon > 137.8347) return true; if (lat < 0.8293 || lat > 55.8271) return true; return false; } static double transformLat(double x, double y) { double ret = -100.0 + 2.0 * x + 3.0 * y + 0.2 * y * y + 0.1 * x * y + 0.2 * Math.Sqrt(Math.Abs(x)); ret += (20.0 * Math.Sin(6.0 * x * pi) + 20.0 * Math.Sin(2.0 * x * pi)) * 2.0 / 3.0; ret += (20.0 * Math.Sin(y * pi) + 40.0 * Math.Sin(y / 3.0 * pi)) * 2.0 / 3.0; ret += (160.0 * Math.Sin(y / 12.0 * pi) + 320 * Math.Sin(y * pi / 30.0)) * 2.0 / 3.0; return ret; } static double transformLon(double x, double y) { double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.Sqrt(Math.Abs(x)); ret += (20.0 * Math.Sin(6.0 * x * pi) + 20.0 * Math.Sin(2.0 * x * pi)) * 2.0 / 3.0; ret += (20.0 * Math.Sin(x * pi) + 40.0 * Math.Sin(x / 3.0 * pi)) * 2.0 / 3.0; ret += (150.0 * Math.Sin(x / 12.0 * pi) + 300.0 * Math.Sin(x / 30.0 * pi)) * 2.0 / 3.0; return ret; } } }
|
以上参考自:http://www.xue5.com/Mobile/iOS/679842.html
2.3 百度在线转换API
http://api.map.baidu.com/ag/coord/convert?from=0&to=4&x=longitude&y=latitude
from: 来源坐标系「0 表示 WGS-84 坐标;2 表示GCJ-02 坐标」
to: 转换后的坐标「4 表示 BD-09 坐标,即百度地图使用的行」
x: 经度
y: 纬度
请求之后会返回一串 Json
1 2 3 4 5 6 7 8
| { "error":0, "x":"MTIxLjUwMDIyODIxNDk2", "y":"MzEuMjM1ODUwMjYwMTE3" }
|
什么情况,经纬度居然还加密?那接下来也只好见招拆招了
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56
| import java.io.BufferedReader; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.net.URL; import java.net.URLConnection; import com.sun.org.apache.xerces.internal.impl.dv.util.Base64; public class BaiduAPIConverter extends Thread { public static void testPost(String x, String y) throws IOException { try { URL url = new URL("http://api.map.baidu.com/ag/coord/convert?from=2&to=4&x="+ x + "&y=" + y); URLConnection connection = url.openConnection(); connection.setDoOutput(true); OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "utf-8"); out.flush(); out.close(); String sCurrentLine, sTotalString; sCurrentLine = sTotalString = ""; InputStream l_urlStream; l_urlStream = connection.getInputStream(); BufferedReader l_reader = new BufferedReader(new InputStreamReader(l_urlStream)); while ((sCurrentLine = l_reader.readLine()) != null) { if (!sCurrentLine.equals("")) sTotalString += sCurrentLine; } sTotalString = sTotalString.substring(1, sTotalString.length() - 1); String[] results = sTotalString.split("\\,"); if (results.length == 3) { if (results[0].split("\\:")[1].equals("0")) { String mapX = results[1].split("\\:")[1]; String mapY = results[2].split("\\:")[1]; mapX = mapX.substring(1, mapX.length() - 1); mapY = mapY.substring(1, mapY.length() - 1); mapX = new String(Base64.decode(mapX)); mapY = new String(Base64.decode(mapY)); System.out.println("\t" + mapX + "\t" + mapY); } } sleep(10000); } catch (InterruptedException e) { e.printStackTrace(); } }
public static void main(String[] args) throws IOException { testPost("120.151379", "30.184678"); System.out.println("ok"); } }
|
到这里也差不多好了,主要的代码都写出来了,其他的您就自己写吧。
以上参考自:http://scalpel.me/archives/136
2.4 百度内置转换方法,可以不局限于百度定位 SDK
在百度地图中取得 WGS-84 坐标,调用如下方法:
1
| BMapManager.getLocationManager().setLocationCoordinateType(MKLocationManager.MK_COORDINATE_WGS84);
|
这样从百度 API 中取得的坐标就是 WGS-84 了,可是这种坐标如果显示到百度地图上就会偏移,也就是说取出一个坐标,原封不动的显示上去就偏移了,所以为了显示也是正常就需要在绘制到百度地图上之前转换成 BD-09 。
转换成 BD-09,调用方法:
1 2
| GeoPoint wgs84; GeoPoint bd09= CoordinateConvert.bundleDecode(CoordinateConvert.fromWgs84ToBaidu(wgs84));
|
这里实在不明白为何要设计成 CoordinateConvert.fromWgs84ToBaidu(wgs84) 返回了一个 Bundle,所以还需要 CoordinateConvert.bundleDecode() 再转成 GeoPoint 。
3. 参考文章
- 国内各地图API坐标系统比较与转换
- 关于百度地图坐标转换接口的研究
- 构建自己的百度地图坐标纠偏数据库
- GPS纠偏算法,适用于google,高德体系的地图