國內各地圖API座標系統比較與轉換


源地址:http://blog.csdn.net/yorling/article/details/9175913


一、各個座標系的概況

        衆所周知地球是一個不規則橢圓體,GIS中的座標系定義由基準面和地圖投影兩組參數確定,而基準面的定義則由特定橢球體及其對應的轉換參數確定。 基準面是利用特定橢球體對特定地區地球表面的逼近,因此每個國家或地區均有各自的基準面。基準面是在橢球體基礎上建立的,橢球體可以對應多個基準面,而基準面只能對應一個橢球體。意思就是無論是谷歌地圖、搜搜地圖還是高德地圖、百度地圖區別只是針對不同的大地地理座標系標準制作的經緯度,不存在準不準的問題,大家都是準的只是參照物或者說是標準不一樣。谷歌地圖採用的是WGS84地理座標系(中國範圍除外),谷歌中國地圖和搜搜中國地圖採用的是GCJ02地理座標系,百度採用的是BD09座標系,而設備一般包含GPS芯片或者北斗芯片獲取的經緯度爲WGS84地理座標系,爲什麼不統一用WGS84地理座標系這就是國家地理測繪總局對於出版地圖的要求,出版地圖必須符合GCJ02座標系標準了,也就是國家規定不能直接使用WGS84地理座標系。所以定位大家感覺不準確很多又叫出版地圖爲火星地圖其實只是座標系不一樣而已。這就是爲什麼設備採集的經緯度在地圖上顯示的時候經常有很大的偏差,遠遠超出民用GPS 10米偏移量的技術規範。

以上參考自:haotsp.com 


總結:

WGS84座標系:即地球座標系,國際上通用的座標系。

GCJ02座標系:即火星座標系,WGS84座標系經加密後的座標系。

BD09座標系:即百度座標系,GCJ02座標系經加密後的座標系。

搜狗座標系、圖吧座標系等,估計也是在GCJ02基礎上加密而成的。

 

二、各個地圖API採用的座標系

API 座標系
百度地圖API 百度座標
騰訊搜搜地圖API 火星座標
搜狐搜狗地圖API 搜狗座標*
阿里雲地圖API 火星座標
圖吧MapBar地圖API 圖吧座標
高德MapABC地圖API 火星座標
靈圖51ditu地圖API 火星座標

注1:百度地圖使用百度座標,支持從地球座標和火星座標導入成百度座標,但無法導出。並且批量座標轉換一次只能轉換20個(待驗證)。

注2:搜狗地圖支持直接顯示地球座標,支持地球座標、火星座標、百度座標導入成搜狗座標,同樣,搜狗座標也無法導出。

個人認爲:採用自家座標體系,而不採用國內通用的火星座標體系,實在是自尋短處。當然,百度是因爲做的足夠大、足夠好,所以很霸道,也爲以後一統天下而不讓別人瓜分之而做準備吧。搜狗雖然用自家座標體系,但能將地球座標直接導入,此舉也屬唯一。而圖吧地圖不知道學什麼加密方式,以前用地球座標用的好好的,現在用圖吧自己的座標,難道是因爲給百度做過所以也來了這麼一招?或者沿用百度?不得而知。

本文的目的在於:做地圖開發的時候,不希望被一家地圖API遷就,所以採用火星座標是正確的選擇,希望本文能夠對選擇使用誰家API的開發者提供一點幫助吧。就我個人而言,我絕不會使用非火星座標系統的地圖API,雖然百度地圖API很好很強大確實很吸引我。


以上參考自:http://rovertang.com/labs/map-compare/


三、各個座標系的相互轉換

1.火星座標系 (GCJ-02) 與百度座標系 (BD-09) 的轉換算法,其中 bd_encrypt 將 GCJ-02 座標轉換成 BD-09 座標, bd_decrypt 反之。

[java] view plaincopy
  1. void bd_encrypt(double gg_lat, double gg_lon, double &bd_lat, double &bd_lon)  
  2.   
  3. {  
  4.     double x_pi = 3.14159265358979324 * 3000.0 / 180.0;  
  5.     double x = gg_lon, y = gg_lat;  
  6.   
  7.     double z = sqrt(x * x + y * y) + 0.00002 * sin(y * x_pi);  
  8.   
  9.     double theta = atan2(y, x) + 0.000003 * cos(x * x_pi);  
  10.   
  11.     bd_lon = z * cos(theta) + 0.0065;  
  12.   
  13.     bd_lat = z * sin(theta) + 0.006;  
  14.   
  15. }  
  16.   
  17.    
  18.   
  19. void bd_decrypt(double bd_lat, double bd_lon, double &gg_lat, double &gg_lon)  
  20.   
  21. {  
  22.   
  23.     double x = bd_lon - 0.0065, y = bd_lat - 0.006;  
  24.   
  25.     double z = sqrt(x * x + y * y) - 0.00002 * sin(y * x_pi);  
  26.   
  27.     double theta = atan2(y, x) - 0.000003 * cos(x * x_pi);  
  28.   
  29.     gg_lon = z * cos(theta);  
  30.   
  31.     gg_lat = z * sin(theta);  
  32.   
  33. }  

2.地球座標系 (WGS-84) 到火星座標系 (GCJ-02) 的轉換算法

WGS-84 到 GCJ-02 的轉換(即 GPS 加偏)算法

[java] view plaincopy
  1. using System;  
  2.   
  3. namespace Navi  
  4. {  
  5.     class EvilTransform  
  6.     {  
  7.         const double pi = 3.14159265358979324;  
  8.   
  9.         //  
  10.         // Krasovsky 1940  
  11.         //  
  12.         // a = 6378245.0, 1/f = 298.3  
  13.         // b = a * (1 - f)  
  14.         // ee = (a^2 - b^2) / a^2;  
  15.         const double a = 6378245.0;  
  16.         const double ee = 0.00669342162296594323;  
  17.   
  18.         //  
  19.         // World Geodetic System ==> Mars Geodetic System  
  20.         public static void transform(double wgLat, double wgLon, out double mgLat, out double mgLon)  
  21.         {  
  22.             if (outOfChina(wgLat, wgLon))  
  23.             {  
  24.                 mgLat = wgLat;  
  25.                 mgLon = wgLon;  
  26.                 return;  
  27.             }  
  28.             double dLat = transformLat(wgLon - 105.0, wgLat - 35.0);  
  29.             double dLon = transformLon(wgLon - 105.0, wgLat - 35.0);  
  30.             double radLat = wgLat / 180.0 * pi;  
  31.             double magic = Math.Sin(radLat);  
  32.             magic = 1 - ee * magic * magic;  
  33.             double sqrtMagic = Math.Sqrt(magic);  
  34.             dLat = (dLat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * pi);  
  35.             dLon = (dLon * 180.0) / (a / sqrtMagic * Math.Cos(radLat) * pi);  
  36.             mgLat = wgLat + dLat;  
  37.             mgLon = wgLon + dLon;  
  38.         }  
  39.   
  40.         static bool outOfChina(double lat, double lon)  
  41.         {  
  42.             if (lon < 72.004 || lon > 137.8347)  
  43.                 return true;  
  44.             if (lat < 0.8293 || lat > 55.8271)  
  45.                 return true;  
  46.             return false;  
  47.         }  
  48.   
  49.         static double transformLat(double x, double y)  
  50.         {  
  51.             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));  
  52.             ret += (20.0 * Math.Sin(6.0 * x * pi) + 20.0 * Math.Sin(2.0 * x * pi)) * 2.0 / 3.0;  
  53.             ret += (20.0 * Math.Sin(y * pi) + 40.0 * Math.Sin(y / 3.0 * pi)) * 2.0 / 3.0;  
  54.             ret += (160.0 * Math.Sin(y / 12.0 * pi) + 320 * Math.Sin(y * pi / 30.0)) * 2.0 / 3.0;  
  55.             return ret;  
  56.         }  
  57.   
  58.         static double transformLon(double x, double y)  
  59.         {  
  60.             double ret = 300.0 + x + 2.0 * y + 0.1 * x * x + 0.1 * x * y + 0.1 * Math.Sqrt(Math.Abs(x));  
  61.             ret += (20.0 * Math.Sin(6.0 * x * pi) + 20.0 * Math.Sin(2.0 * x * pi)) * 2.0 / 3.0;  
  62.             ret += (20.0 * Math.Sin(x * pi) + 40.0 * Math.Sin(x / 3.0 * pi)) * 2.0 / 3.0;  
  63.             ret += (150.0 * Math.Sin(x / 12.0 * pi) + 300.0 * Math.Sin(x / 30.0 * pi)) * 2.0 / 3.0;  
  64.             return ret;  
  65.         }  
  66.     }  
  67. }  


以上參考自:http://www.xue5.com/Mobile/iOS/679842.html

 

3.百度在線轉換API

[java] view plaincopy
  1. http://api.map.baidu.com/ag/coord/convert?from=0&to=4&x=longitude&y=latitude  
  2. from: 來源座標系   (0表示原始GPS座標,2表示Google座標)  
  3. to: 轉換後的座標  (4就是百度自己啦,好像這個必須是4才行)  
  4. x: 精度  
  5. y: 緯度  

請求之後會返回一串Json

[java] view plaincopy
  1. {  
  2.     "error":0,  
  3.     "x":"MTIxLjUwMDIyODIxNDk2",  
  4.     "y":"MzEuMjM1ODUwMjYwMTE3"  
  5. }  
  6. error:是結果是否出錯標誌位,"0"表示OK  
  7. x: 百度座標系的精度(Base64加密)  
  8. y: 百度座標系的緯度(Base64加密)  


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


[java] view plaincopy
  1. import java.io.BufferedReader;  
  2. import java.io.IOException;  
  3. import java.io.InputStream;  
  4. import java.io.InputStreamReader;  
  5. import java.io.OutputStreamWriter;  
  6. import java.net.URL;  
  7. import java.net.URLConnection;  
  8. import com.sun.org.apache.xerces.internal.impl.dv.util.Base64;  
  9. public class BaiduAPIConverter extends Thread {  
  10.   public static void testPost(String x, String y) throws IOException {  
  11.     try {  
  12.       URL url = new URL("http://api.map.baidu.com/ag/coord/convert?from=2&to=4&x="+ x + "&y=" + y);  
  13.       URLConnection connection = url.openConnection();  
  14.       connection.setDoOutput(true);  
  15.       OutputStreamWriter out = new OutputStreamWriter(connection.getOutputStream(), "utf-8");  
  16.       // remember to clean up  
  17.       out.flush();  
  18.       out.close();  
  19.       // 一旦發送成功,用以下方法就可以得到服務器的迴應:  
  20.       String sCurrentLine, sTotalString;  
  21.       sCurrentLine = sTotalString = "";  
  22.       InputStream l_urlStream;  
  23.       l_urlStream = connection.getInputStream();  
  24.       BufferedReader l_reader = new BufferedReader(new InputStreamReader(l_urlStream));  
  25.       while ((sCurrentLine = l_reader.readLine()) != null) {  
  26.         if (!sCurrentLine.equals(""))  
  27.           sTotalString += sCurrentLine;  
  28.       }  
  29.       sTotalString = sTotalString.substring(1, sTotalString.length() - 1);  
  30.       String[] results = sTotalString.split("\\,");  
  31.       if (results.length == 3) {  
  32.         if (results[0].split("\\:")[1].equals("0")) {  
  33.           String mapX = results[1].split("\\:")[1];  
  34.           String mapY = results[2].split("\\:")[1];  
  35.           mapX = mapX.substring(1, mapX.length() - 1);  
  36.           mapY = mapY.substring(1, mapY.length() - 1);  
  37.           mapX = new String(Base64.decode(mapX));  
  38.           mapY = new String(Base64.decode(mapY));  
  39.           System.out.println("\t" + mapX + "\t" + mapY);  
  40.         }  
  41.       }  
  42.      sleep(10000);  
  43.     } catch (InterruptedException e) {  
  44.       // TODO Auto-generated catch block  
  45.       e.printStackTrace();  
  46.     }  
  47.   }  
  48.   /** 
  49.    * @param args 
  50.    * @throws IOException 
  51.    */  
  52.   public static void main(String[] args) throws IOException {  
  53.     testPost("120.151379""30.184678");  
  54.     System.out.println("ok");  
  55.   }  
  56. }  

到這裏也差不多好了,主要的代碼都寫出來了,其他的您就自己寫吧。


以上參考自:http://scalpel.me/archives/136/


四、重點啊,原來百度有內置轉換方法,這下可以不侷限於百度定位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。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章