1. 程式人生 > 其它 >Qt編寫地圖綜合應用59-經緯度座標糾偏

Qt編寫地圖綜合應用59-經緯度座標糾偏

一、前言

地圖應用中都涉及到一個問題就是座標糾偏的問題,這個問題的是因為根據地方規則保密性要求不允許地圖廠商使用標準的GPS座標,而是要用國家定義的偏移標準,或者在此基礎上再做演算法運算,所以這就出現了三種常規的座標標準:GPS座標(WGS-84,國際標準,谷歌地球軟體,硬體GPS模組等)、中國座標偏移標準(GCJ-02,谷歌地圖、騰訊地圖、高德地圖等)、百度座標偏移標準(BD-09,僅僅百度地圖採用)。由於不允許其他座標轉換成GPS座標,所以三種座標互換就剩4種常用轉換:GPS轉百度、GPS轉其他、百度轉其他、其他轉百度,其他就是中國座標偏移標準,目前谷歌地圖、騰訊地圖、高德地圖都採用這個標準。

線上百度地圖提供了Convertor類專門負責這個轉換,只需要呼叫對應translate方法傳入引數即可,這個轉換出來的結果比較理想,缺點就是必須線上使用,畢竟演算法是儲存在伺服器上的保密的,但是很多應用場景是離線使用的,這就需要找一個比較準確的演算法做這個轉換,網上流傳的主要是java、c#、python等語言的版本,於是特意將其改寫成了c++ Qt的版本,親測和線上版本的運算結果基本一致,據說赤道附近可能偏差很大,但是在中國地圖上偏差很小。

二、功能特點

1 省市區域地圖封裝類功能特點

  1. 同時支援閃爍點圖、遷徙圖、區域地圖、世界地圖、儀表盤等。
  2. 可以設定標題、提示資訊、背景顏色、文字顏色、線條顏色、區域顏色等各種顏色。
  3. 可設定城市的名稱、值、經緯度 集合。
  4. 可設定地圖的放大倍數、是否允許滑鼠滾輪縮放。
  5. 內建世界地圖、全國地圖、省份地圖、地區地圖,可以精確到縣,所有地圖全部離線使用。
  6. 內建了各省市json資料檔案轉js檔案功能,如有資料更新自行轉換即可,支援單個檔案轉換和一鍵轉換所有檔案。
  7. 內建了從json檔案或者js檔案獲取該區域的所有名稱和經緯度資訊集合的功能,可以通過該方法獲取到資訊用來顯示。
  8. 依賴瀏覽器元件顯示地圖,提供的demo支援webkit/webengine/miniblink/ie 多種方式載入網頁。
  9. 採用miniblink瀏覽器核心打通了Qt5.6及後續版本+mingw編譯器缺少瀏覽器模組的遺憾,使得整個專案支援所有Qt版本,親測4.7到6.2等任意版本。
  10. 閃爍點遷徙圖等設定的點支援單獨設定顏色。
  11. 提供介面直接獲取點選的點相關資訊,方便程式聯動處理。
  12. 拓展性極強,可以依葫蘆畫瓢自行增加各種精美的echarts元件,做出牛逼的效果。
  13. 內建的儀表盤元件提供互動功能,demo演示中包含了對應的程式碼。
  14. 函式介面友好和統一,使用簡單方便,就一個類。
  15. 支援任意Qt版本、任意系統、任意編譯器。

2 百度地圖封裝類功能特點

  1. 同時支援線上地圖和離線地圖兩種模式。
  2. 同時支援webkit核心、webengine核心、miniblink核心、IE核心。
  3. 支援設定多個標註點,資訊包括名稱、地址、經緯度。
  4. 可設定地圖是否可單擊、拖動、滑鼠滾輪縮放。
  5. 可設定協議版本、祕鑰、主題樣式、中心座標、中心城市、地理編碼位置等。
  6. 可設定地圖縮放比例和級別,縮圖、比例尺、路況資訊等控制元件的可見。
  7. 支援地圖互動,比如滑鼠按下獲取對應位置的經緯度。
  8. 支援查詢路線,可設定起點位置、終點位置、路線模式、路線方式、路線方案(最少時間、最少換乘、最少步行、不乘地鐵、最短距離、避開高速)。
  9. 可顯示點線面工具,可直接在地圖上劃線、點、矩形、圓形等。
  10. 可設定行政區劃,指定某個城市區域繪製圖層,線上地圖自動輸出行政區劃邊界點集合到js檔案給離線地圖使用。
  11. 可靜態或者動態新增多個覆蓋物。支援點、折線、多邊形、矩形、圓形、弧線、點聚合等。
  12. 提供函式介面處理經緯度解析成地址和地址解析成經緯度座標。
  13. 提供的demo直接可以單獨選點執行對應的處理比如路線查詢。
  14. 可以拿到路線查詢到的點座標資訊集合,比如用於機器人座標導航等。
  15. 封裝了豐富的函式比如刪除指定點和所有點,刪除指定覆蓋物和所有覆蓋物等。
  16. 標註點彈框資訊可以自定義內容,標準html格式。
  17. 標註點單擊事件可選 0-不處理 1-自己彈框 2-傳送訊號。
  18. 標註點可設定動畫效果 0-不處理 1-跳動 2-墜落
  19. 標註點可設定本地圖片檔案等。
  20. 函式介面友好和統一,使用簡單方便,就一個類。
  21. 支援js動態互動新增點、刪除點、清空點、重置點,不需要重新整理頁面。
  22. 支援任意Qt版本、任意系統、任意編譯器。

3 離線地圖下載類功能特點

  1. 多執行緒同步下載多級別瓦片地圖,不卡介面。
  2. 內建多個離線地圖下載請求地址,自動隨機選擇一個傳送請求。
  3. 下載地圖型別同時支援街道圖和衛星圖。
  4. 自動計算可視區域或者行政區域的下載瓦片數量。
  5. 下載的級別可以自定義範圍和選擇。
  6. 每個瓦片下載完成都發送訊號通知,引數包括下載用時。
  7. 可設定下載最大超時時間,超過了則丟棄跳到下一個下載任務。
  8. 實時顯示下載進度,以及當前級別已經下載的瓦片數和總瓦片數。
  9. 下載過程中可以停止下載,下載完成自動統計總用時。
  10. 內建經緯度和螢幕座標互相轉換函式。
  11. 目前支援百度地圖,其他地圖比如谷歌地圖、騰訊地圖、高德地圖可以定製。
  12. 函式介面友好和統一,使用簡單方便,就一個類。
  13. 支援任意Qt版本、任意系統、任意編譯器。

4 省市輪廓下載類功能特點

  1. 定時器排隊下載省市輪廓圖點座標集合儲存到JS檔案。
  2. 支援一個行政區域多個不規則區域下載。
  3. 自動計算行政區域的下載輪廓數量。
  4. 可精確選擇省份、市區、縣城,也可直接輸入行政區域的名稱。
  5. 可以設定下載間隔、隨時開始下載和停止下載。
  6. 提供編輯邊界功能,可以直接在地圖上編輯好不規則區域的點集合,然後獲取邊界點集合資料,這個可以用來自己繪製區域拿到資料,比如某個鄉鎮甚至某個小區的行政區域資料,很牛逼。

三、體驗地址

  1. 體驗地址:https://pan.baidu.com/s/1ZxG-oyUKe286LPMPxOrO2A 提取碼:o05q 檔名:bin_map.zip
  2. 國內站點:https://gitee.com/feiyangqingyun
  3. 國際站點:https://github.com/feiyangqingyun
  4. 個人主頁:https://blog.csdn.net/feiyangqingyun
  5. 知乎主頁:https://www.zhihu.com/people/feiyangqingyun/

四、效果圖

五、相關程式碼

//常用轉換就4種: GPS轉百度、GPS轉其他、百度轉其他、其他轉百度
//WGS-84: 國際標準,GPS座標(Google Earth使用、或者GPS模組)
//GCJ-02: 中國座標偏移標準,Google Map、高德、騰訊使用
//BD-09:  百度座標偏移標準,Baidu Map使用
static QPointF gcj2bd(const QPointF &point);
static QPointF bd2gcj(const QPointF &point);
static double transformLng(double lng, double lat);
static double transformLat(double lng, double lat);
static QPointF wgs2gcj(const QPointF &point);
static QPointF wgs2bd(const QPointF &point);

//圓周率轉換量
double x_pi = M_PI * 3000.0 / 180.0;
QPointF MapHelper::gcj2bd(const QPointF &point)
{
    double x = point.x();
    double y = point.y();
    double z = qSqrt(x * x + y * y) + 0.00002 * qSin(y * x_pi);
    double theta = qAtan2(y, x) + 0.000003 * qCos(x * x_pi);
    double lng = z * qCos(theta) + 0.0065;
    double lat = z * qSin(theta) + 0.006;
    return QPointF(lng, lat);
}

QPointF MapHelper::bd2gcj(const QPointF &point)
{
    double x = point.x() - 0.0065;
    double y = point.y() - 0.006;
    double z = qSqrt(x * x + y * y) - 0.00002 * qSin(y * x_pi);
    double theta = qAtan2(y, x) - 0.000003 * qCos(x * x_pi);
    double lng = z * qCos(theta);
    double lat = z * qSin(theta);
    return QPointF(lng, lat);
}

double MapHelper::transformLng(double lng, double lat)
{
    double ret = 300.0 + lat + 2.0 * lng + 0.1 * lat * lat + 0.1 * lat * lng + 0.1 * qSqrt(qAbs(lat));
    ret += (20.0 * qSin(6.0 * lat * M_PI) + 20.0 * qSin(2.0 * lat * M_PI)) * 2.0 / 3.0;
    ret += (20.0 * qSin(lat * M_PI) + 40.0 * qSin(lat / 3.0 * M_PI)) * 2.0 / 3.0;
    ret += (150.0 * qSin(lat / 12.0 * M_PI) + 300.0 * qSin(lat / 30.0 * M_PI)) * 2.0 / 3.0;
    return ret;
}

double MapHelper::transformLat(double lng, double lat)
{
    double ret = -100.0 + 2.0 * lat + 3.0 * lng + 0.2 * lng * lng + 0.1 * lat * lng + 0.2 * qSqrt(qAbs(lat));
    ret += (20.0 * qSin(6.0 * lat * M_PI) + 20.0 * qSin(2.0 * lat * M_PI)) * 2.0 / 3.0;
    ret += (20.0 * qSin(lng * M_PI) + 40.0 * qSin(lng / 3.0 * M_PI)) * 2.0 / 3.0;
    ret += (160.0 * qSin(lng / 12.0 * M_PI) + 320 * qSin(lng * M_PI / 30.0)) * 2.0 / 3.0;
    return ret;
}

//衛星橢球座標投影到平面地圖座標系的投影因子
double a = 6378245.0;
//橢球的偏心率
double ee = 0.00669342162296594323;
QPointF MapHelper::wgs2gcj(const QPointF &point)
{
    double x = point.x();
    double y = point.y();
    double lng = transformLng(y - 35.0, x - 105.0);
    double lat = transformLat(y - 35.0, x - 105.0);

    double rad = y / 180.0 * M_PI;
    double magic = qSin(rad);
    magic = 1 - ee * magic * magic;
    double sqrtMagic = qSqrt(magic);

    lng = x + (lng * 180.0) / (a / sqrtMagic * qCos(rad) * M_PI);
    lat = y + (lat * 180.0) / ((a * (1 - ee)) / (magic * sqrtMagic) * M_PI);
    return QPointF(lng, lat);
}

QPointF MapHelper::wgs2bd(const QPointF &point)
{
    //GPS轉百度要經過兩重轉換
    return gcj2bd(wgs2gcj(point));
}