記錄--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
  • 移動開發(一):使用.NET MAUI開發第一個安卓APP 對於工作多年的C#程式員來說,近來想嘗試開發一款安卓APP,考慮了很久最終選擇使用.NET MAUI這個微軟官方的框架來嘗試體驗開發安卓APP,畢竟是使用Visual Studio開發工具,使用起來也比較的順手,結合微軟官方的教程進行了安卓 ...
  • 前言 QuestPDF 是一個開源 .NET 庫,用於生成 PDF 文檔。使用了C# Fluent API方式可簡化開發、減少錯誤並提高工作效率。利用它可以輕鬆生成 PDF 報告、發票、導出文件等。 項目介紹 QuestPDF 是一個革命性的開源 .NET 庫,它徹底改變了我們生成 PDF 文檔的方 ...
  • 項目地址 項目後端地址: https://github.com/ZyPLJ/ZYTteeHole 項目前端頁面地址: ZyPLJ/TreeHoleVue (github.com) https://github.com/ZyPLJ/TreeHoleVue 目前項目測試訪問地址: http://tree ...
  • 話不多說,直接開乾 一.下載 1.官方鏈接下載: https://www.microsoft.com/zh-cn/sql-server/sql-server-downloads 2.在下載目錄中找到下麵這個小的安裝包 SQL2022-SSEI-Dev.exe,運行開始下載SQL server; 二. ...
  • 前言 隨著物聯網(IoT)技術的迅猛發展,MQTT(消息隊列遙測傳輸)協議憑藉其輕量級和高效性,已成為眾多物聯網應用的首選通信標準。 MQTTnet 作為一個高性能的 .NET 開源庫,為 .NET 平臺上的 MQTT 客戶端與伺服器開發提供了強大的支持。 本文將全面介紹 MQTTnet 的核心功能 ...
  • Serilog支持多種接收器用於日誌存儲,增強器用於添加屬性,LogContext管理動態屬性,支持多種輸出格式包括純文本、JSON及ExpressionTemplate。還提供了自定義格式化選項,適用於不同需求。 ...
  • 目錄簡介獲取 HTML 文檔解析 HTML 文檔測試參考文章 簡介 動態內容網站使用 JavaScript 腳本動態檢索和渲染數據,爬取信息時需要模擬瀏覽器行為,否則獲取到的源碼基本是空的。 本文使用的爬取步驟如下: 使用 Selenium 獲取渲染後的 HTML 文檔 使用 HtmlAgility ...
  • 1.前言 什麼是熱更新 游戲或者軟體更新時,無需重新下載客戶端進行安裝,而是在應用程式啟動的情況下,在內部進行資源或者代碼更新 Unity目前常用熱更新解決方案 HybridCLR,Xlua,ILRuntime等 Unity目前常用資源管理解決方案 AssetBundles,Addressable, ...
  • 本文章主要是在C# ASP.NET Core Web API框架實現向手機發送驗證碼簡訊功能。這裡我選擇是一個互億無線簡訊驗證碼平臺,其實像阿裡雲,騰訊雲上面也可以。 首先我們先去 互億無線 https://www.ihuyi.com/api/sms.html 去註冊一個賬號 註冊完成賬號後,它會送 ...
  • 通過以下方式可以高效,並保證數據同步的可靠性 1.API設計 使用RESTful設計,確保API端點明確,並使用適當的HTTP方法(如POST用於創建,PUT用於更新)。 設計清晰的請求和響應模型,以確保客戶端能夠理解預期格式。 2.數據驗證 在伺服器端進行嚴格的數據驗證,確保接收到的數據符合預期格 ...