openlayers3瓦片加载的源码浅析与小结

作者: 老羽 | 来源:发表于2016-08-23 10:09 被阅读3334次

    本文适用于对ol3的使用有一定了解,目的是为了对ol3瓦片加载部分有更深入的了解。ol3源码极其庞大,本文仅仅只包括瓦片图层加载的这一部分逻辑。

    一、类图与逻辑

    ol-tilerender.png

    上图中列了关于瓦片图层加载相关的重要方法。
    1)Map对象初始化时根据options.renderer创建ol.renderer.Map的实例,默认是ol.renderer.canvas.Map;
    2)ol.render.canvas.Map实现了抽象方法createLayerRenderer,这是一个简单工厂,根据不同的图层创建对应的ol.renderer.Layer。其中ol.layer.Tile对应的就是ol.renderer.canvas.TileLayer;
    3)ol.renderer.canvas.TileLayer.prepareFrame调用source对应的
    TileGrid.getTileRangeForExtentAndResolution获取可视范围内的瓦片范围,并循环遍历加载瓦片;
    4)TileGrid在初始化时就计算出了对应layer的所有瓦片范围:
    i)calculateTileRanges_-》循环遍历resolutions_,调用
    getTileRangeForExtentAndZ,根据extent计算瓦片范围;
    ii)getTileRangeForExtentAndResolution计算瓦片的范围:

    ol.tilegrid.TileGrid.prototype.getTileRangeForExtentAndResolution = function(extent, resolution, opt_tileRange) {
      var tileCoord = ol.tilegrid.TileGrid.tmpTileCoord_;
      //根据extent的左下角的计算瓦片坐标;
      this.getTileCoordForXYAndResolution_(
          extent[0], extent[1], resolution, false, tileCoord);
      var minX = tileCoord[1];
      var minY = tileCoord[2];
        //根据extent的右上角的计算瓦片坐标;
       this.getTileCoordForXYAndResolution_(
          extent[2], extent[3], resolution, true, tileCoord);
      //得到某个resolution级别下的瓦片范围(左下角瓦片坐标 - 右上角瓦片坐标)
      return ol.TileRange.createOrUpdate(
          minX, tileCoord[1], minY, tileCoord[2], opt_tileRange);
     };
    
    //根据extent左下角及右上角的坐标-origin后得到地图的长宽 / resolution得到像素值;然后 / tileSize 得到需要多少张瓦片;
    //当计算extent右上角的瓦片坐标时,因为瓦片坐标是从0开始计算,当瓦片数量为例如1.5此类小数时,
    //应该是2张瓦片,从0开始计算,那么XY就应该向下取整,取1;0,1两张瓦片;
    ol.tilegrid.TileGrid.prototype.getTileCoordForXYAndResolution_ = function(  
            x, y, resolution, reverseIntersectionPolicy, opt_tileCoord) {
      var z = this.getZForResolution(resolution);
      var scale = resolution / this.getResolution(z);
      var origin = this.getOrigin(z);
      var tileSize = ol.size.toSize(this.getTileSize(z), this.tmpSize_);
    
      var adjustX = reverseIntersectionPolicy ? 0.5 : 0;
      var adjustY = reverseIntersectionPolicy ? 0 : 0.5;
      var xFromOrigin = Math.floor((x - origin[0]) / resolution + adjustX);
      var yFromOrigin = Math.floor((y - origin[1]) / resolution + adjustY);
      var tileCoordX = scale * xFromOrigin / tileSize[0];
      var tileCoordY = scale * yFromOrigin / tileSize[1];
    
      if (reverseIntersectionPolicy) {
        tileCoordX = Math.ceil(tileCoordX) - 1;
        tileCoordY = Math.ceil(tileCoordY) - 1;
      } else {
        tileCoordX = Math.floor(tileCoordX);
        tileCoordY = Math.floor(tileCoordY);
      }
    
      return ol.tilecoord.createOrUpdate(z, tileCoordX, tileCoordY, opt_tileCoord);
    };
    

    二、各种瓦片加载的小结

    通过上述分析后,应该能较好的理解瓦片的坐标是如何计算的,当应用到不同的地图瓦片加载时就可以得心应手。以下通过不同的几种类型继续说明瓦片计算的方式:

    1、TMS瓦片加载

    先看看TMS瓦片的规则,origin在左下角,X轴从左至右递增,Y轴从下往上递增(先计算左下角,然后计算右上角)。


    TMS瓦片规则

    而TileGrid设置origin为ol.extent.getBottomLeft(extent)后,规则也是从左下角到右上角,X轴从左至右递增,Y轴从下往上递增,与TMS规则是完全一致的,参考代码与参考效果如下:

       var resolutions = [];
            var tileSize = 256;
            var extent = [12665080.52765571, 2550703.6338763316, 12725465.780000998, 2601457.820657688];//深圳地区
            var projection = new ol.proj.get("EPSG:3857");
            var projectionExtent = projection.getExtent();
    
            for (var i = 0; i < 19; i++) {
                resolutions[i] = Math.pow(2, 18 - i);
            }            
    
            var tilegrid = new ol.tilegrid.TileGrid({
                origin: ol.extent.getBottomLeft(projectionExtent),
                resolutions: resolutions,
                extent: projectionExtent,//extent,
                tileSize: [256, 256],
            });         
    
            var map = new ol.Map({
                target: "map",
                layers: [
              ////调试瓦片
                new ol.layer.Tile({
                    source: new ol.source.TileDebug({
                        projection: projection,
                        tileGrid: tilegrid,
                        tileSize: [256, 256],
                        extent :  projectionExtent,
                        wrapX: false
                    }),
                })
                ],
                view: new ol.View({
                    projection: projection,
                    center: [12697184.079535482, 2563239.3065151004],//深圳
                    resolutions: resolutions,
                }),
            });
            map.getView().setZoom(1);
    
    示意图

    假如上面代码中,我想只显示深圳地区的瓦片,其余的瓦片不显示,这种场景是很普遍的,那么代码调整如下:

      var tilegrid = new ol.tilegrid.TileGrid({
                origin: ol.extent.getBottomLeft(projectionExtent),  //origin位置不能变!!!!!!
                resolutions: resolutions,
                extent:  extent,//projectionExtent //设置extent为深圳片区的extent;
                tileSize: [256, 256],
            });      
    //..................
    ////调试瓦片
                new ol.layer.Tile({
                    source: new ol.source.TileDebug({
                        projection: projection,
                        tileGrid: tilegrid,
                        extent: extent,//projectionExtent //设置extent为深圳片区的extent;
                        wrapX: false
                    }),
                })
    

    2、WMTS瓦片加载

    WMTS规则如下图,origin在左上角,X轴从左至右递增,Y轴是从上往下递增(先计算左上角,然后计算右下角)

    WMTS规则

    首先将tileGrid设置origin为左上角:ol.extent.getTopLeft(projectionExtent), 但是TileGrid始终都是先计算左下角的瓦片坐标,然后计算右上角的瓦片坐标,因此Y轴是相反的。那么修改Y轴坐标就可以得到正确瓦片坐标:

    var tilegrid = new ol.tilegrid.TileGrid({
                origin: ol.extent.getTopLeft(projectionExtent),  //WMTS Origin在左上角,origin位置不能变;
                resolutions: resolutions,
                extent: extent,
                tileSize: [256, 256],
            });
    //其余代码略.....
    new ol.layer.Tile({
           source: new ol.source.TileImage({
           projection: projection,
            tileGrid: tilegrid(),
            tileUrlFunction: function (tileCoord, pixelRatio, proj) {
                  if (!tileCoord) {
                      return "";
                   }
                  var z = tileCoord[0];
                  var x = tileCoord[1];
                  var y = -tileCoord[2] - 1; //y轴取反,-1目的是为了从0开始计数;
    
                  return ''; //自行设置URL ,请注意 WMTS中用TileRow标识Y,用TileCol表示X;
                }
            }),
      })
    

    3、百度地图瓦片加载

    百度瓦片片规则遵从TMS规则,如下图:Origin在[0,0],X轴从左至右递增,Y轴从下往上递增(从左下角到右上角)。

    百度地图

    从百度的瓦片规则看出来,与TileGrid的规则是完全一致,将origin设置为[0,0]即可。参考代码如下:

     var tilegrid = new ol.tilegrid.TileGrid({
                origin: [0, 0],
                resolutions: resolutions,
                extent: extent,//projectionExtent,
                tileSize: [256, 256],
            });
    
     var tilesource = new ol.source.TileImage({
                projection: projection,
                tileGrid: tilegrid,
                tileUrlFunction: function (xyz, obj1, obj2) {
                    if (!xyz) {
                        return "";
                    }
                    var z = xyz[0]+ 11; //从第11级开始加载;深圳地区;
                    var x = xyz[1];
                    var y = xyz[2];
                    if (x < 0) {
                        x = "M" + (-x);
                    }
                    if (y < 0) {
                        y = "M" + (-y);
                    }                    
                    return "http://online3.map.bdimg.com/tile/?qt=tile&x=" + x + "&y=" + y + "&z=" + z + "&styles=pl&udt=20141119&scaler=1";                   
                }
            });
    

    4、腾讯地图瓦片加载

    腾讯地图完全遵守TMS规则,地图投影坐标系采用Web Mercator投影,最小缩放级别为第4级。参考代码如下:

    //QQ地图完全遵守TMS规则;
        var tileGrid = new ol.tilegrid.TileGrid({
            resolutions: resolutions3857,
            tileSize: [256, 256],
            extent: projection3857Extent,
            origin: ol.extent.getBottomLeft(projection3857Extent), //Origin左下角
        });
    
        var tilesource = new ol.source.TileImage({
            tileUrlFunction: function (xyz, obj1, obj2) {
                if (!xyz) {
                    return "";
                }
                var z = xyz[0];
                var x = xyz[1];
                var y = xyz[2];
                return "http://rt1.map.gtimg.com/realtimerender?z=" + z + "&x=" + x + "&y=" + y + "&type=vector&style=0&v=1.1.2"
            },
            projection: projection3857,
            tileGrid : tileGrid
        });
    

    5 高德地图瓦片加载

    高德地图瓦片遵从WMTS规则,如下图:


    高德地图
      var layerMap = new ol.layer.Tile({
        source: new ol.source.XYZ({
            tileUrlFunction: function (tileCoord) {
                var z = tileCoord[0];
                var x = tileCoord[1];
                 var y = tileCoord[2];
                 y = -y - 1;            
                return "http://webrd01.is.autonavi.com/appmaptile?lang=zh_cn&size=1&scale=1&style=8&x=" + x + "&y=" + y + "&z=" + z ;
            },
            tileGrid: tilegrid,
            extent: extent
        })
    });

    相关文章

      网友评论

      • 四爷在此:好文章,再看一遍。。
        老羽:@四爷在此 我之前写的瓦片下载工具就是参考的ol加载瓦片的算法。
        老羽:@四爷在此 一休哥过奖了。
      • 逍遥散人一生逍遥:在加载百度离线瓦片的时候,我页面上显示的是空白,报错一些图片找不到,但是我本地有的瓦片应该加载出来才对啊
        逍遥散人一生逍遥:路径没错误,我感觉好像和坐标系有关系,我瓦片上的中心点在我页面上要显示的差好多,
        老羽:打开浏览器调试工具,F12,看看路径是否正确,很容易就看出来了。
      • 58841f7d57fa::cold_sweat: 加载QQ地图那部分的代码有问题
        老羽:QQ地图没有问题,这都是我实践过的代码,关键在于TileGrid的设置,Origin要设置为左下角。它是标准的TMS瓦片

      本文标题:openlayers3瓦片加载的源码浅析与小结

      本文链接:https://www.haomeiwen.com/subject/stegsttx.html