我是靠谱客的博主 听话鼠标,这篇文章主要介绍Openlayers源码阅读(七):图层canvas渲染,现在分享给大家,希望可以做个参考。

上一篇中,ol.renderer.canvas.Map在renderFrame()中通过getLayerRenderer()初始化ol.renderer.canvas.Layer,并调用了ol.renderer.canvas.Layer的prepareFrame()、composeFrame方法。

文章目录

    • 导图
    • 一、ol.renderer.canvas.Layer
    • 二、ol.renderer.canvas.IntermediateCanvas
    • 三、ol.renderer.canvas.ImageLayer
    • 四、ol.renderer.canvas.TileLayer
    • 五、 ol.renderer.canvas.VectorLayer
    • 六、 ol.renderer.canvas.VectorTileLayer

导图

extends
extends
extends
extends
extends
ImageLayer
IntermediateCanvas
TileLayer
VectorTileLayer
Layer
VectorLayer

说明: 命名空间 ol.renderer.canvas

一、ol.renderer.canvas.Layer

ol.renderer.canvas.Layer 提供抽象类和抽象方法

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
/** * @constructor * @abstract * @extends {ol.renderer.Layer} * @param {ol.layer.Layer} layer Layer. */ ol.renderer.canvas.Layer = function(layer) { ol.renderer.Layer.call(this, layer); /** * @protected * @type {number} */ this.renderedResolution; /** * @private * @type {ol.Transform} */ this.transform_ = ol.transform.create(); }; ol.inherits(ol.renderer.canvas.Layer, ol.renderer.Layer); /** * @abstract * @param {olx.FrameState} frameState Frame state. * @param {ol.LayerState} layerState Layer state. * @param {CanvasRenderingContext2D} context Context. */ ol.renderer.canvas.Layer.prototype.composeFrame = function(frameState, layerState, context) {}; /** * @abstract * @param {olx.FrameState} frameState Frame state. * @param {ol.LayerState} layerState Layer state. * @return {boolean} whether composeFrame should be called. */ ol.renderer.canvas.Layer.prototype.prepareFrame = function(frameState, layerState) {}; /** * @param {CanvasRenderingContext2D} context Context. * @param {olx.FrameState} frameState Frame state. * @param {ol.LayerState} layerState Layer state. * @param {ol.Transform=} opt_transform Transform. * @protected */ ol.renderer.canvas.Layer.prototype.postCompose = function(context, frameState, layerState, opt_transform) { this.dispatchComposeEvent_(ol.render.EventType.POSTCOMPOSE, context, frameState, opt_transform); }; /** * @param {CanvasRenderingContext2D} context Context. * @param {olx.FrameState} frameState Frame state. * @param {ol.Transform=} opt_transform Transform. * @protected */ ol.renderer.canvas.Layer.prototype.preCompose = function(context, frameState, opt_transform) { this.dispatchComposeEvent_(ol.render.EventType.PRECOMPOSE, context, frameState, opt_transform); }; /** * @param {ol.render.EventType} type Event type. * @param {CanvasRenderingContext2D} context Context. * @param {olx.FrameState} frameState Frame state. * @param {ol.Transform=} opt_transform Transform. * @private */ ol.renderer.canvas.Layer.prototype.dispatchComposeEvent_ = function(type, context, frameState, opt_transform) { var layer = this.getLayer(); if (layer.hasListener(type)) { var width = frameState.size[0] * frameState.pixelRatio; var height = frameState.size[1] * frameState.pixelRatio; var rotation = frameState.viewState.rotation; ol.render.canvas.rotateAtOffset(context, -rotation, width / 2, height / 2); var transform = opt_transform !== undefined ? opt_transform : this.getTransform(frameState, 0); var render = new ol.render.canvas.Immediate( context, frameState.pixelRatio, frameState.extent, transform, frameState.viewState.rotation); var composeEvent = new ol.render.Event(type, render, frameState, context, null); layer.dispatchEvent(composeEvent); ol.render.canvas.rotateAtOffset(context, rotation, width / 2, height / 2); } };

二、ol.renderer.canvas.IntermediateCanvas

实现了父类的composeFrame()的方法:

1、获取Image

2、对Image进行处理:范围裁剪、图像转换、透明度设置

3、context.drawImage()

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
ol.renderer.canvas.IntermediateCanvas.prototype.composeFrame = function(frameState, layerState, context) { this.preCompose(context, frameState); var image = this.getImage(); if (image) { // clipped rendering if layer extent is set var extent = layerState.extent; var clipped = extent !== undefined && !ol.extent.containsExtent(extent, frameState.extent) && ol.extent.intersects(extent, frameState.extent); if (clipped) { this.clip(context, frameState, /** @type {ol.Extent} */ (extent)); } var imageTransform = this.getImageTransform(); // for performance reasons, context.save / context.restore is not used // to save and restore the transformation matrix and the opacity. // see http://jsperf.com/context-save-restore-versus-variable var alpha = context.globalAlpha; context.globalAlpha = layerState.opacity; // for performance reasons, context.setTransform is only used // when the view is rotated. see http://jsperf.com/canvas-transform var dx = imageTransform[4]; var dy = imageTransform[5]; var dw = image.width * imageTransform[0]; var dh = image.height * imageTransform[3]; context.drawImage(image, 0, 0, +image.width, +image.height, Math.round(dx), Math.round(dy), Math.round(dw), Math.round(dh)); context.globalAlpha = alpha; if (clipped) { context.restore(); } } this.postCompose(context, frameState, layerState); };

三、ol.renderer.canvas.ImageLayer

实现了父类的prepareFrame()的方法

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
ol.renderer.canvas.ImageLayer.prototype.prepareFrame = function(frameState, layerState) { var pixelRatio = frameState.pixelRatio; var size = frameState.size; var viewState = frameState.viewState; var viewCenter = viewState.center; var viewResolution = viewState.resolution; var image; var imageLayer = /** @type {ol.layer.Image} */ (this.getLayer()); var imageSource = imageLayer.getSource(); var hints = frameState.viewHints; var renderedExtent = frameState.extent; if (layerState.extent !== undefined) { renderedExtent = ol.extent.getIntersection( renderedExtent, layerState.extent); } if (!hints[ol.ViewHint.ANIMATING] && !hints[ol.ViewHint.INTERACTING] && !ol.extent.isEmpty(renderedExtent)) { var projection = viewState.projection; if (!ol.ENABLE_RASTER_REPROJECTION) { var sourceProjection = imageSource.getProjection(); if (sourceProjection) { projection = sourceProjection; } } var vectorRenderer = this.vectorRenderer_; if (vectorRenderer) { var context = vectorRenderer.context; var imageFrameState = /** @type {olx.FrameState} */ (ol.obj.assign({}, frameState, { size: [ ol.extent.getWidth(renderedExtent) / viewResolution, ol.extent.getHeight(renderedExtent) / viewResolution ], viewState: /** @type {olx.ViewState} */ (ol.obj.assign({}, frameState.viewState, { rotation: 0 })) })); var skippedFeatures = Object.keys(imageFrameState.skippedFeatureUids).sort(); if (vectorRenderer.prepareFrame(imageFrameState, layerState) && (vectorRenderer.replayGroupChanged || !ol.array.equals(skippedFeatures, this.skippedFeatures_))) { context.canvas.width = imageFrameState.size[0] * pixelRatio; context.canvas.height = imageFrameState.size[1] * pixelRatio; vectorRenderer.composeFrame(imageFrameState, layerState, context); this.image_ = new ol.ImageCanvas(renderedExtent, viewResolution, pixelRatio, context.canvas); this.skippedFeatures_ = skippedFeatures; } } else { image = imageSource.getImage( renderedExtent, viewResolution, pixelRatio, projection); if (image) { var loaded = this.loadImage(image); if (loaded) { this.image_ = image; } } } } if (this.image_) { image = this.image_; var imageExtent = image.getExtent(); var imageResolution = image.getResolution(); var imagePixelRatio = image.getPixelRatio(); var scale = pixelRatio * imageResolution / (viewResolution * imagePixelRatio); var transform = ol.transform.compose(this.imageTransform_, pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, scale, scale, 0, imagePixelRatio * (imageExtent[0] - viewCenter[0]) / imageResolution, imagePixelRatio * (viewCenter[1] - imageExtent[3]) / imageResolution); ol.transform.compose(this.coordinateToCanvasPixelTransform, pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], pixelRatio / viewResolution, -pixelRatio / viewResolution, 0, -viewCenter[0], -viewCenter[1]); this.updateLogos(frameState, imageSource); this.renderedResolution = imageResolution * pixelRatio / imagePixelRatio; } return !!this.image_; };

四、ol.renderer.canvas.TileLayer

实现了父类的prepareFrame()的方法

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
ol.renderer.canvas.TileLayer.prototype.prepareFrame = function(frameState, layerState) { var pixelRatio = frameState.pixelRatio; var size = frameState.size; var viewState = frameState.viewState; var projection = viewState.projection; var viewResolution = viewState.resolution; var viewCenter = viewState.center; var tileLayer = this.getLayer(); var tileSource = /** @type {ol.source.Tile} */ (tileLayer.getSource()); var sourceRevision = tileSource.getRevision(); var tileGrid = tileSource.getTileGridForProjection(projection); var z = tileGrid.getZForResolution(viewResolution, this.zDirection); var tileResolution = tileGrid.getResolution(z); var oversampling = Math.round(viewResolution / tileResolution) || 1; var extent = frameState.extent; if (layerState.extent !== undefined) { extent = ol.extent.getIntersection(extent, layerState.extent); } if (ol.extent.isEmpty(extent)) { // Return false to prevent the rendering of the layer. return false; } var tileRange = tileGrid.getTileRangeForExtentAndZ(extent, z); var imageExtent = tileGrid.getTileRangeExtent(z, tileRange); var tilePixelRatio = tileSource.getTilePixelRatio(pixelRatio); /** * @type {Object.<number, Object.<string, ol.Tile>>} */ var tilesToDrawByZ = {}; tilesToDrawByZ[z] = {}; var findLoadedTiles = this.createLoadedTileFinder( tileSource, projection, tilesToDrawByZ); var tmpExtent = this.tmpExtent; var tmpTileRange = this.tmpTileRange_; var newTiles = false; var tile, x, y; for (x = tileRange.minX; x <= tileRange.maxX; ++x) { for (y = tileRange.minY; y <= tileRange.maxY; ++y) { tile = tileSource.getTile(z, x, y, pixelRatio, projection); if (tile.getState() == ol.TileState.ERROR) { if (!tileLayer.getUseInterimTilesOnError()) { // When useInterimTilesOnError is false, we consider the error tile as loaded. tile.setState(ol.TileState.LOADED); } else if (tileLayer.getPreload() > 0) { // Preloaded tiles for lower resolutions might have finished loading. newTiles = true; } } if (!this.isDrawableTile_(tile)) { tile = tile.getInterimTile(); } if (this.isDrawableTile_(tile)) { var uid = ol.getUid(this); if (tile.getState() == ol.TileState.LOADED) { tilesToDrawByZ[z][tile.tileCoord.toString()] = tile; var inTransition = tile.inTransition(uid); if (!newTiles && (inTransition || this.renderedTiles.indexOf(tile) === -1)) { newTiles = true; } } if (tile.getAlpha(uid, frameState.time) === 1) { // don't look for alt tiles if alpha is 1 continue; } } var childTileRange = tileGrid.getTileCoordChildTileRange( tile.tileCoord, tmpTileRange, tmpExtent); var covered = false; if (childTileRange) { covered = findLoadedTiles(z + 1, childTileRange); } if (!covered) { tileGrid.forEachTileCoordParentTileRange( tile.tileCoord, findLoadedTiles, null, tmpTileRange, tmpExtent); } } } var renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; var hints = frameState.viewHints; var animatingOrInteracting = hints[ol.ViewHint.ANIMATING] || hints[ol.ViewHint.INTERACTING]; if (!(this.renderedResolution && Date.now() - frameState.time > 16 && animatingOrInteracting) && ( newTiles || !(this.renderedExtent_ && ol.extent.containsExtent(this.renderedExtent_, extent)) || this.renderedRevision != sourceRevision || oversampling != this.oversampling_ || !animatingOrInteracting && renderedResolution != this.renderedResolution )) { var context = this.context; if (context) { var tilePixelSize = tileSource.getTilePixelSize(z, pixelRatio, projection); var width = Math.round(tileRange.getWidth() * tilePixelSize[0] / oversampling); var height = Math.round(tileRange.getHeight() * tilePixelSize[1] / oversampling); var canvas = context.canvas; if (canvas.width != width || canvas.height != height) { this.oversampling_ = oversampling; canvas.width = width; canvas.height = height; } else { if (this.renderedExtent_ && !ol.extent.equals(imageExtent, this.renderedExtent_)) { context.clearRect(0, 0, width, height); } oversampling = this.oversampling_; } } this.renderedTiles.length = 0; /** @type {Array.<number>} */ var zs = Object.keys(tilesToDrawByZ).map(Number); zs.sort(function(a, b) { if (a === z) { return 1; } else if (b === z) { return -1; } else { return a > b ? 1 : a < b ? -1 : 0; } }); var currentResolution, currentScale, currentTilePixelSize, currentZ, i, ii; var tileExtent, tileGutter, tilesToDraw, w, h; for (i = 0, ii = zs.length; i < ii; ++i) { currentZ = zs[i]; currentTilePixelSize = tileSource.getTilePixelSize(currentZ, pixelRatio, projection); currentResolution = tileGrid.getResolution(currentZ); currentScale = currentResolution / tileResolution; tileGutter = tilePixelRatio * tileSource.getGutter(projection); tilesToDraw = tilesToDrawByZ[currentZ]; for (var tileCoordKey in tilesToDraw) { tile = tilesToDraw[tileCoordKey]; tileExtent = tileGrid.getTileCoordExtent(tile.getTileCoord(), tmpExtent); x = (tileExtent[0] - imageExtent[0]) / tileResolution * tilePixelRatio / oversampling; y = (imageExtent[3] - tileExtent[3]) / tileResolution * tilePixelRatio / oversampling; w = currentTilePixelSize[0] * currentScale / oversampling; h = currentTilePixelSize[1] * currentScale / oversampling; this.drawTileImage(tile, frameState, layerState, x, y, w, h, tileGutter, z === currentZ); this.renderedTiles.push(tile); } } this.renderedRevision = sourceRevision; this.renderedResolution = tileResolution * pixelRatio / tilePixelRatio * oversampling; this.renderedExtent_ = imageExtent; } var scale = this.renderedResolution / viewResolution; var transform = ol.transform.compose(this.imageTransform_, pixelRatio * size[0] / 2, pixelRatio * size[1] / 2, scale, scale, 0, (this.renderedExtent_[0] - viewCenter[0]) / this.renderedResolution * pixelRatio, (viewCenter[1] - this.renderedExtent_[3]) / this.renderedResolution * pixelRatio); ol.transform.compose(this.coordinateToCanvasPixelTransform, pixelRatio * size[0] / 2 - transform[4], pixelRatio * size[1] / 2 - transform[5], pixelRatio / viewResolution, -pixelRatio / viewResolution, 0, -viewCenter[0], -viewCenter[1]); this.updateUsedTiles(frameState.usedTiles, tileSource, z, tileRange); this.manageTilePyramid(frameState, tileSource, tileGrid, pixelRatio, projection, extent, z, tileLayer.getPreload()); this.scheduleExpireCache(frameState, tileSource); this.updateLogos(frameState, tileSource); return this.renderedTiles.length > 0; };

drawTileImage() 根据x,y绘制tile

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
/** * @param {ol.Tile} tile Tile. * @param {olx.FrameState} frameState Frame state. * @param {ol.LayerState} layerState Layer state. * @param {number} x Left of the tile. * @param {number} y Top of the tile. * @param {number} w Width of the tile. * @param {number} h Height of the tile. * @param {number} gutter Tile gutter. * @param {boolean} transition Apply an alpha transition. */ ol.renderer.canvas.TileLayer.prototype.drawTileImage = function(tile, frameState, layerState, x, y, w, h, gutter, transition) { var image = tile.getImage(this.getLayer()); if (!image) { return; } var uid = ol.getUid(this); var alpha = transition ? tile.getAlpha(uid, frameState.time) : 1; if (alpha === 1 && !this.getLayer().getSource().getOpaque(frameState.viewState.projection)) { this.context.clearRect(x, y, w, h); } var alphaChanged = alpha !== this.context.globalAlpha; if (alphaChanged) { this.context.save(); this.context.globalAlpha = alpha; } this.context.drawImage(image, gutter, gutter, image.width - 2 * gutter, image.height - 2 * gutter, x, y, w, h); if (alphaChanged) { this.context.restore(); } if (alpha !== 1) { frameState.animate = true; } else if (transition) { tile.endTransition(uid); } };

五、 ol.renderer.canvas.VectorLayer

实现父类的composeFrame()、prepareFrame()方法

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
ol.renderer.canvas.VectorLayer.prototype.composeFrame = function(frameState, layerState, context) { var extent = frameState.extent; var pixelRatio = frameState.pixelRatio; var skippedFeatureUids = layerState.managed ? frameState.skippedFeatureUids : {}; var viewState = frameState.viewState; var projection = viewState.projection; var rotation = viewState.rotation; var projectionExtent = projection.getExtent(); var vectorSource = /** @type {ol.source.Vector} */ (this.getLayer().getSource()); var transform = this.getTransform(frameState, 0); this.preCompose(context, frameState, transform); // clipped rendering if layer extent is set var clipExtent = layerState.extent; var clipped = clipExtent !== undefined; if (clipped) { this.clip(context, frameState, /** @type {ol.Extent} */ (clipExtent)); } var replayGroup = this.replayGroup_; if (replayGroup && !replayGroup.isEmpty()) { if (this.declutterTree_) { this.declutterTree_.clear(); } var layer = /** @type {ol.layer.Vector} */ (this.getLayer()); var drawOffsetX = 0; var drawOffsetY = 0; var replayContext; var transparentLayer = layerState.opacity !== 1; var hasRenderListeners = layer.hasListener(ol.render.EventType.RENDER); if (transparentLayer || hasRenderListeners) { var drawWidth = context.canvas.width; var drawHeight = context.canvas.height; if (rotation) { var drawSize = Math.round(Math.sqrt(drawWidth * drawWidth + drawHeight * drawHeight)); drawOffsetX = (drawSize - drawWidth) / 2; drawOffsetY = (drawSize - drawHeight) / 2; drawWidth = drawHeight = drawSize; } // resize and clear this.context.canvas.width = drawWidth; this.context.canvas.height = drawHeight; replayContext = this.context; } else { replayContext = context; } var alpha = replayContext.globalAlpha; if (!transparentLayer) { // for performance reasons, context.save / context.restore is not used // to save and restore the transformation matrix and the opacity. // see http://jsperf.com/context-save-restore-versus-variable replayContext.globalAlpha = layerState.opacity; } if (replayContext != context) { replayContext.translate(drawOffsetX, drawOffsetY); } var width = frameState.size[0] * pixelRatio; var height = frameState.size[1] * pixelRatio; ol.render.canvas.rotateAtOffset(replayContext, -rotation, width / 2, height / 2); replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids); if (vectorSource.getWrapX() && projection.canWrapX() && !ol.extent.containsExtent(projectionExtent, extent)) { var startX = extent[0]; var worldWidth = ol.extent.getWidth(projectionExtent); var world = 0; var offsetX; while (startX < projectionExtent[0]) { --world; offsetX = worldWidth * world; transform = this.getTransform(frameState, offsetX); replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids); startX += worldWidth; } world = 0; startX = extent[2]; while (startX > projectionExtent[2]) { ++world; offsetX = worldWidth * world; transform = this.getTransform(frameState, offsetX); replayGroup.replay(replayContext, transform, rotation, skippedFeatureUids); startX -= worldWidth; } // restore original transform for render and compose events transform = this.getTransform(frameState, 0); } ol.render.canvas.rotateAtOffset(replayContext, rotation, width / 2, height / 2); if (replayContext != context) { if (hasRenderListeners) { this.dispatchRenderEvent(replayContext, frameState, transform); } if (transparentLayer) { var mainContextAlpha = context.globalAlpha; context.globalAlpha = layerState.opacity; context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); context.globalAlpha = mainContextAlpha; } else { context.drawImage(replayContext.canvas, -drawOffsetX, -drawOffsetY); } replayContext.translate(-drawOffsetX, -drawOffsetY); } if (!transparentLayer) { replayContext.globalAlpha = alpha; } } if (clipped) { context.restore(); } this.postCompose(context, frameState, layerState, transform); }; ol.renderer.canvas.VectorLayer.prototype.prepareFrame = function(frameState, layerState) { var vectorLayer = /** @type {ol.layer.Vector} */ (this.getLayer()); var vectorSource = vectorLayer.getSource(); this.updateLogos(frameState, vectorSource); var animating = frameState.viewHints[ol.ViewHint.ANIMATING]; var interacting = frameState.viewHints[ol.ViewHint.INTERACTING]; var updateWhileAnimating = vectorLayer.getUpdateWhileAnimating(); var updateWhileInteracting = vectorLayer.getUpdateWhileInteracting(); if (!this.dirty_ && (!updateWhileAnimating && animating) || (!updateWhileInteracting && interacting)) { return true; } var frameStateExtent = frameState.extent; var viewState = frameState.viewState; var projection = viewState.projection; var resolution = viewState.resolution; var pixelRatio = frameState.pixelRatio; var vectorLayerRevision = vectorLayer.getRevision(); var vectorLayerRenderBuffer = vectorLayer.getRenderBuffer(); var vectorLayerRenderOrder = vectorLayer.getRenderOrder(); if (vectorLayerRenderOrder === undefined) { vectorLayerRenderOrder = ol.renderer.vector.defaultOrder; } var extent = ol.extent.buffer(frameStateExtent, vectorLayerRenderBuffer * resolution); var projectionExtent = viewState.projection.getExtent(); if (vectorSource.getWrapX() && viewState.projection.canWrapX() && !ol.extent.containsExtent(projectionExtent, frameState.extent)) { // For the replay group, we need an extent that intersects the real world // (-180° to +180°). To support geometries in a coordinate range from -540° // to +540°, we add at least 1 world width on each side of the projection // extent. If the viewport is wider than the world, we need to add half of // the viewport width to make sure we cover the whole viewport. var worldWidth = ol.extent.getWidth(projectionExtent); var buffer = Math.max(ol.extent.getWidth(extent) / 2, worldWidth); extent[0] = projectionExtent[0] - buffer; extent[2] = projectionExtent[2] + buffer; } if (!this.dirty_ && this.renderedResolution_ == resolution && this.renderedRevision_ == vectorLayerRevision && this.renderedRenderOrder_ == vectorLayerRenderOrder && ol.extent.containsExtent(this.renderedExtent_, extent)) { this.replayGroupChanged = false; return true; } this.replayGroup_ = null; this.dirty_ = false; var replayGroup = new ol.render.canvas.ReplayGroup( ol.renderer.vector.getTolerance(resolution, pixelRatio), extent, resolution, pixelRatio, vectorSource.getOverlaps(), this.declutterTree_, vectorLayer.getRenderBuffer()); vectorSource.loadFeatures(extent, resolution, projection); /** * @param {ol.Feature} feature Feature. * @this {ol.renderer.canvas.VectorLayer} */ var renderFeature = function(feature) { var styles; var styleFunction = feature.getStyleFunction(); if (styleFunction) { styles = styleFunction.call(feature, resolution); } else { styleFunction = vectorLayer.getStyleFunction(); if (styleFunction) { styles = styleFunction(feature, resolution); } } if (styles) { var dirty = this.renderFeature( feature, resolution, pixelRatio, styles, replayGroup); this.dirty_ = this.dirty_ || dirty; } }.bind(this); if (vectorLayerRenderOrder) { /** @type {Array.<ol.Feature>} */ var features = []; vectorSource.forEachFeatureInExtent(extent, /** * @param {ol.Feature} feature Feature. */ function(feature) { features.push(feature); }, this); features.sort(vectorLayerRenderOrder); for (var i = 0, ii = features.length; i < ii; ++i) { renderFeature(features[i]); } } else { vectorSource.forEachFeatureInExtent(extent, renderFeature, this); } replayGroup.finish(); this.renderedResolution_ = resolution; this.renderedRevision_ = vectorLayerRevision; this.renderedRenderOrder_ = vectorLayerRenderOrder; this.renderedExtent_ = extent; this.replayGroup_ = replayGroup; this.replayGroupChanged = true; return true; };

renderFeature() 渲染要素

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
/** * @param {ol.Feature} feature Feature. * @param {number} resolution Resolution. * @param {number} pixelRatio Pixel ratio. * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of * styles. * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. * @return {boolean} `true` if an image is loading. */ ol.renderer.canvas.VectorLayer.prototype.renderFeature = function(feature, resolution, pixelRatio, styles, replayGroup) { if (!styles) { return false; } var loading = false; if (Array.isArray(styles)) { for (var i = 0, ii = styles.length; i < ii; ++i) { loading = ol.renderer.vector.renderFeature( replayGroup, feature, styles[i], ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), this.handleStyleImageChange_, this) || loading; } } else { loading = ol.renderer.vector.renderFeature( replayGroup, feature, styles, ol.renderer.vector.getSquaredTolerance(resolution, pixelRatio), this.handleStyleImageChange_, this); } return loading; };

六、 ol.renderer.canvas.VectorTileLayer

VectorTileLayer的渲染方式是TileLayer和VectorLayer两种方式的结合。

复制代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
/** * @inheritDoc */ ol.renderer.canvas.VectorTileLayer.prototype.prepareFrame = function(frameState, layerState) { var layer = this.getLayer(); var layerRevision = layer.getRevision(); if (this.renderedLayerRevision_ != layerRevision) { this.renderedTiles.length = 0; var renderMode = layer.getRenderMode(); if (!this.context && renderMode != ol.layer.VectorTileRenderType.VECTOR) { this.context = ol.dom.createCanvasContext2D(); } if (this.context && renderMode == ol.layer.VectorTileRenderType.VECTOR) { this.context = null; } } this.renderedLayerRevision_ = layerRevision; return ol.renderer.canvas.TileLayer.prototype.prepareFrame.apply(this, arguments); }; /** * @param {ol.VectorImageTile} tile Tile. * @param {olx.FrameState} frameState Frame state. * @private */ ol.renderer.canvas.VectorTileLayer.prototype.createReplayGroup_ = function( tile, frameState) { var layer = this.getLayer(); var pixelRatio = frameState.pixelRatio; var projection = frameState.viewState.projection; var revision = layer.getRevision(); var renderOrder = /** @type {ol.RenderOrderFunction} */ (layer.getRenderOrder()) || null; var replayState = tile.getReplayState(layer); if (!replayState.dirty && replayState.renderedRevision == revision && replayState.renderedRenderOrder == renderOrder) { return; } var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); var sourceTileGrid = source.getTileGrid(); var tileGrid = source.getTileGridForProjection(projection); var resolution = tileGrid.getResolution(tile.tileCoord[0]); var tileExtent = tileGrid.getTileCoordExtent(tile.wrappedTileCoord); var zIndexKeys = {}; for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { var sourceTile = tile.getTile(tile.tileKeys[t]); if (sourceTile.getState() == ol.TileState.ERROR) { continue; } var sourceTileCoord = sourceTile.tileCoord; var sourceTileExtent = sourceTileGrid.getTileCoordExtent(sourceTileCoord); var sharedExtent = ol.extent.getIntersection(tileExtent, sourceTileExtent); var bufferedExtent = ol.extent.equals(sourceTileExtent, sharedExtent) ? null : ol.extent.buffer(sharedExtent, layer.getRenderBuffer() * resolution); var tileProjection = sourceTile.getProjection(); var reproject = false; if (!ol.proj.equivalent(projection, tileProjection)) { reproject = true; sourceTile.setProjection(projection); } replayState.dirty = false; var replayGroup = new ol.render.canvas.ReplayGroup(0, sharedExtent, resolution, pixelRatio, source.getOverlaps(), this.declutterTree_, layer.getRenderBuffer()); var squaredTolerance = ol.renderer.vector.getSquaredTolerance( resolution, pixelRatio); /** * @param {ol.Feature|ol.render.Feature} feature Feature. * @this {ol.renderer.canvas.VectorTileLayer} */ var renderFeature = function(feature) { var styles; var styleFunction = feature.getStyleFunction(); if (styleFunction) { styles = styleFunction.call(/** @type {ol.Feature} */ (feature), resolution); } else { styleFunction = layer.getStyleFunction(); if (styleFunction) { styles = styleFunction(feature, resolution); } } if (styles) { var dirty = this.renderFeature(feature, squaredTolerance, styles, replayGroup); this.dirty_ = this.dirty_ || dirty; replayState.dirty = replayState.dirty || dirty; } }; var features = sourceTile.getFeatures(); if (renderOrder && renderOrder !== replayState.renderedRenderOrder) { features.sort(renderOrder); } var feature; for (var i = 0, ii = features.length; i < ii; ++i) { feature = features[i]; if (reproject) { if (tileProjection.getUnits() == ol.proj.Units.TILE_PIXELS) { // projected tile extent tileProjection.setWorldExtent(sourceTileExtent); // tile extent in tile pixel space tileProjection.setExtent(sourceTile.getExtent()); } feature.getGeometry().transform(tileProjection, projection); } if (!bufferedExtent || ol.extent.intersects(bufferedExtent, feature.getGeometry().getExtent())) { renderFeature.call(this, feature); } } replayGroup.finish(); for (var r in replayGroup.getReplays()) { zIndexKeys[r] = true; } sourceTile.setReplayGroup(layer, tile.tileCoord.toString(), replayGroup); } replayState.renderedRevision = revision; replayState.renderedRenderOrder = renderOrder; }; /** * @inheritDoc */ ol.renderer.canvas.VectorTileLayer.prototype.drawTileImage = function( tile, frameState, layerState, x, y, w, h, gutter, transition) { var vectorImageTile = /** @type {ol.VectorImageTile} */ (tile); this.createReplayGroup_(vectorImageTile, frameState); if (this.context) { this.renderTileImage_(vectorImageTile, frameState, layerState); ol.renderer.canvas.TileLayer.prototype.drawTileImage.apply(this, arguments); } }; /** * @inheritDoc */ ol.renderer.canvas.VectorTileLayer.prototype.postCompose = function(context, frameState, layerState) { var layer = this.getLayer(); var declutterReplays = layer.getDeclutter() ? {} : null; var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); var renderMode = layer.getRenderMode(); var replayTypes = ol.renderer.canvas.VectorTileLayer.VECTOR_REPLAYS[renderMode]; var pixelRatio = frameState.pixelRatio; var rotation = frameState.viewState.rotation; var size = frameState.size; var offsetX, offsetY; if (rotation) { offsetX = Math.round(pixelRatio * size[0] / 2); offsetY = Math.round(pixelRatio * size[1] / 2); ol.render.canvas.rotateAtOffset(context, -rotation, offsetX, offsetY); } if (declutterReplays) { this.declutterTree_.clear(); } var tiles = this.renderedTiles; var tileGrid = source.getTileGridForProjection(frameState.viewState.projection); var clips = []; var zs = []; for (var i = tiles.length - 1; i >= 0; --i) { var tile = /** @type {ol.VectorImageTile} */ (tiles[i]); if (tile.getState() == ol.TileState.ABORT) { continue; } var tileCoord = tile.tileCoord; var worldOffset = tileGrid.getTileCoordExtent(tileCoord)[0] - tileGrid.getTileCoordExtent(tile.wrappedTileCoord)[0]; var transform = undefined; for (var t = 0, tt = tile.tileKeys.length; t < tt; ++t) { var sourceTile = tile.getTile(tile.tileKeys[t]); if (sourceTile.getState() == ol.TileState.ERROR) { continue; } var replayGroup = sourceTile.getReplayGroup(layer, tileCoord.toString()); if (renderMode != ol.layer.VectorTileRenderType.VECTOR && !replayGroup.hasReplays(replayTypes)) { continue; } if (!transform) { transform = this.getTransform(frameState, worldOffset); } var currentZ = sourceTile.tileCoord[0]; var currentClip = replayGroup.getClipCoords(transform); context.save(); context.globalAlpha = layerState.opacity; // Create a clip mask for regions in this low resolution tile that are // already filled by a higher resolution tile for (var j = 0, jj = clips.length; j < jj; ++j) { var clip = clips[j]; if (currentZ < zs[j]) { context.beginPath(); // counter-clockwise (outer ring) for current tile context.moveTo(currentClip[0], currentClip[1]); context.lineTo(currentClip[2], currentClip[3]); context.lineTo(currentClip[4], currentClip[5]); context.lineTo(currentClip[6], currentClip[7]); // clockwise (inner ring) for higher resolution tile context.moveTo(clip[6], clip[7]); context.lineTo(clip[4], clip[5]); context.lineTo(clip[2], clip[3]); context.lineTo(clip[0], clip[1]); context.clip(); } } replayGroup.replay(context, transform, rotation, {}, replayTypes, declutterReplays); context.restore(); clips.push(currentClip); zs.push(currentZ); } } if (declutterReplays) { ol.render.canvas.ReplayGroup.replayDeclutter(declutterReplays, context, rotation); } if (rotation) { ol.render.canvas.rotateAtOffset(context, rotation, /** @type {number} */ (offsetX), /** @type {number} */ (offsetY)); } ol.renderer.canvas.TileLayer.prototype.postCompose.apply(this, arguments); }; /** * @param {ol.Feature|ol.render.Feature} feature Feature. * @param {number} squaredTolerance Squared tolerance. * @param {(ol.style.Style|Array.<ol.style.Style>)} styles The style or array of * styles. * @param {ol.render.canvas.ReplayGroup} replayGroup Replay group. * @return {boolean} `true` if an image is loading. */ ol.renderer.canvas.VectorTileLayer.prototype.renderFeature = function(feature, squaredTolerance, styles, replayGroup) { if (!styles) { return false; } var loading = false; if (Array.isArray(styles)) { for (var i = 0, ii = styles.length; i < ii; ++i) { loading = ol.renderer.vector.renderFeature( replayGroup, feature, styles[i], squaredTolerance, this.handleStyleImageChange_, this) || loading; } } else { loading = ol.renderer.vector.renderFeature( replayGroup, feature, styles, squaredTolerance, this.handleStyleImageChange_, this); } return loading; }; /** * @param {ol.VectorImageTile} tile Tile. * @param {olx.FrameState} frameState Frame state. * @param {ol.LayerState} layerState Layer state. * @private */ ol.renderer.canvas.VectorTileLayer.prototype.renderTileImage_ = function( tile, frameState, layerState) { var layer = this.getLayer(); var replayState = tile.getReplayState(layer); var revision = layer.getRevision(); var replays = ol.renderer.canvas.VectorTileLayer.IMAGE_REPLAYS[layer.getRenderMode()]; if (replays && replayState.renderedTileRevision !== revision) { replayState.renderedTileRevision = revision; var tileCoord = tile.wrappedTileCoord; var z = tileCoord[0]; var pixelRatio = frameState.pixelRatio; var source = /** @type {ol.source.VectorTile} */ (layer.getSource()); var tileGrid = source.getTileGridForProjection(frameState.viewState.projection); var resolution = tileGrid.getResolution(z); var context = tile.getContext(layer); var size = source.getTilePixelSize(z, pixelRatio, frameState.viewState.projection); context.canvas.width = size[0]; context.canvas.height = size[1]; var tileExtent = tileGrid.getTileCoordExtent(tileCoord); for (var i = 0, ii = tile.tileKeys.length; i < ii; ++i) { var sourceTile = tile.getTile(tile.tileKeys[i]); if (sourceTile.getState() == ol.TileState.ERROR) { continue; } var pixelScale = pixelRatio / resolution; var transform = ol.transform.reset(this.tmpTransform_); ol.transform.scale(transform, pixelScale, -pixelScale); ol.transform.translate(transform, -tileExtent[0], -tileExtent[3]); var replayGroup = sourceTile.getReplayGroup(layer, tile.tileCoord.toString()); replayGroup.replay(context, transform, 0, {}, replays); } } };

最后

以上就是听话鼠标最近收集整理的关于Openlayers源码阅读(七):图层canvas渲染的全部内容,更多相关Openlayers源码阅读(七)内容请搜索靠谱客的其他文章。

本图文内容来源于网友提供,作为学习参考使用,或来自网络收集整理,版权属于原作者所有。
点赞(58)

评论列表共有 0 条评论

立即
投稿
返回
顶部