OpenLayers中切片图层TileLayer的渲染解析
切片图层是最常用也是最基础的图层,通常底图或者影像图层都采用TileLayer加载
TileLayer由Tile组成,Tile一般是图片切片
我们来看TileLayer渲染过程
TileLayer的创建
从最初的地方开始,也就是初始化地图时,会实例化一个切片图层
const map = new Map({
layers: [
new TileLayer({
source: new OSM()
})
],
target: 'map',
view: new View({
center: [0, 0],
zoom: 2
})
});
TileLayer里也比较简洁,初始化调用父类的构造函数,自己只负责创建渲染器
class TileLayer extends BaseTileLayer {
constructor(opt_options) {
super(opt_options);
}
createRenderer() {
return new CanvasTileLayerRenderer(this);
}
}
那在何处执行createRenderer,构建layer.render以备Map调用呢?
TileLayer属于Layer的子类,看Layer的实现:
class Layer extends BaseLayer {
constructor(options) {
...
}
render(frameState, target) {
const layerRenderer = this.getRenderer();
if (layerRenderer.prepareFrame(frameState)) {
return layerRenderer.renderFrame(frameState, target);
}
}
getRenderer() {
if (!this.renderer_) {
this.renderer_ = this.createRenderer();
}
return this.renderer_;
}
}
可以看到Layer定义了render()方法,并调用了createRenderer(),并执行渲染器的renderFrame()方法
这里就引出了ol的一个图层渲染机制
ol的图层类型很多,但大部分属性和方法通用,不同的图层类型自己创建渲染器,用
Layer统筹初始化图层,再调用各自实现的渲染器
TileLayer的继承链条为:
TileLayer->BaseTileLayer->Layer->BaseLayer->BaseObject->Observable->EventTarget->Disposable
TileLayer的渲染器CanvasTileLayerRenderer的继承链条为:
CanvasTileLayerRenderer->CanvasLayerRenderer->LayerRenderer->Observable->EventTarget->Disposable
接下来看ol是如何渲染切片图层的
TileLayer的渲染器CanvasTileLayerRenderer的实现逻辑
通过上面的解析可以看出渲染的核心方法是renderFrame(),代码很长,来看主要逻辑
首先看下切片图层的数据源Source
TileLayer的一个重要属性就是source,定义了切片的网络地址、规则、大小、分辨率、比例尺、生产商等等信息。要正确渲染就必须先正确解析并请求到切片图片。
用OSM看下source的继承关系
OSM->XYZ->TileImage->UrlTile->TileSource->Source->BaseObject->Observable->EventTarget->Disposable
OpenStreetMap(简称OSM,中文是公开地图)是一个网上地图协作计划,目标是创造一个内容自由且能让所有人编辑的世界地图。它是利用公众集体的力量和无偿的贡献来改善地图相关的地理数据。OSM是非营利性 的,它将数据回馈给社区重新用于其它的产品与服务。
可以看到OSM继承自XYZ,XYZ是切片地图很常用的一套切片规则,在new OSM()时已经将对应投影和切片规则读取出来
在调用渲染器时,根据设置好的中心点和级别,通过切片规则和投影定义计算出需要的切片Z\X\Y的值,从而请求到切片的图片,再绘制到canvas中。
canvas / TileLayer.js
class CanvasTileLayerRenderer extends CanvasLayerRenderer {
...
renderFrame(frameState, target) {
...
for (const tileCoordKey in tilesToDraw) {
//调用绘制切片
this.drawTileImage(tile, frameState, x, y, w, h, tileGutter, transition, layerState.opacity);
}
...
}
drawTileImage(tile, frameState, x, y, w, h, gutter, transition, opacity) {
...
//画布绘制
this.context.drawImage(image, gutter, gutter, image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h);
...
}
...
}