記錄--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
  • 隨著Aspire發佈preview5的發佈,Microsoft.Extensions.ServiceDiscovery隨之更新, 服務註冊發現這個屬於老掉牙的話題解決什麼問題就不贅述了,這裡主要講講Microsoft.Extensions.ServiceDiscovery(preview5)以及如何 ...
  • 概述:通過使用`SemaphoreSlim`,可以簡單而有效地限制非同步HTTP請求的併發量,確保在任何給定時間內不超過20個網頁同時下載。`ParallelOptions`不適用於非同步操作,但可考慮使用`Parallel.ForEach`,儘管在非同步場景中謹慎使用。 對於併發非同步 I/O 操作的數量 ...
  • 1.Linux上安裝Docken 伺服器系統版本以及內核版本:cat /etc/redhat-release 查看伺服器內核版本:uname -r 安裝依賴包:yum install -y yum-utils device-mapper-persistent-data lvm2 設置阿裡雲鏡像源:y ...
  • 概述:WPF界面綁定和渲染大量數據可能導致性能問題。通過啟用UI虛擬化、非同步載入和數據分頁,可以有效提高界面響應性能。以下是簡單示例演示這些優化方法。 在WPF中,當你嘗試綁定和渲染大量的數據項時,性能問題可能出現。以下是一些可能導致性能慢的原因以及優化方法: UI 虛擬化: WPF提供了虛擬化技術 ...
  • 引言 上一章節介紹了 TDD 的三大法則,今天我們講一下在單元測試中模擬對象的使用。 Fake Fake - Fake 是一個通用術語,可用於描述 stub或 mock 對象。 它是 stub 還是 mock 取決於使用它的上下文。 也就是說,Fake 可以是 stub 或 mock Mock - ...
  • 為.net6在CentOS7上面做準備,先在vmware虛擬機安裝CentOS 7.9 新建CentOS764位的系統 因為CentOS8不更新了,所以安裝7;簡單就一筆帶過了 選擇下載好的操作系統的iso文件,下載地址https://mirrors.aliyun.com/centos/7.9.20 ...
  • 經過前面幾篇的學習,我們瞭解到指令的大概分類,如:參數載入指令,該載入指令以 Ld 開頭,將參數載入到棧中,以便於後續執行操作命令。參數存儲指令,其指令以 St 開頭,將棧中的數據,存儲到指定的變數中,以方便後續使用。創建實例指令,其指令以 New 開頭,用於在運行時動態生成並初始化對象。方法調用指... ...
  • LiteDB 是一個輕量級的嵌入式 NoSQL 資料庫,其設計理念與 MongoDB 類似,但它是完全使用 C# 開發的,因此與 C# 應用程式的集成非常順暢。與 SQLite 相比,LiteDB 提供了 NoSQL(即鍵值對)的數據存儲方式,並且是一個開源且免費的項目。它適用於桌面、移動以及 We ...
  • 1 開源解析和拆分文檔 第三方的工具去對文件解析拆分,去將我們的文件內容給提取出來,並將我們的文檔內容去拆分成一個小的chunk。常見的PDF word mark down, JSON、HTML。都可以有很好的一些模塊去把這些文件去進行一個東西去提取。 優勢 支持豐富的文檔類型 每種文檔多樣化選擇 ...
  • OOM是什麼?英文全稱為 OutOfMemoryError(記憶體溢出錯誤)。當程式發生OOM時,如何去定位導致異常的代碼還是挺麻煩的。 要檢查OOM發生的原因,首先需要瞭解各種OOM情況下會報的異常信息。這樣能縮小排查範圍,再結合異常堆棧、heapDump文件、JVM分析工具和業務代碼來判斷具體是哪 ...