在這個神奇的國度里,我們總得學習一些有中國特色的東東,例如“火星坐標”。也許有人還不知道這是什么玩意,我就簡要介紹一下吧。 如果你有帶GPS模塊的智能手機,打開定位功能,然后訪問Google地圖。只要你身處中國大陸,你就會發現定位不準,大概有幾百米的偏差。然而運行一些導航軟件,你又會發現定位很準確,說明手機的GPS模塊確實是正常的。 這種現象是怎么造成的呢?答案是人為造成的。簡單來說,GPS模塊獲取到的坐標是WGS84坐標系的,中國政府出于種種目的的考慮,不允許中國的地圖使用國際通用的WGS84坐標系,而非要加上一些偏移,這樣的坐標系就俗稱“火星坐標系”。而Google地圖采用的也是加偏移過的火星坐標系,但GPS模塊傳給它的坐標卻沒有加偏移,于是就出現幾百米的偏差了。這樣的后果就是沒法做需要很高精度的地理位置的應用了,就像那個悲劇的Google地圖一樣,你迷路時無法指望它告訴你正確的位置。 經過不懈的努力, 終于發現一位牛人用C語言寫了一個算法, 不過他要用到一個數據文件, 而且比較大, 放在手機客戶端一跑就內存崩潰了, 所以覺得不可取, 于是把他的C代碼轉化為C#的WebService服務. 代碼如下, 供大家參考.希望對大家有所幫助!
class Program{const double M_PI = 3.14159265358979323846264338327950288;const double M_E = 2.71828182845904523536028747135266250;public class MapCoord{ public int lng { set; get; } //12151表示121.51 public int lat { set; get; } //3130表示31.30 public int x_off { set; get; } //地圖x軸偏移像素值 public int y_off { set; get; } //地圖y軸偏移像素值}/// <summary>/// 自定義比較類/// </summary>public class myReverserClass : IComparer{ public int Compare(object x, object y) { MapCoord data1 = (MapCoord)x, data2 = (MapCoord)y; int det_lng = data1.lng - data2.lng; if (det_lng != 0) return det_lng; else return data1.lat - data2.lat; }}//這就需要一個把經緯度轉換成地圖xy軸坐標的算法:private static double lngToPixel(double lng, int zoom){ return (lng + 180) * (256L << zoom) / 360;}private static double latToPixel(double lat, int zoom){ double siny = Math.Sin(lat * M_PI / 180); double y = Math.Log((1 + siny) / (1 - siny)); return (128 << zoom) * (1 - y / (2 * M_PI));}//xy軸坐標加上對應的地圖xy軸的偏移量,最后還要反過來將最終正確的地圖xy軸坐標轉換成正確的經緯度private static double pixelToLng(double pixelX, int zoom){ return pixelX * 360 / (256L << zoom) - 180;}private static double pixelToLat(double pixelY, int zoom){ double y = 2 * M_PI * (1 - pixelY / (128 << zoom)); double z = Math.Pow(M_E, y); double siny = (z - 1) / (z + 1); return Math.Asin(siny) * 180 / M_PI;}/// <summary>/// 將字節轉化為具體的數據對象/// </summary>/// <param name="buf"></param>/// <returns></returns>private static MapCoord getMapCoordFromBytes(byte[] buf){ //數據文檔結構是八字節為一個坐標及其偏移量,分別為經度,緯度,x偏移量,y偏移量; 每兩字節為一個數據 MapCoord coord = new MapCoord(); byte[] b1 = new byte[2], b2 = new byte[2], b3 = new byte[2], b4 = new byte[2]; Array.Copy(buf, 0, b1, 0, 2); Array.Copy(buf, 2, b2, 0, 2); Array.Copy(buf, 4, b3, 0, 2); Array.Copy(buf, 6, b4, 0, 2); coord.lng = System.BitConverter.ToInt16(b1, 0); coord.lat = System.BitConverter.ToInt16(b2, 0); coord.x_off = System.BitConverter.ToInt16(b3, 0); coord.y_off = System.BitConverter.ToInt16(b4, 0); return coord;}/// <summary>/// WGS84(GPS)坐標轉火星坐標/// </summary>/// <param name="lat">緯度</param>/// <param name="lng">經度</param>public static void WGS2Mars(double lat, double lng){ //讀取數據文件 //這里讀取文件的地方可以單獨提出來, 讀一次之后保存到內存里, 讀取時比較耗時間. 或者放到數據庫中去. FileStream fs = new FileStream("offset.dat", FileMode.OpenOrCreate, FileAccess.Read); BinaryReader br = new BinaryReader(fs); int size = (int)fs.Length / 8; ArrayList array = new ArrayList(); for (int i = 0; i < size; i ++) { //按八個字節八個字節來讀取, 放在MapCoord對象中,并添加到ArrayList中 byte[] source = br.ReadBytes(8); array.Add(getMapCoordFromBytes(source)); } br.Close(); fs.Close(); //將要查找的坐標放置在MapCoord對象中 MapCoord search = new MapCoord(); search.lat = (int)(lat * 100); search.lng = (int)(lng * 100); myReverserClass rc = new myReverserClass(); //執行查找, 查詢結果將返回array中的索引值 int x = array.BinarySearch(0, array.Count, search, rc); //取得查找到的結果并進行計算 MapCoord ret = (MapCoord)array[x]; double pixY = latToPixel(lat, 18); double pixX = lngToPixel(lng, 18); pixY += ret.y_off; pixX += ret.x_off; lat = pixelToLat(pixY, 18); lng = pixelToLng(pixX, 18); //輸出校正后的結果 Console.WriteLine("歡迎來到火星,坐標: lat:{0},lng:{1} !!!", lat, lng);}static void Main(string[] args){ //測試一下 WGS2Mars(30.283780,120.116356); Console.ReadLine();} }
OK,大功告成,火星人民歡迎您!!
參考資料: 原C語言帖 http://xcodev.com/Wordpress/?p=131
原文地址: http://freshflower.VEvb.com/blog/1606960
新聞熱點
疑難解答