「轉(zhuǎn)」電子地圖坐標系統(tǒng)研究整理

「博客搬家」 原地址: CSDN 原發(fā)表時間: 2016-04-15

本文由文末的參考鏈接整理仗岖、整合朽们、修改而成

1. 電子地圖坐標系統(tǒng)簡介

  • WGS-84 坐標系:即地球坐標系,國際經(jīng)緯度坐標標準
  • GCJ-02 坐標系:即火星坐標系绸栅,WGS84 坐標系經(jīng)加密后的坐標系,國測局制定。
  • BD-09 坐標系:即百度坐標系哟玷,GCJ02 坐標系經(jīng)加密后的坐標系。

GCJ-02 坐標系統(tǒng)一也,就是在標準的 WGS-84 坐標系統(tǒng)上進行了人為的偏移巢寡,比如 Google 地圖、騰訊 SOSO 地圖等就是直接使用了國家 GCJ-02 坐標系統(tǒng)椰苟,我們有一個不成文的說法抑月,前者叫地球坐標,后者叫火星坐標舆蝴,并且谦絮,火星坐標是無法轉(zhuǎn)換成地球坐標的「網(wǎng)上雖然有一定的方法,但基本上都是基于偏移數(shù)據(jù)庫洁仗,精度較高的數(shù)據(jù)庫需要購買挨稿,當然這都是一種破解手段」。

所有的電子地圖京痢、導航設備奶甘,都需要加入國家保密插件。第一步祭椰,地圖公司測繪地圖臭家,測繪完成后,送到國家測繪局方淤,將真實坐標的電子地圖钉赁,加密成“火星坐標”,這樣的地圖才是可以出版和發(fā)布的携茂,然后才可以讓 GPS 公司處理你踩。第二步,所有的 GPS 公司,只要需要汽車導航的带膜,需要用到導航電子地圖的吩谦,都需要在軟件中加入國家保密算法,將 COM 口讀出來的真實的坐標信號膝藕,加密轉(zhuǎn)換成國家要求的保密的坐標式廷。這樣,GPS 導航儀和導航電子地圖就可以完全匹配芭挽,GPS 也就可以正常工作了滑废。

API 坐標系
百度地圖 API 百度坐標
騰訊搜搜地圖 API 火星坐標
搜狐搜狗地圖 API 搜狗坐標
阿里云地圖 API 火星坐標
圖吧 MapBar 地圖 API 圖吧坐標
高德 MapABC 地圖 API 火星坐標
靈圖 51ditu 地圖 API 火星坐標
  • 注1:百度地圖使用百度坐標,支持從地球坐標和火星坐標導入成百度坐標袜爪,但無法導出蠕趁。并且批量坐標轉(zhuǎn)換一次只能轉(zhuǎn)換20個(待驗證)。
  • 注2:搜狗地圖支持直接顯示地球坐標辛馆,支持地球坐標俺陋、火星坐標、百度坐標導入成搜狗坐標怀各,同樣,搜狗坐標也無法導出术浪。

說完坐標系統(tǒng)瓢对,我們自然能夠知道這里的問題,通過以下問題胰苏,來說說我對此的研究硕蛹。

2、各個坐標系的相互轉(zhuǎn)換

2.1 火星坐標系「GCJ-02」與百度坐標系「BD-09」的轉(zhuǎn)換算法

其中 bd_encrypt 將 GCJ-02 坐標轉(zhuǎn)換成 BD-09 坐標硕并, bd_decrypt 反之法焰。

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」的轉(zhuǎn)換算法

WGS-84 到 GCJ-02 的轉(zhuǎn)換「即 GPS 加偏」算法

using System;   
namespace Navi  
{  
    class EvilTransform  
    {  
        const double pi = 3.14159265358979324;  
        //   
        // Krasovsky 1940   
        //   
        // a = 6378245.0, 1/f = 298.3   
        // b = a * (1 - f)   
        // ee = (a^2 - b^2) / a^2;   
        const double a = 6378245.0;  
        const double ee = 0.00669342162296594323;  
        //   
        // World Geodetic System ==> Mars Geodetic System   
        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 百度在線轉(zhuǎn)換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: 轉(zhuǎn)換后的坐標「4 表示 BD-09 坐標倔毙,即百度地圖使用的行」
x: 經(jīng)度
y: 緯度

請求之后會返回一串 Json

{  
    "error":0,  
    "x":"MTIxLjUwMDIyODIxNDk2",  
    "y":"MzEuMjM1ODUwMjYwMTE3"  
}  
error:是結(jié)果是否出錯標志位埃仪,0 表示成功
x: 坐標系 2 時為經(jīng)度,4 時為緯度(Base64編碼)
y: 坐標系 4 時為經(jīng)度陕赃,2 時為緯度(Base64編碼)

什么情況卵蛉,經(jīng)緯度居然還加密?那接下來也只好見招拆招了

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");  
      // remember to clean up   
      out.flush();  
      out.close();  
      // 一旦發(fā)送成功么库,用以下方法就可以得到服務器的回應:   
      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) {  
      // TODO Auto-generated catch block   
      e.printStackTrace();  
    }  
  }  
  /** 
   * @param args 
   * @throws IOException 
   */  
  public static void main(String[] args) throws IOException {  
    testPost("120.151379", "30.184678");  
    System.out.println("ok");  
  }  
}  

到這里也差不多好了傻丝,主要的代碼都寫出來了,其他的您就自己寫吧诉儒。
以上參考自:http://scalpel.me/archives/136

2.4 百度內(nèi)置轉(zhuǎn)換方法葡缰,可以不局限于百度定位 SDK

在百度地圖中取得 WGS-84 坐標,調(diào)用如下方法:

BMapManager.getLocationManager().setLocationCoordinateType(MKLocationManager.MK_COORDINATE_WGS84);

這樣從百度 API 中取得的坐標就是 WGS-84 了,可是這種坐標如果顯示到百度地圖上就會偏移泛释,也就是說取出一個坐標滤愕,原封不動的顯示上去就偏移了,所以為了顯示也是正常就需要在繪制到百度地圖上之前轉(zhuǎn)換成 BD-09 胁澳。
轉(zhuǎn)換成 BD-09该互,調(diào)用方法:

GeoPoint wgs84;
GeoPoint bd09= CoordinateConvert.bundleDecode(CoordinateConvert.fromWgs84ToBaidu(wgs84));

這里實在不明白為何要設計成 CoordinateConvert.fromWgs84ToBaidu(wgs84) 返回了一個 Bundle韭畸,所以還需要 CoordinateConvert.bundleDecode() 再轉(zhuǎn)成 GeoPoint 宇智。

3. 參考文章

  1. 國內(nèi)各地圖API坐標系統(tǒng)比較與轉(zhuǎn)換
  2. 關于百度地圖坐標轉(zhuǎn)換接口的研究
  3. 構(gòu)建自己的百度地圖坐標糾偏數(shù)據(jù)庫
  4. GPS糾偏算法,適用于google,高德體系的地圖
最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請聯(lián)系作者
  • 序言:七十年代末胰丁,一起剝皮案震驚了整個濱河市随橘,隨后出現(xiàn)的幾起案子,更是在濱河造成了極大的恐慌锦庸,老刑警劉巖机蔗,帶你破解...
    沈念sama閱讀 206,482評論 6 481
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件,死亡現(xiàn)場離奇詭異甘萧,居然都是意外死亡萝嘁,警方通過查閱死者的電腦和手機,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 88,377評論 2 382
  • 文/潘曉璐 我一進店門扬卷,熙熙樓的掌柜王于貴愁眉苦臉地迎上來牙言,“玉大人,你說我怎么就攤上這事怪得≡弁鳎” “怎么了?”我有些...
    開封第一講書人閱讀 152,762評論 0 342
  • 文/不壞的土叔 我叫張陵徒恋,是天一觀的道長蚕断。 經(jīng)常有香客問我,道長入挣,這世上最難降的妖魔是什么亿乳? 我笑而不...
    開封第一講書人閱讀 55,273評論 1 279
  • 正文 為了忘掉前任,我火速辦了婚禮径筏,結(jié)果婚禮上风皿,老公的妹妹穿的比我還像新娘。我一直安慰自己匠璧,他們只是感情好桐款,可當我...
    茶點故事閱讀 64,289評論 5 373
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著夷恍,像睡著了一般魔眨。 火紅的嫁衣襯著肌膚如雪媳维。 梳的紋絲不亂的頭發(fā)上,一...
    開封第一講書人閱讀 49,046評論 1 285
  • 那天遏暴,我揣著相機與錄音侄刽,去河邊找鬼。 笑死朋凉,一個胖子當著我的面吹牛州丹,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播杂彭,決...
    沈念sama閱讀 38,351評論 3 400
  • 文/蒼蘭香墨 我猛地睜開眼墓毒,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了亲怠?” 一聲冷哼從身側(cè)響起所计,我...
    開封第一講書人閱讀 36,988評論 0 259
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎团秽,沒想到半個月后主胧,有當?shù)厝嗽跇淞掷锇l(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 43,476評論 1 300
  • 正文 獨居荒郊野嶺守林人離奇死亡习勤,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點故事閱讀 35,948評論 2 324
  • 正文 我和宋清朗相戀三年踪栋,在試婚紗的時候發(fā)現(xiàn)自己被綠了。 大學時的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片图毕。...
    茶點故事閱讀 38,064評論 1 333
  • 序言:一個原本活蹦亂跳的男人離奇死亡夷都,死狀恐怖,靈堂內(nèi)的尸體忽然破棺而出吴旋,到底是詐尸還是另有隱情损肛,我是刑警寧澤厢破,帶...
    沈念sama閱讀 33,712評論 4 323
  • 正文 年R本政府宣布荣瑟,位于F島的核電站,受9級特大地震影響摩泪,放射性物質(zhì)發(fā)生泄漏笆焰。R本人自食惡果不足惜,卻給世界環(huán)境...
    茶點故事閱讀 39,261評論 3 307
  • 文/蒙蒙 一见坑、第九天 我趴在偏房一處隱蔽的房頂上張望嚷掠。 院中可真熱鬧,春花似錦荞驴、人聲如沸不皆。這莊子的主人今日做“春日...
    開封第一講書人閱讀 30,264評論 0 19
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽霹娄。三九已至,卻和暖如春,著一層夾襖步出監(jiān)牢的瞬間犬耻,已是汗流浹背踩晶。 一陣腳步聲響...
    開封第一講書人閱讀 31,486評論 1 262
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留枕磁,地道東北人渡蜻。 一個月前我還...
    沈念sama閱讀 45,511評論 2 354
  • 正文 我出身青樓,卻偏偏與公主長得像计济,于是被迫代替她去往敵國和親茸苇。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 42,802評論 2 345

推薦閱讀更多精彩內(nèi)容