• 高德地图百度地图纠偏


    1、写在前面

    在使用高德地图API和百度地图API的时候,如果要加载地图服务如WMS,WMTS等,这些地图服务常用的投影坐标系是EPSG:3857。加载上去会发现存在偏移,因为投影坐标系不一致。

    高德的坐标系是GCJ-02,而百度的坐标系是在GCJ-02再次偏移的BD-09,这些坐标系是没有收录在EPSG中的,所以无法用Proj.4库来做坐标转换。

    我们是否可以通过整体的偏移来做呢?不行的,因为GCJ-02坐标系相对于WMS坐标系的偏差是非线性随机的。这么做感觉就是在为难国内的开发者,一方面不能不使用WGS坐标,因为这个是国际通用的,另一方面又在设置重重障碍让WGS坐标和GCJ-02坐标难以转化。

    不过也不是束手无策的,高德和百度都有提供单点的坐标转换功能,我们可以利用单点的坐标转换来实现切片的偏移。也有一个开源的项目 gcoord 融合了百度高德的转化

    2、思路

    百度高德在请求切片图层的时候,对于每一个切片来说,切片的BBOX坐标是可以计算出来的。在默认情况下,会使用计算出来的BBOX坐标请求WMS或是WMTS服务,这样是有偏差的。我们可以对计算出来的BBOX坐标进行单点偏移,使用偏移后的BBOX坐标请求地图服务就可以实现地图的吻合。

    3、实现

    百度地图:

    <!DOCTYPE html>
    <html lang="en">
    
    <head>
        <meta charset="UTF-8">
        <meta name="viewport" content="width=device-width, initial-scale=1.0">
        <style>
            html,
            body {
                margin: 0;
                padding: 0;
            }
    
            #allmap {
                 100%;
                height: 100vh;
            }
        </style>
        <script type="text/javascript" src="./util.js"></script>
        <script src="https://unpkg.com/gcoord/dist/gcoord.js"></script>
    </head>
    
    <body>
        <div id="allmap"></div>
        <script type="text/javascript">
            var map = null;
            //百度地图API功能
            function loadJScript() {
                var script = document.createElement("script");
                script.type = "text/javascript";
                script.src = "https://api.map.baidu.com/api?v=2.0&ak=你的秘钥&callback=init";
                document.body.appendChild(script);
            }
            
            function init() {
                map = new BMap.Map("allmap");            // 创建Map实例
                var point = new BMap.Point(116.997528, 36.676244); // 创建点坐标
                map.centerAndZoom(point, 15);
                map.enableScrollWheelZoom();                 //启用滚轮放大缩小
                addWMSLayer();
                map.addEventListener("click", e => {
                    console.log(e);
                })
            }
    
    
            function addWMSLayer() {
                let tileLayer = new BMap.TileLayer({
                    transparentPng: true
                })
    
                // 不同的分辨率,数值上等于一像素对应的距离
                var resolutions = []
                for (var i = 0; i < 19; i++) {
                    resolutions[i] = Math.pow(2, 18 - i);
                }
    
                tileLayer.getTilesUrl = function (tileCoord, zoom) {
    
                    // x,y瓦片左上角横纵坐标(米)
                    var x = tileCoord.x;
                    var y = tileCoord.y;
    
                    var resolution = resolutions[zoom];
                    var tileWidth = 256;
                    
                    // 计算瓦片的BBOX
                    var minx = x * tileWidth * resolution;
                    var miny = y * tileWidth * resolution;
                    var maxx = (x + 1) * tileWidth * resolution;
                    var maxy = (y + 1) * tileWidth * resolution;
    
                    // 将BBOX的BD-09米制坐标转为EPSG:3857
                    var bottomLeft = gcoord.transform(
                        [minx, miny],
                        gcoord.BD09MC,
                        gcoord.EPSG3857
                    );
                    var topRight = gcoord.transform(
                        [maxx, maxy],
                        gcoord.BD09MC,
                        gcoord.EPSG3857
                    );
                    
                    let bbox = [bottomLeft[0], bottomLeft[1], topRight[0], topRight[1]];
    
                    // wms参数
                    let baseUrl = "http://{host}:{port}/geoserver/{workspace}/wms?";
                    let params = {
                        SERVICE: "WMS",
                        VERSION: "1.1.1",
                        REQUEST: "GetMap",
                        FORMAT: "image/png",
                        TRANSPARENT: true,
                        LAYERS: "{workspace:layerName}",
                        WIDTH: 256,
                        HEIGHT: 256,
                        SRS: "EPSG:3857"
                    }
    
                    var url = baseUrl + objToParams(params) + `&bbox=${bbox.join(",")}`
                    return url;
                }
                map.addTileLayer(tileLayer);
            }
    
            window.onload = loadJScript;  //异步加载地图
        </script>
    </body>
    
    </html>
    

    高德地图:

    <!doctype html>
    <html>
    
    <head>
        <meta charset="utf-8">
        <meta http-equiv="X-UA-Compatible" content="IE=edge">
        <meta name="viewport" content="initial-scale=1.0, user-scalable=no, width=device-width">
        <title>异步加载地图</title>
        <link rel="stylesheet" href="https://cache.amap.com/lbs/static/main1119.css" />
        <script src="https://unpkg.com/gcoord/dist/gcoord.js"></script>
        <script src="./util.js"></script>
    
    </head>
    
    <body>
        <div id="container"></div>
        <script>
            var map = null;
            function onApiLoaded() {
                map = new AMap.Map('container', {
                    center: [117.11116783127187, 36.67459186502283],
                    zoom: 15
                });
                map.on("click", e => {
                    console.log(e);
                })
                addWMSLayer();
            }
    
            // 切片转经度
            function tile2lon(x, z) {
                return (x / Math.pow(2, z) * 360 - 180);
            }
    
            // 切片转纬度
            function tile2lat(y, z) {
                var n = Math.PI - 2 * Math.PI * y / Math.pow(2, z);
                return (180 / Math.PI * Math.atan(0.5 * (Math.exp(n) - Math.exp(-n))));
            }
    
            function addWMSLayer() {
    
                var resolutions = []
                for (var i = 0; i < 19; i++) {
                    resolutions[i] = Math.pow(2, 18 - i);
                }
    
                var tileLayer = new AMap.TileLayer({
                    tileSize: 256,
                    tileUrl: function (x, y, z) {
                       	// 获取瓦片BBOX的经纬度坐标,这里和百度坐标的转化不一样,因为没有在gcoord中找到GCJ-02米制坐标转化,只好通过经纬度了
                        let xmin = tile2lon(x, z);
                        let xmax = tile2lon(x + 1, z);
                        let ymin = tile2lat(y + 1, z);
                        let ymax = tile2lat(y, z);
    
                        // 将BBOX的GCJ02坐标转为EPSG:3857
                        let t1 = gcoord.transform([xmin, ymin], gcoord.GCJ02, gcoord.EPSG3857);
                        let t2 = gcoord.transform([xmax, ymax], gcoord.GCJ02, gcoord.EPSG3857);
    
                        // wms配置
                        let url = 'http://{host}:{port}/geoserver/{workspace}/wms?';
                        let params = {
                            service: "WMS",
                            version: "1.1.0",
                            transparent: true,
                            request: "GetMap",
                            layers: "{workspace:layerName}",
                             256,
                            height: 256,
                            srs: "EPSG:3857",
                            format: "image/png",
                            bbox: [t1[0], t1[1], t2[0], t2[1]].join(',')
                        };
    
                        // 构建查询字符串
                        let str = objToParams(params);
    
                        return url + str;
                    },
                    zIndex: 10
                })
                map.add(tileLayer);
            }
    
            var url = 'https://webapi.amap.com/maps?v=1.4.15&key=你的秘钥&callback=onApiLoaded';
            var jsapi = document.createElement('script');
            jsapi.charset = 'utf-8';
            jsapi.src = url;
            document.head.appendChild(jsapi);
        </script>
    </body>
    
    </html>
    

    帮助类

    // 对象转为查询字符串
    function objToParams(obj){
        return Object.keys(obj).map(function (key) {
            return "".concat(encodeURIComponent(key), "=").concat(encodeURIComponent(obj[key]));
          }).join('&');
    }
    

    4、源码

    源码地址

  • 相关阅读:
    activiti 清库脚本(转)
    T-Sql 递归查询(给定节点查所有父节点、所有子节点的方法)
    Chrome firefox ie等浏览器空格(&nbsp;)兼容问题
    activiti5.22整合modeler时出错TypeError: Cannot read property 'split' of undefined
    activiti如何获取当前节点以及下一步路径或节点(转)
    Activiti5 添加/查询审批批注(审批意见)
    Django中多对多关系的orm表设计
    css背景雪碧图等
    Django图书管理系统(前端对有外键的数据表增删改查)
    Django图书管理系统(前端对数据库的增删改查)
  • 原文地址:https://www.cnblogs.com/asdlijian/p/13657043.html
Copyright © 2020-2023  润新知