記錄--Openlayers 高德騰訊、百度、天地圖坐標相互轉換

来源:https://www.cnblogs.com/smileZAZ/archive/2022/06/18/16388322.html
-Advertisement-
Play Games

這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 在地圖開發過程中,坐標的轉換是很常用的功能,國內的話一般西安80(EPSG:4610)、北京54(EPSG:2433)轉WGS84比較多,不同坐標系轉換,只要知道EPSG碼,通過 Openlayers 的方法就可以轉換。 但是,像國內商用 ...


這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助

在地圖開發過程中,坐標的轉換是很常用的功能,國內的話一般西安80(EPSG:4610)、北京54(EPSG:2433)轉WGS84比較多,不同坐標系轉換,只要知道EPSG碼,通過 Openlayers 的方法就可以轉換。

但是,像國內商用的地圖(高德、騰訊、百度),要求數據加密,一般通過 GCJ-02 或者 BD-09 加密,不能簡單通過 openlayers 的轉換方法實現,需要手動使用演算法完成轉換。

本教程演算法來自網路,目前提供點數據的轉換,對於線和麵推薦在資料庫或者後端實現轉換。

核心代碼展示

通用部分

//定義一些常量
    var PI = 3.1415926535897932384626;
    var a = 6378245.0;
    var ee = 0.00669342162296594323;

    let transformlat = function (lng, lat) {
        var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
        return ret
    }

    let transformlng = function (lng, lat) {
        var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
        return ret
    }
/**
     * 判斷是否在國內,不在國內則不做偏移
     * @param lng
     * @param lat
     * @returns {boolean}
     */
    let out_of_china = function (lng, lat) {
        return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
    }

天地圖坐標轉高德

 /**
     * WGS84轉GCj02
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    let wgs84togcj02 = function (lng, lat) {
        if (out_of_china(lng, lat)) {
            return [lng, lat]
        } else {
            var dlat = transformlat(lng - 105.0, lat - 35.0);
            var dlng = transformlng(lng - 105.0, lat - 35.0);
            var radlat = lat / 180.0 * PI;
            var magic = Math.sin(radlat);
            magic = 1 - ee * magic * magic;
            var sqrtmagic = Math.sqrt(magic);
            dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
            dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
            var mglat = lat + dlat;
            var mglng = lng + dlng;
            return [mglng, mglat]
        }
    }

高德坐標轉天地圖

/**
     * GCJ02 轉換為 WGS84
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    let gcj02towgs84 = function (lng, lat) {
        if (out_of_china(lng, lat)) {
            return [lng, lat]
        } else {
            var dlat = transformlat(lng - 105.0, lat - 35.0);
            var dlng = transformlng(lng - 105.0, lat - 35.0);
            var radlat = lat / 180.0 * PI;
            var magic = Math.sin(radlat);
            magic = 1 - ee * magic * magic;
            var sqrtmagic = Math.sqrt(magic);
            dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
            dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
            let mglat = lat + dlat;
            let mglng = lng + dlng;
            return [lng * 2 - mglng, lat * 2 - mglat]
        }
    }

百度坐標系 (BD-09) 與 火星坐標系 (GCJ-02)的轉換

/**
     * 百度坐標系 (BD-09) 與 火星坐標系 (GCJ-02)的轉換
     * 即 百度 轉 谷歌、高德
     * @param bd_lon
     * @param bd_lat
     * @returns {*[]}
     */
    let bd09togcj02 = function (bd_lon, bd_lat) {
        var x_pi = 3.14159265358979324 * 3000.0 / 180.0;
        var x = bd_lon - 0.0065;
        var y = bd_lat - 0.006;
        var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        var gg_lng = z * Math.cos(theta);
        var gg_lat = z * Math.sin(theta);
        return [gg_lng, gg_lat]
    }

火星坐標系 (GCJ-02) 與百度坐標系 (BD-09) 的轉換

 /**
     * 火星坐標系 (GCJ-02) 與百度坐標系 (BD-09) 的轉換
     * 即谷歌、高德 轉 百度
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    let gcj02tobd09 = function (lng, lat) {
        var x_PI = 3.14159265358979324 * 3000.0 / 180.0;
        var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
        var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);
        var bd_lng = z * Math.cos(theta) + 0.0065;
        var bd_lat = z * Math.sin(theta) + 0.006;
        return [bd_lng, bd_lat]
    }

完整案例:

<html lang="en">
<head>
    <meta charset="utf-8">
    <!--註意:openlayers 原版的比較慢,這裡引起自己伺服器版-->
    <link rel="stylesheet" href="http://openlayers.vip/examples/css/ol.css" type="text/css">
    <style>
        /* 註意:這裡必須給高度,否則地圖初始化之後不顯示;一般是計算得到高度,然後才初始化地圖 */
        .map {
            height: 400px;
            width: 100%;
            float: left;
        }
    </style>
    <!--註意:openlayers 原版的比較慢,這裡引起自己伺服器版-->
    <script src="http://openlayers.vip/examples/resources/ol.js"></script>
    <script src="./tiandituLayers.js"></script>
    <title>OpenLayers example</title>
</head>
<body>
<h2>Feature transfer</h2>
<!--地圖容器,需要指定 id -->
<div id="map" class="map"></div>
<!--註意:本示例將 高德騰訊坐標設置為黑色;將百度坐標設置為黃色 -->
<!--註意:本示例將 高德騰訊坐標轉為WGS84顏色設置為粉色;將百度坐標轉為WS84顏色設置為綠色 -->
<script type="text/javascript">
    var map = new ol.Map({
        // 地圖容器
        target: 'map',
        // 地圖圖層,比如底圖、矢量圖等
        layers: [
            getIMG_CLayer(),
            getIBO_CLayer(),
            getCIA_CLayer(),
        ],
        // 地圖視野
        view: new ol.View({
            projection: "EPSG:4326",
            // 定位
            center: [116, 39],
            // 縮放
            zoom: 4,
            maxZoom: 18,
            minZoom: 1,
        })
    });


    var xy = [116.391232637988,39.907157016256974];
    // 初始點
    var originPoint = new ol.Feature({
        geometry: new ol.geom.Point(xy),
        name: 'My Point'
    });

    // 矢量圖層
    var layer = initVectorLayer();

    /**
     * @todo 矢量圖層
     * @returns {VectorLayer}
     * @constructor
     */
    function initVectorLayer() {
        //實例化一個矢量圖層Vector作為繪製層
        let source = new ol.source.Vector();
        //創建一個圖層
        let customVectorLayer = new ol.layer.Vector({
            source: source,
            zIndex: 2,
            //設置樣式
            style: new ol.style.Style({
                //邊框樣式
                stroke: new ol.style.Stroke({
                    color: 'red',
                    width: 5,
                    lineDash: [3, 5]
                }),
                //填充樣式
                fill: new ol.style.Fill({
                    color: 'rgba(0, 0, 255, 0.3)',
                }),
                image: new ol.style.Circle({
                    radius: 9,
                    fill: new ol.style.Fill({
                        color: 'red',
                    })
                })
            }),
        });
        //將繪製層添加到地圖容器中
        map.addLayer(customVectorLayer);

        customVectorLayer.getSource().addFeatures([originPoint]);

        var extent = customVectorLayer.getSource().getExtent();

        map.getView().fit(extent, {
            duration: 1,//動畫的持續時間,
            callback: null,
        });
        return customVectorLayer;
    }

    // =====坐標轉換工具 start ====================================================================================
    //定義一些常量
    var PI = 3.1415926535897932384626;
    var a = 6378245.0;
    var ee = 0.00669342162296594323;

    let transformlat = function (lng, lat) {
        var ret = -100.0 + 2.0 * lng + 3.0 * lat + 0.2 * lat * lat + 0.1 * lng * lat + 0.2 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lat * PI) + 40.0 * Math.sin(lat / 3.0 * PI)) * 2.0 / 3.0;
        ret += (160.0 * Math.sin(lat / 12.0 * PI) + 320 * Math.sin(lat * PI / 30.0)) * 2.0 / 3.0;
        return ret
    }

    let transformlng = function (lng, lat) {
        var ret = 300.0 + lng + 2.0 * lat + 0.1 * lng * lng + 0.1 * lng * lat + 0.1 * Math.sqrt(Math.abs(lng));
        ret += (20.0 * Math.sin(6.0 * lng * PI) + 20.0 * Math.sin(2.0 * lng * PI)) * 2.0 / 3.0;
        ret += (20.0 * Math.sin(lng * PI) + 40.0 * Math.sin(lng / 3.0 * PI)) * 2.0 / 3.0;
        ret += (150.0 * Math.sin(lng / 12.0 * PI) + 300.0 * Math.sin(lng / 30.0 * PI)) * 2.0 / 3.0;
        return ret
    }

    /**
     * 判斷是否在國內,不在國內則不做偏移
     * @param lng
     * @param lat
     * @returns {boolean}
     */
    let out_of_china = function (lng, lat) {
        return (lng < 72.004 || lng > 137.8347) || ((lat < 0.8293 || lat > 55.8271) || false);
    }

    /**
     * WGS84轉GCj02
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    let wgs84togcj02 = function (lng, lat) {
        if (out_of_china(lng, lat)) {
            return [lng, lat]
        } else {
            var dlat = transformlat(lng - 105.0, lat - 35.0);
            var dlng = transformlng(lng - 105.0, lat - 35.0);
            var radlat = lat / 180.0 * PI;
            var magic = Math.sin(radlat);
            magic = 1 - ee * magic * magic;
            var sqrtmagic = Math.sqrt(magic);
            dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
            dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
            var mglat = lat + dlat;
            var mglng = lng + dlng;
            return [mglng, mglat]
        }
    }

    /**
     * GCJ02 轉換為 WGS84
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    let gcj02towgs84 = function (lng, lat) {
        if (out_of_china(lng, lat)) {
            return [lng, lat]
        } else {
            var dlat = transformlat(lng - 105.0, lat - 35.0);
            var dlng = transformlng(lng - 105.0, lat - 35.0);
            var radlat = lat / 180.0 * PI;
            var magic = Math.sin(radlat);
            magic = 1 - ee * magic * magic;
            var sqrtmagic = Math.sqrt(magic);
            dlat = (dlat * 180.0) / ((a * (1 - ee)) / (magic * sqrtmagic) * PI);
            dlng = (dlng * 180.0) / (a / sqrtmagic * Math.cos(radlat) * PI);
            let mglat = lat + dlat;
            let mglng = lng + dlng;
            return [lng * 2 - mglng, lat * 2 - mglat]
        }
    }

    /**
     * 百度坐標系 (BD-09) 與 火星坐標系 (GCJ-02)的轉換
     * 即 百度 轉 谷歌、高德
     * @param bd_lon
     * @param bd_lat
     * @returns {*[]}
     */
    let bd09togcj02 = function (bd_lon, bd_lat) {
        var x_pi = 3.14159265358979324 * 3000.0 / 180.0;
        var x = bd_lon - 0.0065;
        var y = bd_lat - 0.006;
        var z = Math.sqrt(x * x + y * y) - 0.00002 * Math.sin(y * x_pi);
        var theta = Math.atan2(y, x) - 0.000003 * Math.cos(x * x_pi);
        var gg_lng = z * Math.cos(theta);
        var gg_lat = z * Math.sin(theta);
        return [gg_lng, gg_lat]
    }

    /**
     * 火星坐標系 (GCJ-02) 與百度坐標系 (BD-09) 的轉換
     * 即谷歌、高德 轉 百度
     * @param lng
     * @param lat
     * @returns {*[]}
     */
    let gcj02tobd09 = function (lng, lat) {
        var x_PI = 3.14159265358979324 * 3000.0 / 180.0;
        var z = Math.sqrt(lng * lng + lat * lat) + 0.00002 * Math.sin(lat * x_PI);
        var theta = Math.atan2(lat, lng) + 0.000003 * Math.cos(lng * x_PI);
        var bd_lng = z * Math.cos(theta) + 0.0065;
        var bd_lat = z * Math.sin(theta) + 0.006;
        return [bd_lng, bd_lat]
    }

    // =====坐標轉換工具 end ====================================================================================

    /**
     * 添加點到地圖
     * @param geom
     * @param color 顏色
     * @returns {Feature|Feature|null}
     */
    function addFeature(geom, color) {
        let temp = new ol.Feature({
            geometry: new ol.geom.Point(geom),
            name: 'My Point'
        });
        let style = new ol.style.Style({
            image: new ol.style.Circle({
                radius: 9,
                fill: new ol.style.Fill({
                    color: color || 'blue',
                })
            })
        });
        temp.setStyle(style);
        layer.getSource().addFeatures([temp]);
        move();
        return temp;
    }

    // 定位到圖層
    function move() {
        var extent = layer.getSource().getExtent();
        map.getView().fit(extent, {
            duration: 1,//動畫的持續時間,
            callback: null,
        });
    }

    // 記錄高德騰訊坐標對象
    var cjFeature;

    function toCJ02() {
        // 高德騰訊坐標設置為黑色
        cjFeature = addFeature(wgs84togcj02(xy[0], xy[1]), 'black')
    }

    // 高德騰訊坐標轉WGS84
    function CJ02TO() {
        if(!cjFeature){
            return;
        }
        let cjGeom = cjFeature.getGeometry().getCoordinates();
        // 還原為WGS坐標,設置為粉色
        addFeature(gcj02towgs84(cjGeom[0], cjGeom[1]), 'pink');
    }

    // 記錄百度坐標對象
    var bdFeature;

    function toBD09() {
        // 先將WGS84轉為高德騰訊,在轉為BD09
        let tempGeom = wgs84togcj02(xy[0], xy[1]);
        // 百度坐標設置為黃色
        bdFeature = addFeature(gcj02tobd09(tempGeom[0], tempGeom[1]), 'yellow');
    }

    // 百度坐標轉WGS84
    function BD09TO() {
        if(!bdFeature){
            return;
        }
        let bdGeom = bdFeature.getGeometry().getCoordinates();
        // 現將BD09轉為高德騰訊,在轉為WGS84
        let tempGeom = bd09togcj02(bdGeom[0], bdGeom[1]);
        // 還原為WGS坐標,設置為粉色
        addFeature(gcj02towgs84(tempGeom[0], tempGeom[1]), 'green');
    }

</script>

<button id="toCJ02" onclick="toCJ02()">WGS84轉騰訊/高德</button>
<button id="CJ02TO" onclick="CJ02TO()">高德/騰訊轉WGS84</button>
<button id="toBD09" onclick="toBD09()">WGS84轉百度</button>
<button id="BD09TO" onclick="BD09TO()">百度轉WGS84</button>
</body>
</html>

線上示例

Openlayers 高德騰訊、百度、天地圖坐標相互轉換:Openlayers transfer_gcj

本文轉載於:

https://blog.csdn.net/linzi19900517/article/details/123570916

如果對您有所幫助,歡迎您點個關註,我會定時更新技術文檔,大家一起討論學習,一起進步。

 


您的分享是我們最大的動力!

-Advertisement-
Play Games
更多相關文章
  • vmstat 是一個查看虛擬記憶體(Virtual Memory)使用狀況的工具,但是怎樣通過 vmstat 來發現系統中的瓶頸呢? 1。 使用vmstat 使用前我們先看下命令介紹及參數定義 Usage: vmstat [options] [delay [count]] Options: -a, - ...
  • Air105 的時鐘 高頻振蕩源 * 晶元支持使用內部振蕩源, 或使用外置12MHz晶體 * 晶元上電覆位後 ROM boot 啟動過程基於內部12MHz的振蕩器 * 晶元內部集成的12MHz振蕩源精度為±2%, 精度一般 * 使用外置12MHz晶體, 需要軟體切換 * 經過PLL倍頻後為系統提供... ...
  • 寫在前面 前些天看了一本漫畫,裡面一個老技術人員的話,讓我不自覺的想到很多東西。 是啊,有時候我們自認為的自我實現,其實就是在湊這個時代的熱鬧,每個時代都有自己的熱鬧,個人之於時代不過是浪花一朵朵。 但這並不能作為我們不去湊這個熱鬧的理由,你去湊時代的這個熱鬧,這個時代必會給予你獎勵,或早或晚,或大 ...
  • 1、Linux常用命令 1.1、Linux命令初體驗 在日常使用過程當中經常使用到的命令有如下幾種 命令 對應英文 作用 ls [目錄名] list 查看當前目錄下的內容 pwd print work directory 查看當前所在目錄 cd [目錄名] change directory 切換目錄 ...
  • 導讀: 本文主要介紹了快手的精排模型實踐,包括快手的推薦系統,以及結合快手業務展開的各種模型實戰和探索,全文圍繞以下幾大方面展開: 快手推薦系統 CTR模型——PPNet 多domain多任務學習框架 短期行為序列建模 長期行為序列建模 千億特征,萬億參數模型 總結和展望 -- 01 快手推薦系統 ...
  • SQL的約束 概述 概念:約束是作用於表中欄位上的規則,用於限制存儲表中的數據; 目的:保證資料庫中數據的正確性、有效性、完整性; 常見的約束分類: 約束 描述 關鍵字 非空約束 限制該欄位的數據不能為null not null 唯一約束 保證該欄位的所有數據都是唯一的、不重覆的 unique 主鍵 ...
  • 自定義封裝分頁器組件之前需要知道的數據: 1.當前的頁碼 2.總共多少條數據 3.每頁展示多少條數據 4.連續頁碼數(5|7) 5.總頁數 計算連續頁碼數的起始值和結束值 //通過計算屬性來計算出來 computed:{ startNumAndEndNum(){ //首先先結構出我們需要的值(當前頁 ...
  • 定義指令的變化 根據vue3文檔的描述 https://v3.cn.vuejs.org/guide/migration/introduction.html#%E6%B8%B2%E6%9F%93%E5%87%BD%E6%95%B0 自定義指令的 API 已更改為與組件生命周期一致。 且 binding ...
一周排行
    -Advertisement-
    Play Games
  • 一:背景 準備開個系列來聊一下 PerfView 這款工具,熟悉我的朋友都知道我喜歡用 WinDbg,這東西雖然很牛,但也不是萬能的,也有一些場景他解決不了或者很難解決,這時候藉助一些其他的工具來輔助,是一個很不錯的主意。 很多朋友喜歡在項目中以記錄日誌的方式來監控項目的流轉情況,其實 CoreCL ...
  • 本來閑來無事,準備看看Dapper擴展的源碼學習學習其中的編程思想,同時整理一下自己代碼的單元測試,為以後的進一步改進打下基礎。 突然就發現問題了,源碼也不看了,開始改代碼,改了好久。 測試Dapper.LiteSql數據批量插入的時候,耗時20秒,感覺不正常,於是我測試了非Dapper版的Lite ...
  • 需求如下,在DEV框架項目中,需要在表格中增加一列顯示圖片,並且能編輯該列圖片,然後進行保存等操作,最終效果如下 這裡使用的是PictureEdit控制項來實現,打開DEV GridControl設計器,在ColumnEdit選擇PictureEdit: 綁定圖片代碼如下: DataTable dtO ...
  • 前兩天微軟偷偷更新了Visual Studio 2022 正式版版本 17.3 發佈,發佈摘要: MAUI 工作負荷 GA 生成 MAUI/Blazor CSS 熱重載支持 現在,你將能夠使用我們的新增功能在 Visual Studio 中使用每個更新試用一系列新功能。 選擇每個功能以瞭解有關特定功 ...
  • 航天和軍工領域的數字化轉型和建設正在積極推進,在與航天二院、航天三院、航天六院、航天九院、無線電廠、兵工廠等單位交流的過程中,用戶更聚焦試驗和生產過程中的痛點,迫切需要解決軟體平臺統一監測和控制設備及軟體與設備協同的問題。 ...
  • .NET 項目預設情況下 日誌是使用的 ILogger 介面,預設提供一下四種日誌記錄程式: 控制台 調試 EventSource EventLog 這四種記錄程式都是預設包含在 .NET 運行時庫中。關於這四種記錄程式的詳細介紹可以直接查看微軟的官方文檔 https://docs.microsof ...
  • 一:背景 上一篇我們聊到瞭如何去找 熱點函數,這一篇我們來看下當你的程式出現了 非托管記憶體泄漏 時如何去尋找可疑的代碼源頭,其實思路很簡單,就是在 HeapAlloc 或者 VirtualAlloc 時做 Hook 攔截,記錄它的調用棧以及分配的記憶體量, PerfView 會將這個 分配量 做成一個 ...
  • 背景 在 CI/CD 流程當中,測試是 CI 中很重要的部分。跟開發人員關係最大的就是單元測試,單元測試編寫完成之後,我們可以使用 IDE 或者 dot cover 等工具獲得單元測試對於業務代碼的覆蓋率。不過我們需要一個獨立的 CLI 工具,這樣我們才能夠在 Jenkins 的 CI 流程集成。 ...
  • 一、應用場景 大家在使用Mybatis進行開發的時候,經常會遇到一種情況:按照月份month將數據放在不同的表裡面,查詢數據的時候需要跟不同的月份month去查詢不同的表。 但是我們都知道,Mybatis是ORM持久層框架,即:實體關係映射,實體Object與資料庫表之間是存在一一對應的映射關係。比 ...
  • 我國目前並未出台專門針對網路爬蟲技術的法律規範,但在司法實踐中,相關判決已屢見不鮮,K 哥特設了“K哥爬蟲普法”專欄,本欄目通過對真實案例的分析,旨在提高廣大爬蟲工程師的法律意識,知曉如何合法合規利用爬蟲技術,警鐘長鳴,做一個守法、護法、有原則的技術人員。 案情介紹 深圳市快鴿互聯網科技有限公司 2 ...