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);
...
}
...
}