@mesh3d/cesium-vectortile-gl 0.4.4 → 0.4.6

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (51) hide show
  1. package/.gitattributes +11 -0
  2. package/.gitconfig +3 -0
  3. package/.husky/pre-commit +1 -0
  4. package/.prettierignore +5 -0
  5. package/.vscode/settings.json +25 -0
  6. package/LICENSE.md +203 -203
  7. package/README.md +202 -167
  8. package/Source/Cesium.d.ts +2692 -2691
  9. package/Source/VectorTileLOD.js +720 -532
  10. package/Source/VectorTileRenderList.js +70 -70
  11. package/Source/VectorTileset.js +473 -447
  12. package/Source/layers/BackgroundRenderLayer.js +91 -89
  13. package/Source/layers/FillRenderLayer.js +18 -18
  14. package/Source/layers/IRenderLayer.js +160 -152
  15. package/Source/layers/LineRenderLayer.js +104 -94
  16. package/Source/layers/SymbolRenderLayer.js +30 -31
  17. package/Source/layers/index.js +23 -16
  18. package/Source/layers/registerRenderLayer.js +24 -24
  19. package/Source/layers/visualizers/FillLayerVisualizer.js +542 -426
  20. package/Source/layers/visualizers/ILayerVisualizer.js +90 -94
  21. package/Source/layers/visualizers/LineLayerVisualizer.js +702 -571
  22. package/Source/layers/visualizers/SymbolLayerVisualizer.js +514 -244
  23. package/Source/sources/GeoJSONSource.js +53 -46
  24. package/Source/sources/ISource.js +39 -39
  25. package/Source/sources/VectorSource.js +94 -52
  26. package/Source/sources/granularitySettings.js +23 -20
  27. package/Source/sources/index.js +6 -11
  28. package/Source/sources/registerSource.js +17 -19
  29. package/Source/style/StyleLayer.js +43 -43
  30. package/Source/style/StyleLayerProperties.js +44 -43
  31. package/Source/style/index.js +2 -2
  32. package/Source/symbol/SymbolPlacements.js +117 -88
  33. package/Source/workers/VectorTileWorker.js +41 -0
  34. package/Source/workers/ellipsoid.js +47 -0
  35. package/Source/workers/processTileTask.js +329 -0
  36. package/Source/workers/styleEvaluator.js +168 -0
  37. package/benchmark.html +148 -0
  38. package/dist/cvt-gl-worker.js +9274 -0
  39. package/dist/cvt-gl-worker.js.map +1 -0
  40. package/dist/cvt-gl.js +2570 -2001
  41. package/dist/cvt-gl.js.map +1 -1
  42. package/dist/cvt-gl.min.js +3 -3
  43. package/dist/cvt-gl.min.js.map +1 -1
  44. package/eslint.config.mjs +58 -0
  45. package/index.js +9 -6
  46. package/mlt.html +26 -25
  47. package/package.json +64 -41
  48. package/prettier.config.mjs +30 -0
  49. package/vite.config.mjs +43 -0
  50. package/vite.worker.config.mjs +31 -0
  51. package/worker.html +26 -0
@@ -1,447 +1,473 @@
1
- import { VectorTileLOD } from "./VectorTileLOD"
2
- import { StyleLayer } from "./style/StyleLayer"
3
- import './layers/index'
4
- import { VectorTileRenderList } from "./VectorTileRenderList"
5
- import { Sources } from "./sources"
6
- import { ISource } from "./sources/ISource"
7
- import { warnOnce } from "maplibre-gl/src/util/util"
8
- import { SymbolPlacements } from "./symbol/SymbolPlacements"
9
-
10
- export class VectorTileset {
11
- /**
12
- * @param {object} options
13
- * @param {string|import('@maplibre/maplibre-gl-style-spec').StyleSpecification} options.style
14
- * @param {boolean} [options.showTileColor=false]
15
- */
16
- constructor(options) {
17
- this.maximumLevel = 24
18
- this.show = true
19
- this.showTileColor = !!options.showTileColor
20
- this.ready = false
21
- this.tilingScheme = new Cesium.WebMercatorTilingScheme()
22
-
23
- this.readyEvent = new Cesium.Event()
24
- this.errorEvent = new Cesium.Event()
25
-
26
- this._styleJson = null
27
- this._style = options.style
28
- this._rootTiles = []
29
- this._cacheTiles = []
30
- this._tilesToUpdate = []
31
- this._tilesToRender = []
32
- /**@type {StyleLayer[]} */
33
- this._styleLayers = []
34
- /**@type {VectorTileRenderList} */
35
- this._renderList = new VectorTileRenderList(this._styleLayers)
36
- this.numLoading = 0
37
- this.maxLoading = 6
38
- this.numInitializing = 0
39
- this.maxInitializing = 6
40
- /**@type {Cesium.Texture} */
41
- this.tileIdTexture = null
42
- this.zoom = 0
43
- /**
44
- * 负责符号碰撞检测(自动避让),SymbolPlacements 内部基于 maplibre-gl GridIndex 实现
45
- */
46
- this._symbolPlacements = new SymbolPlacements()
47
-
48
- requestAnimationFrame(() => {
49
- this.init()
50
- })
51
- }
52
-
53
- async init() {
54
- let style = this._style
55
- if (!style) {
56
- this.errorEvent.raiseEvent(new Error('请传入 style 参数'))
57
- return
58
- }
59
-
60
- this.path = ''
61
- if (typeof style == 'string') {
62
- this.path = style.split('/').slice(0, -1).join('/')
63
- if (this.path) this.path += '/'
64
- style = await Cesium.Resource.fetchJson(style)
65
- }
66
-
67
- //初始化数据源
68
-
69
- /** @type {{[sourceId:string]:ISource}}*/
70
- this.sources = {}
71
- for (const sourceId in style.sources) {
72
- /**@type {import('@maplibre/maplibre-gl-style-spec').SourceSpecification} */
73
- const sourceParams = style.sources[sourceId]
74
- const SourceCls = Sources[sourceParams.type]
75
- if (SourceCls) {
76
- this.sources[sourceId] = new SourceCls(sourceParams, this.path)
77
- try {
78
- await this.sources[sourceId].init()
79
- this.maximumLevel = Math.min(sourceParams.maxzoom || 24, this.maximumLevel)
80
- } catch (err) {
81
- this.errorEvent.raiseEvent(err)
82
- }
83
- }
84
- }
85
-
86
- //初始化样式图层
87
- for (let i = 0; i < style.layers.length; i++) {
88
- this._styleLayers[i] = new StyleLayer(style.layers[i])
89
- }
90
-
91
- //创建顶级瓦片LOD
92
- const numX = this.tilingScheme.getNumberOfXTilesAtLevel(0)
93
- const numY = this.tilingScheme.getNumberOfYTilesAtLevel(0)
94
- let i = 0
95
- for (let y = 0; y < numY; y++) {
96
- for (let x = 0; x < numX; x++) {
97
- var tile = new VectorTileLOD({
98
- parent: this, x, y, z: 0,
99
- tilingScheme: this.tilingScheme
100
- })
101
- tile.createChildren()
102
- this._rootTiles[i++] = tile
103
- }
104
- }
105
-
106
- //初始化渲染队列
107
- this._renderList.init()
108
-
109
- this._styleJson = style
110
- this.ready = true
111
- this.readyEvent.raiseEvent(this)
112
- }
113
-
114
- //更新瓦片id纹理,用于裁剪超出瓦片边界的像素
115
- executeTileIdCommands(frameState) {
116
- const tileIdCommands = this._renderList.tileIdCommands
117
-
118
- if (tileIdCommands.length > 0) {
119
- const context = frameState.context
120
- /**@type {Cesium.FrameBuffer} */
121
- let tileIdFbo = this._tileIdFbo
122
- if (!tileIdFbo) {
123
- tileIdFbo = new Cesium.FramebufferManager({
124
- depthStencil: true,
125
- supportsDepthTexture: true,
126
- })
127
- this._tileIdFbo = tileIdFbo
128
- this._idClearCommand = new Cesium.ClearCommand({
129
- color: new Cesium.Color(0.0, 0.0, 0.0, 0.0),
130
- depth: 1.0,
131
- stencil: 0.0,
132
- })
133
- }
134
- const pixelDatatype = context.floatingPointTexture
135
- ? Cesium.PixelDatatype.FLOAT
136
- : Cesium.PixelDatatype.UNSIGNED_BYTE;
137
- const width = context.drawingBufferWidth
138
- const height = context.drawingBufferHeight
139
- tileIdFbo.update(context, width, height, 1, pixelDatatype)
140
- tileIdFbo.clear(context, this._idClearCommand)
141
-
142
- const framebuffer = tileIdFbo.framebuffer
143
- for (const tileIdCommand of tileIdCommands) {
144
- tileIdCommand.framebuffer = framebuffer
145
- tileIdCommand.execute(context)
146
- }
147
-
148
- this.tileIdTexture = tileIdFbo.getColorTexture(0)
149
- }
150
- }
151
-
152
- update(frameState) {
153
- if (!this.ready || !this.show) return
154
-
155
- if (frameState.context.webgl2) {
156
- warnOnce('webgl2模式下贴地线面的支持将导致性能下降')
157
- }
158
-
159
- const renderList = this._renderList
160
- //清空渲染队列
161
- renderList.beginFrame()
162
-
163
- this.numInitializing = 0
164
-
165
- /**@type {Cesium.Globe} */
166
- const scene = frameState.camera._scene
167
- const globe = scene.globe
168
- const globeSuspendLodUpdate = globe._surface._debug.suspendLodUpdate
169
- this.scene = scene
170
-
171
- // 获取可见瓦片
172
- // 优化:采用更高效的LOD调度算法,获取当前帧实际可渲染到屏幕的瓦片,避免出现瓦片层级切换时候出现闪烁
173
-
174
- /**@type {VectorTileLOD[]} */
175
- const tilesToUpdate = getTilesToUpdate(frameState, this)
176
- // const tilesToUpdate = globeSuspendLodUpdate ? this._tilesToUpdate : getTilesToUpdate(frameState, this)
177
-
178
- //瓦片排序,决定瓦片加载瓦片数据、初始化的优先级
179
- //优化:采用更精细、高效的优先级策略
180
- if (!globeSuspendLodUpdate) {
181
- tilesToUpdate.sort((a, b) => a.distanceToCamera - b.distanceToCamera)
182
- }
183
-
184
- //更新瓦片状态:请求瓦片数据,创建渲染图层,初始化等
185
- for (const tile of tilesToUpdate) {
186
- tile.lastVisitTime = frameState.frameNumber
187
- tile.expired = false
188
- tile.update(frameState, renderList, this)
189
- }
190
-
191
- /**@type {VectorTileLOD[]} */
192
- const tilesToRender = globeSuspendLodUpdate ? this._tilesToRender : getTilesToRender(tilesToUpdate, this._tilesToRender)
193
- if (!globeSuspendLodUpdate) {
194
- tilesToRender.sort((a, b) => a.distanceToCamera - b.distanceToCamera)
195
- }
196
- //渲染瓦片内容
197
- for (const tile of tilesToRender) {
198
- tile.lastVisitTime = frameState.frameNumber
199
- tile.expired = false
200
- tile.render(frameState, renderList, this)
201
- }
202
-
203
- //渲染图层分组、排序
204
- const orderedRenderLayers = renderList.getList()
205
- //符号碰撞检测
206
- this._symbolPlacements.update(frameState, orderedRenderLayers, this.zoom)
207
- //获取渲染命令(DrawCommand),渲染图层内部可以使用Primitive、PolylineCollection、LabelCollection、BillboardCollection等API,
208
- //也可以自定义DrawCommand
209
- for (const renderLayer of orderedRenderLayers) {
210
- renderLayer.render(frameState, this)
211
- }
212
- for (const visualizer of renderList.visualizers) {
213
- visualizer.render(frameState, this)
214
- }
215
- //瓦片颜色、深度
216
- frameState.commandList.push(...renderList.tileCommands)
217
-
218
- this.executeTileIdCommands(frameState)
219
-
220
- //释放过期瓦片
221
- //优化:使用更高效的内存缓存管理策略
222
- const expiredTiles = []
223
- for (const cacheTile of this._cacheTiles) {
224
- if (cacheTile.lastVisitTime < frameState.frameNumber) {
225
- if (!cacheTile.expired) expiredTiles.push(cacheTile)
226
- }
227
- }
228
- expiredTiles.sort((a, b) => a.lastVisitTime - b.lastVisitTime)
229
- if (expiredTiles.length > 100) {
230
- for (const expiredTile of expiredTiles) {
231
- expiredTile.unload()
232
- expiredTile.expired = true
233
- if (expiredTiles.length <= 50) break
234
- }
235
- }
236
- }
237
-
238
- destroy() {
239
- const scene = this.scene
240
- const rootTiles = this._rootTiles
241
- this.scene = null
242
- if (scene && scene.primitives.contains(this)) {
243
- scene.primitives.remove(this)
244
- }
245
-
246
- if (rootTiles) {
247
- for (const tile of rootTiles) {
248
- tile.destroy()
249
- }
250
- rootTiles.length = 0
251
- this._rootTiles = null
252
- }
253
- if (this._cacheTiles) {
254
- this._cacheTiles.length = 0
255
- this._cacheTiles = null
256
- }
257
-
258
- if (this.sources) {
259
- for (const key in this.sources) {
260
- if (Object.hasOwnProperty.call(this.sources, key)) {
261
- const source = this.sources[key];
262
- source.destroy()
263
- }
264
- }
265
- this.sources = null
266
- }
267
- this._styleLayers = null
268
-
269
- if (this._renderList) {
270
- this._renderList.destroy()
271
- this._renderList = null
272
- }
273
-
274
- if (this._tilesToUpdate) {
275
- this._tilesToUpdate.length = 0
276
- this._tilesToUpdate = null
277
- }
278
-
279
- if (this._tilesToRender) {
280
- this._tilesToRender.length = 0
281
- this._tilesToRender = null
282
- }
283
-
284
- if (this._tileIdFbo) {
285
- this._tileIdFbo.destroy()
286
- this.tileIdTexture = null
287
- this._tileIdFbo = null
288
- this._idClearCommand = null
289
- }
290
-
291
- this._styleJson = null
292
- }
293
-
294
- isDestroyed() {
295
- return false
296
- }
297
- }
298
-
299
- /**
300
- * 遍历LOD四叉树,获取所有可见瓦片,取离相机最近的一个瓦片的 z 作为全局缩放参数 zoom
301
- * @param {Cesium.FrameState} frameState
302
- * @param {VectorTileset} tileset
303
- * @returns
304
- */
305
- function getTilesToUpdate(frameState, tileset) {
306
- const queue = [
307
- ...tileset._rootTiles
308
- ]
309
- const tilesToUpdate = tileset._tilesToUpdate
310
- let zoom = 24, nearDist = Infinity
311
- const visitor = {
312
- //当see大于阈值,继续查找子级瓦片
313
- visitChildren(tile) {
314
- if (tile.z >= tileset.maximumLevel) {
315
- if (tile.distanceToCamera < nearDist) {
316
- nearDist = tile.distanceToCamera
317
- zoom = tile.z
318
- }
319
- return tilesToUpdate.push(tile)
320
- }
321
-
322
- if (tile.children.length == 0) {
323
- tile.createChildren()
324
- for (const child of tile.children) {
325
- tileset._cacheTiles.push(child)
326
- }
327
- }
328
- for (const child of tile.children) {
329
- queue.push(child)
330
- }
331
- },
332
- //否则使用当前瓦片填充视口
333
- accept(tile) {
334
- if (tile.distanceToCamera < nearDist) {
335
- nearDist = tile.distanceToCamera
336
- zoom = tile.z
337
- }
338
- tilesToUpdate.push(tile)
339
- }
340
- }
341
-
342
- tilesToUpdate.length = 0
343
-
344
- do {
345
- const tile = queue.shift()
346
- tile.visit(frameState, visitor)
347
- } while (queue.length > 0);
348
-
349
- tileset.zoom = zoom
350
-
351
- return tilesToUpdate
352
- }
353
-
354
- /**
355
- * 获取可渲染瓦片
356
- * @param {VectorTileLOD[]} tilesToUpdate
357
- * @param {VectorTileLOD[]} tilesToRender
358
- * @returns
359
- */
360
- function getTilesToRender(tilesToUpdate, tilesToRender) {
361
- const cache = new Map()
362
- for (const newTile of tilesToUpdate) {
363
- if (newTile.renderable) {
364
- cache.set(newTile, true)
365
- }
366
- }
367
-
368
- //在当前可渲染瓦片队列中,找出上一帧所有可渲染瓦片的后代节点瓦片,只有当后代节点瓦片都可渲染才被替代
369
- const descendantsList = []
370
- for (let i = 0; i < tilesToRender.length; i++) {
371
- const oldTile = tilesToRender[i];
372
-
373
- oldTile.renderable = cache.has(oldTile)
374
- if (oldTile.renderable) continue//前后两帧都可见,不需要特殊处理
375
-
376
- const descendants = {
377
- tiles: [],
378
- total: 0,
379
- renderable: 0
380
- }
381
- descendantsList[i] = descendants
382
-
383
- for (const newTile of tilesToUpdate) {
384
- const dz = newTile.z - oldTile.z
385
- if (dz === 0) {
386
- continue
387
- }
388
- else if (dz > 0) {//针对需要后代瓦片替换祖先瓦片的情况:先记录所有可见的后代瓦片,并统计可渲染后代瓦片数量
389
- const scale = Math.pow(2, dz),
390
- newAncestorX = Math.floor(newTile.x / scale),
391
- newAncestorY = Math.floor(newTile.y / scale)
392
-
393
- if (newAncestorX === oldTile.x && newAncestorY === oldTile.y) {
394
- descendants.total++
395
- descendants.tiles.push(newTile)
396
- if (newTile.renderable) descendants.renderable++
397
- }
398
- }
399
- else {//针对需要祖先瓦片覆盖后代瓦片的情况:祖先瓦片可渲染则显示,否则继续显示后代瓦片
400
- const scale = Math.pow(2, -dz),
401
- oldAncestorX = Math.floor(oldTile.x / scale),
402
- oldAncestorY = Math.floor(oldTile.y / scale)
403
-
404
- if (oldAncestorX === newTile.x && oldAncestorY === newTile.y) {
405
- oldTile.renderable = !newTile.renderable
406
- }
407
- }
408
- }
409
- }
410
-
411
- //针对后代瓦片替换祖先瓦片的情况:只有所有可见的后代瓦片都可渲染,才显示后代瓦片,否则继续显示祖先瓦片
412
- for (let i = 0; i < tilesToRender.length; i++) {
413
- const oldTile = tilesToRender[i];
414
- const descendants = descendantsList[i]
415
- if (descendants && descendants.total) {
416
- const descendantsRenderable = descendants.total === descendants.renderable
417
- oldTile.renderable = !descendantsRenderable
418
- for (const descendantTile of descendants.tiles) {
419
- descendantTile.renderable = descendantsRenderable
420
- }
421
- }
422
- }
423
-
424
- //从 tilesToUpdate 和 tilesToRender 中筛选最终可渲染的瓦片
425
-
426
- cache.clear()
427
-
428
- let length = tilesToRender.length
429
- for (let i = 0; i < length; i++) {
430
- const tileToRender = tilesToRender.shift()
431
- if (tileToRender.renderable) {
432
- tilesToRender.push(tileToRender)
433
- cache.set(tileToRender, true)
434
- }
435
- }
436
-
437
- length = tilesToUpdate.length
438
- for (let i = 0; i < length; i++) {
439
- const tileToUpdate = tilesToUpdate[i]
440
- if (tileToUpdate.renderable && !cache.has(tileToUpdate)) {
441
- tilesToRender.push(tileToUpdate)
442
- cache.set(tileToUpdate, true)
443
- }
444
- }
445
-
446
- return tilesToRender
447
- }
1
+ import { VectorTileLOD } from './VectorTileLOD'
2
+ import { StyleLayer } from './style/StyleLayer'
3
+ import './layers/index'
4
+ import { VectorTileRenderList } from './VectorTileRenderList'
5
+ import { Sources } from './sources'
6
+ import { ISource } from './sources/ISource'
7
+ import { warnOnce } from 'maplibre-gl/src/util/util'
8
+ import { SymbolPlacements } from './symbol/SymbolPlacements'
9
+
10
+ export class VectorTileset {
11
+ /**
12
+ * @param {object} options
13
+ * @param {string|import('@maplibre/maplibre-gl-style-spec').StyleSpecification} options.style
14
+ * @param {boolean} [options.showTileColor=false]
15
+ * @param {string} [options.workerUrl] - Web Worker 脚本 URL,用于瓦片解析/几何计算;不传则走主线程
16
+ * @param {number} [options.maximumActiveTasks=4] - 同时进行的 Worker 任务数,与 maxLoading 配合
17
+ */
18
+ constructor(options) {
19
+ this.maximumLevel = 24
20
+ this.show = true
21
+ this.showTileColor = !!options.showTileColor
22
+ this.ready = false
23
+ this.tilingScheme = new Cesium.WebMercatorTilingScheme()
24
+
25
+ this.readyEvent = new Cesium.Event()
26
+ this.errorEvent = new Cesium.Event()
27
+
28
+ this._styleJson = null
29
+ this._style = options.style
30
+ this._rootTiles = []
31
+ this._cacheTiles = []
32
+ this._tilesToUpdate = []
33
+ this._tilesToRender = []
34
+ /**@type {StyleLayer[]} */
35
+ this._styleLayers = []
36
+ /**@type {VectorTileRenderList} */
37
+ this._renderList = new VectorTileRenderList(this._styleLayers)
38
+ this.numLoading = 0
39
+ this.maxLoading = 6
40
+ this.numInitializing = 0
41
+ this.maxInitializing = 6
42
+ /**@type {Cesium.TaskProcessor|null} */
43
+ this._taskProcessor = null
44
+ this._workerUrl = options.workerUrl || null
45
+ this._maximumActiveTasks = options.maximumActiveTasks ?? 4
46
+ /**@type {Cesium.Texture} */
47
+ this.tileIdTexture = null
48
+ this.zoom = 0
49
+ /**
50
+ * 负责符号碰撞检测(自动避让),SymbolPlacements 内部基于 maplibre-gl GridIndex 实现
51
+ */
52
+ this._symbolPlacements = new SymbolPlacements()
53
+
54
+ requestAnimationFrame(() => {
55
+ this.init()
56
+ })
57
+ }
58
+
59
+ async init() {
60
+ let style = this._style
61
+ if (!style) {
62
+ this.errorEvent.raiseEvent(new Error('请传入 style 参数'))
63
+ return
64
+ }
65
+
66
+ this.path = ''
67
+ if (typeof style == 'string') {
68
+ this.path = style.split('/').slice(0, -1).join('/')
69
+ if (this.path) this.path += '/'
70
+ style = await Cesium.Resource.fetchJson(style)
71
+ }
72
+
73
+ //初始化数据源
74
+
75
+ /** @type {{[sourceId:string]:ISource}}*/
76
+ this.sources = {}
77
+ for (const sourceId in style.sources) {
78
+ /**@type {import('@maplibre/maplibre-gl-style-spec').SourceSpecification} */
79
+ const sourceParams = style.sources[sourceId]
80
+ const SourceCls = Sources[sourceParams.type]
81
+ if (SourceCls) {
82
+ this.sources[sourceId] = new SourceCls(sourceParams, this.path)
83
+ try {
84
+ await this.sources[sourceId].init()
85
+ this.maximumLevel = Math.min(
86
+ sourceParams.maxzoom || 24,
87
+ this.maximumLevel
88
+ )
89
+ } catch (err) {
90
+ this.errorEvent.raiseEvent(err)
91
+ }
92
+ }
93
+ }
94
+
95
+ //初始化样式图层
96
+ for (let i = 0; i < style.layers.length; i++) {
97
+ this._styleLayers[i] = new StyleLayer(style.layers[i])
98
+ }
99
+
100
+ //创建顶级瓦片LOD
101
+ const numX = this.tilingScheme.getNumberOfXTilesAtLevel(0)
102
+ const numY = this.tilingScheme.getNumberOfYTilesAtLevel(0)
103
+ let i = 0
104
+ for (let y = 0; y < numY; y++) {
105
+ for (let x = 0; x < numX; x++) {
106
+ var tile = new VectorTileLOD({
107
+ parent: this,
108
+ x,
109
+ y,
110
+ z: 0,
111
+ tilingScheme: this.tilingScheme
112
+ })
113
+ tile.createChildren()
114
+ this._rootTiles[i++] = tile
115
+ }
116
+ }
117
+
118
+ //初始化渲染队列
119
+ this._renderList.init()
120
+
121
+ // Web Worker:有 workerUrl 时创建 TaskProcessor,供瓦片解析/几何计算使用
122
+ if (this._workerUrl && typeof Cesium.TaskProcessor !== 'undefined') {
123
+ this._taskProcessor = new Cesium.TaskProcessor(
124
+ this._workerUrl,
125
+ Math.min(this._maximumActiveTasks, this.maxInitializing)
126
+ )
127
+ }
128
+
129
+ this._styleJson = style
130
+ this.ready = true
131
+ this.readyEvent.raiseEvent(this)
132
+ }
133
+
134
+ //更新瓦片id纹理,用于裁剪超出瓦片边界的像素
135
+ executeTileIdCommands(frameState) {
136
+ const tileIdCommands = this._renderList.tileIdCommands
137
+
138
+ if (tileIdCommands.length > 0) {
139
+ const context = frameState.context
140
+ /**@type {Cesium.FrameBuffer} */
141
+ let tileIdFbo = this._tileIdFbo
142
+ if (!tileIdFbo) {
143
+ tileIdFbo = new Cesium.FramebufferManager({
144
+ depthStencil: true,
145
+ supportsDepthTexture: true
146
+ })
147
+ this._tileIdFbo = tileIdFbo
148
+ this._idClearCommand = new Cesium.ClearCommand({
149
+ color: new Cesium.Color(0.0, 0.0, 0.0, 0.0),
150
+ depth: 1.0,
151
+ stencil: 0.0
152
+ })
153
+ }
154
+ const pixelDatatype = context.floatingPointTexture
155
+ ? Cesium.PixelDatatype.FLOAT
156
+ : Cesium.PixelDatatype.UNSIGNED_BYTE
157
+ const width = context.drawingBufferWidth
158
+ const height = context.drawingBufferHeight
159
+ tileIdFbo.update(context, width, height, 1, pixelDatatype)
160
+ tileIdFbo.clear(context, this._idClearCommand)
161
+
162
+ const framebuffer = tileIdFbo.framebuffer
163
+ for (const tileIdCommand of tileIdCommands) {
164
+ tileIdCommand.framebuffer = framebuffer
165
+ tileIdCommand.execute(context)
166
+ }
167
+
168
+ this.tileIdTexture = tileIdFbo.getColorTexture(0)
169
+ }
170
+ }
171
+
172
+ update(frameState) {
173
+ if (!this.ready || !this.show) return
174
+
175
+ if (frameState.context.webgl2) {
176
+ warnOnce('webgl2模式下贴地线面的支持将导致性能下降')
177
+ }
178
+
179
+ const renderList = this._renderList
180
+ //清空渲染队列
181
+ renderList.beginFrame()
182
+
183
+ this.numInitializing = 0
184
+
185
+ /**@type {Cesium.Globe} */
186
+ const scene = frameState.camera._scene
187
+ const globe = scene.globe
188
+ const globeSuspendLodUpdate = globe._surface._debug.suspendLodUpdate
189
+ this.scene = scene
190
+
191
+ // 获取可见瓦片
192
+ // 优化:采用更高效的LOD调度算法,获取当前帧实际可渲染到屏幕的瓦片,避免出现瓦片层级切换时候出现闪烁
193
+
194
+ /**@type {VectorTileLOD[]} */
195
+ const tilesToUpdate = getTilesToUpdate(frameState, this)
196
+ // const tilesToUpdate = globeSuspendLodUpdate ? this._tilesToUpdate : getTilesToUpdate(frameState, this)
197
+
198
+ //瓦片排序,决定瓦片加载瓦片数据、初始化的优先级
199
+ //优化:采用更精细、高效的优先级策略
200
+ if (!globeSuspendLodUpdate) {
201
+ tilesToUpdate.sort((a, b) => a.distanceToCamera - b.distanceToCamera)
202
+ }
203
+
204
+ //更新瓦片状态:请求瓦片数据,创建渲染图层,初始化等
205
+ for (const tile of tilesToUpdate) {
206
+ tile.lastVisitTime = frameState.frameNumber
207
+ tile.expired = false
208
+ tile.update(frameState, renderList, this)
209
+ }
210
+
211
+ /**@type {VectorTileLOD[]} */
212
+ const tilesToRender = globeSuspendLodUpdate
213
+ ? this._tilesToRender
214
+ : getTilesToRender(tilesToUpdate, this._tilesToRender)
215
+ if (!globeSuspendLodUpdate) {
216
+ tilesToRender.sort((a, b) => a.distanceToCamera - b.distanceToCamera)
217
+ }
218
+ //渲染瓦片内容
219
+ for (const tile of tilesToRender) {
220
+ tile.lastVisitTime = frameState.frameNumber
221
+ tile.expired = false
222
+ tile.render(frameState, renderList, this)
223
+ }
224
+
225
+ //渲染图层分组、排序
226
+ const orderedRenderLayers = renderList.getList()
227
+ //符号碰撞检测
228
+ this._symbolPlacements.update(frameState, orderedRenderLayers, this.zoom)
229
+ //获取渲染命令(DrawCommand),渲染图层内部可以使用Primitive、PolylineCollection、LabelCollection、BillboardCollection等API,
230
+ //也可以自定义DrawCommand
231
+ for (const renderLayer of orderedRenderLayers) {
232
+ renderLayer.render(frameState, this)
233
+ }
234
+ for (const visualizer of renderList.visualizers) {
235
+ visualizer.render(frameState, this)
236
+ }
237
+ //瓦片颜色、深度
238
+ frameState.commandList.push(...renderList.tileCommands)
239
+
240
+ this.executeTileIdCommands(frameState)
241
+
242
+ //释放过期瓦片
243
+ //优化:使用更高效的内存缓存管理策略
244
+ const expiredTiles = []
245
+ for (const cacheTile of this._cacheTiles) {
246
+ if (cacheTile.lastVisitTime < frameState.frameNumber) {
247
+ if (!cacheTile.expired) expiredTiles.push(cacheTile)
248
+ }
249
+ }
250
+ expiredTiles.sort((a, b) => a.lastVisitTime - b.lastVisitTime)
251
+ if (expiredTiles.length > 100) {
252
+ for (const expiredTile of expiredTiles) {
253
+ expiredTile.unload()
254
+ expiredTile.expired = true
255
+ if (expiredTiles.length <= 50) break
256
+ }
257
+ }
258
+ }
259
+
260
+ destroy() {
261
+ const scene = this.scene
262
+ const rootTiles = this._rootTiles
263
+ this.scene = null
264
+ if (scene && scene.primitives.contains(this)) {
265
+ scene.primitives.remove(this)
266
+ }
267
+
268
+ if (rootTiles) {
269
+ for (const tile of rootTiles) {
270
+ tile.destroy()
271
+ }
272
+ rootTiles.length = 0
273
+ this._rootTiles = null
274
+ }
275
+ if (this._cacheTiles) {
276
+ this._cacheTiles.length = 0
277
+ this._cacheTiles = null
278
+ }
279
+
280
+ if (this.sources) {
281
+ for (const key in this.sources) {
282
+ if (Object.hasOwnProperty.call(this.sources, key)) {
283
+ const source = this.sources[key]
284
+ source.destroy()
285
+ }
286
+ }
287
+ this.sources = null
288
+ }
289
+ this._styleLayers = null
290
+
291
+ if (this._renderList) {
292
+ this._renderList.destroy()
293
+ this._renderList = null
294
+ }
295
+
296
+ if (this._taskProcessor && !this._taskProcessor.isDestroyed()) {
297
+ this._taskProcessor.destroy()
298
+ this._taskProcessor = null
299
+ }
300
+
301
+ if (this._tilesToUpdate) {
302
+ this._tilesToUpdate.length = 0
303
+ this._tilesToUpdate = null
304
+ }
305
+
306
+ if (this._tilesToRender) {
307
+ this._tilesToRender.length = 0
308
+ this._tilesToRender = null
309
+ }
310
+
311
+ if (this._tileIdFbo) {
312
+ this._tileIdFbo.destroy()
313
+ this.tileIdTexture = null
314
+ this._tileIdFbo = null
315
+ this._idClearCommand = null
316
+ }
317
+
318
+ this._styleJson = null
319
+ }
320
+
321
+ isDestroyed() {
322
+ return false
323
+ }
324
+ }
325
+
326
+ /**
327
+ * 遍历LOD四叉树,获取所有可见瓦片,取离相机最近的一个瓦片的 z 作为全局缩放参数 zoom
328
+ * @param {Cesium.FrameState} frameState
329
+ * @param {VectorTileset} tileset
330
+ * @returns
331
+ */
332
+ function getTilesToUpdate(frameState, tileset) {
333
+ const queue = [...tileset._rootTiles]
334
+ const tilesToUpdate = tileset._tilesToUpdate
335
+ let zoom = 24,
336
+ nearDist = Infinity
337
+ const visitor = {
338
+ //当see大于阈值,继续查找子级瓦片
339
+ visitChildren(tile) {
340
+ if (tile.z >= tileset.maximumLevel) {
341
+ if (tile.distanceToCamera < nearDist) {
342
+ nearDist = tile.distanceToCamera
343
+ zoom = tile.z
344
+ }
345
+ return tilesToUpdate.push(tile)
346
+ }
347
+
348
+ if (tile.children.length == 0) {
349
+ tile.createChildren()
350
+ for (const child of tile.children) {
351
+ tileset._cacheTiles.push(child)
352
+ }
353
+ }
354
+ for (const child of tile.children) {
355
+ queue.push(child)
356
+ }
357
+ },
358
+ //否则使用当前瓦片填充视口
359
+ accept(tile) {
360
+ if (tile.distanceToCamera < nearDist) {
361
+ nearDist = tile.distanceToCamera
362
+ zoom = tile.z
363
+ }
364
+ tilesToUpdate.push(tile)
365
+ }
366
+ }
367
+
368
+ tilesToUpdate.length = 0
369
+
370
+ do {
371
+ const tile = queue.shift()
372
+ tile.visit(frameState, visitor)
373
+ } while (queue.length > 0)
374
+
375
+ tileset.zoom = zoom
376
+
377
+ return tilesToUpdate
378
+ }
379
+
380
+ /**
381
+ * 获取可渲染瓦片
382
+ * @param {VectorTileLOD[]} tilesToUpdate
383
+ * @param {VectorTileLOD[]} tilesToRender
384
+ * @returns
385
+ */
386
+ function getTilesToRender(tilesToUpdate, tilesToRender) {
387
+ const cache = new Map()
388
+ for (const newTile of tilesToUpdate) {
389
+ if (newTile.renderable) {
390
+ cache.set(newTile, true)
391
+ }
392
+ }
393
+
394
+ //在当前可渲染瓦片队列中,找出上一帧所有可渲染瓦片的后代节点瓦片,只有当后代节点瓦片都可渲染才被替代
395
+ const descendantsList = []
396
+ for (let i = 0; i < tilesToRender.length; i++) {
397
+ const oldTile = tilesToRender[i]
398
+
399
+ oldTile.renderable = cache.has(oldTile)
400
+ if (oldTile.renderable) continue //前后两帧都可见,不需要特殊处理
401
+
402
+ const descendants = {
403
+ tiles: [],
404
+ total: 0,
405
+ renderable: 0
406
+ }
407
+ descendantsList[i] = descendants
408
+
409
+ for (const newTile of tilesToUpdate) {
410
+ const dz = newTile.z - oldTile.z
411
+ if (dz === 0) {
412
+ continue
413
+ } else if (dz > 0) {
414
+ //针对需要后代瓦片替换祖先瓦片的情况:先记录所有可见的后代瓦片,并统计可渲染后代瓦片数量
415
+ const scale = Math.pow(2, dz),
416
+ newAncestorX = Math.floor(newTile.x / scale),
417
+ newAncestorY = Math.floor(newTile.y / scale)
418
+
419
+ if (newAncestorX === oldTile.x && newAncestorY === oldTile.y) {
420
+ descendants.total++
421
+ descendants.tiles.push(newTile)
422
+ if (newTile.renderable) descendants.renderable++
423
+ }
424
+ } else {
425
+ //针对需要祖先瓦片覆盖后代瓦片的情况:祖先瓦片可渲染则显示,否则继续显示后代瓦片
426
+ const scale = Math.pow(2, -dz),
427
+ oldAncestorX = Math.floor(oldTile.x / scale),
428
+ oldAncestorY = Math.floor(oldTile.y / scale)
429
+
430
+ if (oldAncestorX === newTile.x && oldAncestorY === newTile.y) {
431
+ oldTile.renderable = !newTile.renderable
432
+ }
433
+ }
434
+ }
435
+ }
436
+
437
+ //针对后代瓦片替换祖先瓦片的情况:只有所有可见的后代瓦片都可渲染,才显示后代瓦片,否则继续显示祖先瓦片
438
+ for (let i = 0; i < tilesToRender.length; i++) {
439
+ const oldTile = tilesToRender[i]
440
+ const descendants = descendantsList[i]
441
+ if (descendants && descendants.total) {
442
+ const descendantsRenderable = descendants.total === descendants.renderable
443
+ oldTile.renderable = !descendantsRenderable
444
+ for (const descendantTile of descendants.tiles) {
445
+ descendantTile.renderable = descendantsRenderable
446
+ }
447
+ }
448
+ }
449
+
450
+ //从 tilesToUpdate 和 tilesToRender 中筛选最终可渲染的瓦片
451
+
452
+ cache.clear()
453
+
454
+ let length = tilesToRender.length
455
+ for (let i = 0; i < length; i++) {
456
+ const tileToRender = tilesToRender.shift()
457
+ if (tileToRender.renderable) {
458
+ tilesToRender.push(tileToRender)
459
+ cache.set(tileToRender, true)
460
+ }
461
+ }
462
+
463
+ length = tilesToUpdate.length
464
+ for (let i = 0; i < length; i++) {
465
+ const tileToUpdate = tilesToUpdate[i]
466
+ if (tileToUpdate.renderable && !cache.has(tileToUpdate)) {
467
+ tilesToRender.push(tileToUpdate)
468
+ cache.set(tileToUpdate, true)
469
+ }
470
+ }
471
+
472
+ return tilesToRender
473
+ }