「轉」電子地圖坐標系統研究整理

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

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

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 反之。

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 加偏」算法

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 百度在線轉換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

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

什么情況,經緯度居然還加密?那接下來也只好見招拆招了

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();  
      // 一旦發送成功,用以下方法就可以得到服務器的回應:   
      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 百度內置轉換方法,可以不局限于百度定位 SDK

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

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

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

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

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

3. 參考文章

  1. 國內各地圖API坐標系統比較與轉換
  2. 關于百度地圖坐標轉換接口的研究
  3. 構建自己的百度地圖坐標糾偏數據庫
  4. GPS糾偏算法,適用于google,高德體系的地圖
最后編輯于
?著作權歸作者所有,轉載或內容合作請聯系作者
平臺聲明:文章內容(如有圖片或視頻亦包括在內)由作者上傳并發布,文章內容僅代表作者本人觀點,簡書系信息發布平臺,僅提供信息存儲服務。
  • 序言:七十年代末,一起剝皮案震驚了整個濱河市,隨后出現的幾起案子,更是在濱河造成了極大的恐慌,老刑警劉巖,帶你破解...
    沈念sama閱讀 230,182評論 6 543
  • 序言:濱河連續發生了三起死亡事件,死亡現場離奇詭異,居然都是意外死亡,警方通過查閱死者的電腦和手機,發現死者居然都...
    沈念sama閱讀 99,489評論 3 429
  • 文/潘曉璐 我一進店門,熙熙樓的掌柜王于貴愁眉苦臉地迎上來,“玉大人,你說我怎么就攤上這事。” “怎么了?”我有些...
    開封第一講書人閱讀 178,290評論 0 383
  • 文/不壞的土叔 我叫張陵,是天一觀的道長。 經常有香客問我,道長,這世上最難降的妖魔是什么? 我笑而不...
    開封第一講書人閱讀 63,776評論 1 317
  • 正文 為了忘掉前任,我火速辦了婚禮,結果婚禮上,老公的妹妹穿的比我還像新娘。我一直安慰自己,他們只是感情好,可當我...
    茶點故事閱讀 72,510評論 6 412
  • 文/花漫 我一把揭開白布。 她就那樣靜靜地躺著,像睡著了一般。 火紅的嫁衣襯著肌膚如雪。 梳的紋絲不亂的頭發上,一...
    開封第一講書人閱讀 55,866評論 1 328
  • 那天,我揣著相機與錄音,去河邊找鬼。 笑死,一個胖子當著我的面吹牛,可吹牛的內容都是我干的。 我是一名探鬼主播,決...
    沈念sama閱讀 43,860評論 3 447
  • 文/蒼蘭香墨 我猛地睜開眼,長吁一口氣:“原來是場噩夢啊……” “哼!你這毒婦竟也來了?” 一聲冷哼從身側響起,我...
    開封第一講書人閱讀 43,036評論 0 290
  • 序言:老撾萬榮一對情侶失蹤,失蹤者是張志新(化名)和其女友劉穎,沒想到半個月后,有當地人在樹林里發現了一具尸體,經...
    沈念sama閱讀 49,585評論 1 336
  • 正文 獨居荒郊野嶺守林人離奇死亡,尸身上長有42處帶血的膿包…… 初始之章·張勛 以下內容為張勛視角 年9月15日...
    茶點故事閱讀 41,331評論 3 358
  • 正文 我和宋清朗相戀三年,在試婚紗的時候發現自己被綠了。 大學時的朋友給我發了我未婚夫和他白月光在一起吃飯的照片。...
    茶點故事閱讀 43,536評論 1 374
  • 序言:一個原本活蹦亂跳的男人離奇死亡,死狀恐怖,靈堂內的尸體忽然破棺而出,到底是詐尸還是另有隱情,我是刑警寧澤,帶...
    沈念sama閱讀 39,058評論 5 363
  • 正文 年R本政府宣布,位于F島的核電站,受9級特大地震影響,放射性物質發生泄漏。R本人自食惡果不足惜,卻給世界環境...
    茶點故事閱讀 44,754評論 3 349
  • 文/蒙蒙 一、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧,春花似錦、人聲如沸。這莊子的主人今日做“春日...
    開封第一講書人閱讀 35,154評論 0 28
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽。三九已至,卻和暖如春,著一層夾襖步出監牢的瞬間,已是汗流浹背。 一陣腳步聲響...
    開封第一講書人閱讀 36,469評論 1 295
  • 我被黑心中介騙來泰國打工, 沒想到剛下飛機就差點兒被人妖公主榨干…… 1. 我叫王不留,地道東北人。 一個月前我還...
    沈念sama閱讀 52,273評論 3 399
  • 正文 我出身青樓,卻偏偏與公主長得像,于是被迫代替她去往敵國和親。 傳聞我的和親對象是個殘疾皇子,可洞房花燭夜當晚...
    茶點故事閱讀 48,505評論 2 379

推薦閱讀更多精彩內容