這裡給大家分享我在網上總結出來的一些知識,希望對大家有所幫助 在地圖開發過程中,坐標的轉換是很常用的功能,國內的話一般西安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