@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,532 +1,720 @@
1
- import { VectorTileset } from "./VectorTileset";
2
- import * as MVT from '@mapbox/vector-tile'
3
- import { IRenderLayer, ILayerVisualizer, RenderLayers, LayerVisualizers } from "./layers";
4
- import { VectorTileRenderList } from "./VectorTileRenderList";
5
- import { ISource } from "./sources/ISource";
6
- import { EXTENT } from "maplibre-gl/src/data/extent";
7
- import Point from "@mapbox/point-geometry";
8
- import { subdivideVertexLine } from "maplibre-gl/src/render/subdivision";
9
- import { granularitySettings } from "./sources/granularitySettings";
10
- import { warnOnce } from "maplibre-gl/src/util/util";
11
-
12
- let tileDepthRenderSate = null
13
- let nextTileKey = 0
14
- let levelZeroMaximumGeometricError = null
15
- /**
16
- * 计算指定LOD层级的最大几何误差,相当于动态计算3DTiles中geometricError,只是形瓦片的几何误差是一个层级的所有瓦片都相同(rectangle大小都一样)
17
- * @param {number} z 瓦片层级(整数,不是地图的缩放级别)
18
- * @param {Cesium.TilingScheme} tilingScheme
19
- * @returns
20
- */
21
- function getLevelMaximumGeometricError(z, tilingScheme) {
22
- if (levelZeroMaximumGeometricError === null) {
23
- levelZeroMaximumGeometricError = Cesium.TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap(
24
- tilingScheme.ellipsoid,
25
- 128,
26
- tilingScheme.getNumberOfXTilesAtLevel(0)
27
- );
28
- }
29
- return levelZeroMaximumGeometricError / (1 << z);
30
- }
31
-
32
- /**
33
- * LOD调度重要参数SSE的计算函数,从Cesium地形调度模块中提取出来的代码,调度逻辑和地形瓦片的一致
34
- * @param {Cesium.FrameState} frameState
35
- * @param {VectorTileLOD} tile
36
- * @returns
37
- */
38
- function screenSpaceError(frameState, tile) {
39
- const maxGeometricError = getLevelMaximumGeometricError(
40
- tile.z, tile.tilingScheme
41
- );
42
-
43
- const distance2 = tile.distanceToCamera;
44
- const height = frameState.context.drawingBufferHeight;
45
- const sseDenominator = frameState.camera.frustum.sseDenominator;
46
- let error = maxGeometricError * height / (distance2 * sseDenominator);
47
- if (frameState.fog.enabled) {
48
- error -= Cesium.Math.fog(distance2, frameState.fog.density) * frameState.fog.sse;
49
- }
50
- error /= frameState.pixelRatio;
51
- return error;
52
- }
53
-
54
- function initConstants() {
55
- if (tileDepthRenderSate !== null) return
56
-
57
- tileDepthRenderSate = Cesium.RenderState.fromCache({
58
- id: 'vt_tile-depth',
59
- blending: Cesium.BlendingState.DISABLED,
60
- depthTest: {
61
- enabled: true
62
- },
63
- depthMask: true,
64
- cull: {
65
- enabled: true
66
- },
67
- stencilMask: Cesium.StencilConstants.CESIUM_3D_TILE_MASK,
68
- stencilTest: {
69
- backFunction: 519,
70
- backOperation: { fail: 7680, zFail: 7680, zPass: 7681 },
71
- enabled: true,
72
- frontFunction: 519,
73
- frontOperation: { fail: 7680, zFail: 7680, zPass: 7681 },
74
- mask: 128,
75
- reference: 128
76
- },
77
- colorMask: {
78
- red: false,
79
- green: false,
80
- blue: false,
81
- alpha: false
82
- }
83
- })
84
- }
85
-
86
- export class VectorTileLOD {
87
- constructor(options) {
88
- initConstants()
89
-
90
- this.x = options.x
91
- this.y = options.y
92
- this.z = options.z
93
- /**暂时用不到,但是还是记录父级瓦片 */
94
- this.parent = options.parent
95
- this.children = []
96
-
97
- /**@type {Cesium.TilingScheme} */
98
- this.tilingScheme = options.tilingScheme
99
- this.rectangle = this.tilingScheme.tileXYToRectangle(this.x, this.y, this.z)
100
- /**
101
- * Cesium.TileBoundingRegion 不是公开的API,但是可以从Cesium地形调度相关模块源码学习,
102
- */
103
- this.tileBoundingRegion = new Cesium.TileBoundingRegion({
104
- rectangle: this.rectangle,
105
- minimumHeight: 0,
106
- maximumHeight: 0,
107
- ellipsoid: this.tilingScheme.ellipsoid,
108
- computeBoundingVolumes: true
109
- })
110
-
111
- /**@type {IRenderLayer[]} */
112
- this.layers = []
113
- /**@type {ILayerVisualizer[]} */
114
- this.visualizers = []
115
- /**
116
- * 保存pbf/mvt文件解析结果,准备创建图层(要素过滤)时候进一步读取要素,创建图层渲染对象时读取要素几何数据
117
- * @type {Record<string,MVT.VectorTile>}
118
- */
119
- this.sources = {}
120
-
121
- this.tileId = null
122
- /**
123
- * 记录最近一次访问时间(用渲染帧数表示),用于筛选过期瓦片
124
- */
125
- this.lastVisitTime = 0
126
- /**
127
- * 瓦片调度状态
128
- * @type {'none'|'loading'|'loaded'|'ready'|'error'}
129
- */
130
- this.state = 'none'
131
- this.renderable = false
132
-
133
- this.tileId = {
134
- x: this.x, y: this.y, z: this.z,
135
- key: nextTileKey++,
136
- color: Cesium.Color.fromRgba(nextTileKey - 1),
137
- tileColor: Cesium.Color.fromRandom({
138
- alpha: 1
139
- })
140
- }
141
-
142
- const size = EXTENT * Math.pow(2, this.z),
143
- x0 = EXTENT * this.x,
144
- y0 = EXTENT * this.y;
145
- this.transformPoint = function (x, y, lonlat) {
146
- lonlat[0] = (x + x0) * 360 / size - 180
147
- lonlat[1] = 360 / Math.PI * Math.atan(Math.exp((1 - (y + y0) * 2 / size) * Math.PI)) - 90
148
- return lonlat //[x, y]
149
- }
150
- }
151
-
152
- createChildren() {
153
- var tiles = [{
154
- x: this.x * 2,
155
- y: this.y * 2 + 1,
156
- z: this.z + 1
157
- }, {
158
- x: this.x * 2 + 1,
159
- y: this.y * 2 + 1,
160
- z: this.z + 1,
161
- }, {
162
- x: this.x * 2,
163
- y: this.y * 2,
164
- z: this.z + 1,
165
- }, {
166
- x: this.x * 2 + 1,
167
- y: this.y * 2,
168
- z: this.z + 1,
169
- }]
170
-
171
- for (const { x, y, z } of tiles) {
172
- var child = new VectorTileLOD({
173
- x, y, z,
174
- tilingScheme: this.tilingScheme,
175
- parent: this
176
- })
177
- this.children.push(child)
178
- }
179
- }
180
-
181
- /**
182
- * @param {Cesium.FrameState} frameState
183
- * @param {{visitChildren(child:VectorTileLOD):void;accept(tile:VectorTileLOD):void}} visitor
184
- * @returns
185
- */
186
- visit(frameState, visitor) {
187
- const tileBoundingRegion = this.tileBoundingRegion
188
-
189
- //相机视锥剔除
190
- this.distanceToCamera = tileBoundingRegion.distanceToCamera(frameState)
191
- this.visibility = frameState.cullingVolume.computeVisibility(tileBoundingRegion)
192
- if (this.visibility == Cesium.Intersect.OUTSIDE) {
193
- return
194
- }
195
-
196
- //性能优化:还可以进行地平线剔除更多被地球完全遮挡的瓦片
197
-
198
- const maxSSE = frameState.maximumScreenSpaceError
199
- const sse = screenSpaceError(frameState, this)
200
- if (sse >= maxSSE) {//继续遍历子级瓦片
201
- visitor.visitChildren(this)
202
- }
203
- else {//使用当前瓦片渲染
204
- visitor.accept(this)
205
- }
206
- }
207
-
208
- /**
209
- * @param {VectorTileset} tileset
210
- */
211
- async getSources(tileset) {
212
- /**@type {Record<string,ISource>} */
213
- const sourcesToLoad = {}
214
- const style = tileset._styleJson
215
-
216
- for (const styleLayer of style.layers) {
217
- const sourceId = styleLayer.source
218
- const source = tileset.sources[sourceId]
219
- if (source && !sourcesToLoad[sourceId]) {
220
- sourcesToLoad[sourceId] = source
221
- }
222
- }
223
-
224
- for (const sourceId in sourcesToLoad) {
225
- const source = sourcesToLoad[sourceId]
226
- try {
227
- const tileData = await source.requestTile(this.x, this.y, this.z, tileset)
228
- if (tileData) {
229
- this.sources[sourceId] = tileData
230
- }
231
- } catch (err) { }
232
- }
233
-
234
- tileset.numLoading--
235
- this.state = 'loaded'
236
- }
237
-
238
- /**
239
- * @param {Cesium.FrameRateMonitor} frameState
240
- * @param {VectorTileset} tileset
241
- */
242
- async createRenderLayers(frameState, tileset) {
243
- const sources = this.sources
244
- const styleLayers = tileset._styleLayers
245
- const renderLayers = this.layers
246
- const visualizers = this.visualizers
247
- /**@type {Record<string,ILayerVisualizer>} */
248
- const visualizerMap = {}
249
-
250
- for (const styleLayer of styleLayers) {
251
- const sourceVectorTile = sources[styleLayer.source]
252
- /**
253
- * 按图层类型获取对应的渲染图层类
254
- * @type {typeof IRenderLayer}
255
- */
256
- const RenderLayer = RenderLayers[styleLayer.type]
257
- const LayerVisualizer = LayerVisualizers[styleLayer.type]
258
- const isBackgroundLayer = styleLayer.type === 'background'
259
- if (!RenderLayer) {
260
- warnOnce('不支持图层类型' + styleLayer.type)
261
- }
262
- if ((!isBackgroundLayer && !sourceVectorTile) || !RenderLayer) continue
263
-
264
- const features = []
265
- if (!isBackgroundLayer) {
266
- const sourceType = styleLayer.source && tileset.sources[styleLayer.source].type
267
- const sourceLayer = sourceType == 'geojson' ? '_geojsonTileLayer' : styleLayer.sourceLayer
268
- const vectorTileLayer = sourceVectorTile.layers[sourceLayer]
269
- if (!vectorTileLayer) continue
270
-
271
- //读取要素,并根据图层样式filter表达式进行过滤
272
- //性能优化:同一帧读取和过滤大量要素,耗时较长,应该将其移到Web Worker
273
- const featureCount = vectorTileLayer.length
274
- for (let i = 0; i < featureCount; i++) {
275
- //读取要素
276
- const feature = vectorTileLayer.feature(i)
277
- //过滤要素
278
- if (styleLayer.filter && !styleLayer.filter.filter({ zoom: this.z }, feature)) {
279
- continue
280
- }
281
- //MLT的Feature类没有实现toGeoJSON方法,直接使用MVT的方法
282
- if (!feature.toGeoJSON) {
283
- feature.toGeoJSON = MVT.VectorTileFeature.prototype.toGeoJSON
284
- }
285
- features.push(feature)
286
- }
287
- if (!features.length) continue
288
- }
289
-
290
- //创建渲染图层
291
- const renderLayer = new RenderLayer(features, styleLayer, this)
292
- renderLayers.push(renderLayer)
293
-
294
- //将渲染图层分配到对应类型的图层渲染器,图层渲染器应实现如下功能,以提升渲染性能:
295
- //1、合批创建几何体、批次表和 DrawCommand;
296
- //2、克隆合批DrwaCommand,创建副本,通过offset和count指定图层的绘制范围。
297
- if (LayerVisualizer) {
298
- let visualizer = visualizerMap[styleLayer.type]
299
- if (!visualizer) {
300
- visualizer = new LayerVisualizer(this)
301
- visualizerMap[styleLayer.type] = visualizer
302
- visualizers.push(visualizer)
303
- }
304
- visualizer.addLayer(features, renderLayer, frameState, tileset)
305
- }
306
- }
307
-
308
- this.state = 'ready'
309
- }
310
-
311
- /**
312
- * @param {Cesium.FrameState} frameState
313
- * @param {VectorTileRenderList} renderList
314
- * @param {VectorTileset} tileset
315
- */
316
- update(frameState, renderList, tileset) {
317
- // 这里构建的 primitive 有如下用途:
318
- // 1. showTileColor 设置为 true 时,展示瓦片范围,验证LOD调度效果
319
- // 2. 生成绘制瓦片 id 的 DrawCommand,在 VectorTileset 中实时更新瓦片id纹理,实现瓦片边界精准裁剪
320
-
321
- if (!this.primitive) {
322
-
323
- this.primitive = new Cesium.Primitive({
324
- geometryInstances: new Cesium.GeometryInstance({
325
- geometry: new Cesium.RectangleGeometry({
326
- rectangle: this.rectangle
327
- })
328
- }),
329
- compressVertices: false,
330
- asynchronous: false,
331
- appearance: new Cesium.MaterialAppearance({
332
- flat: true,
333
- translucent: false,
334
- material: Cesium.Material.fromType('Color', {
335
- color: Cesium.Color.fromAlpha(this.tileId.tileColor, 0.25)
336
- }),
337
- renderState: {
338
- blending: Cesium.BlendingState.ALPHA_BLEND,
339
- depthMask: false,
340
- depthTest: {
341
- enabled: true
342
- },
343
- cull: {
344
- enabled: false
345
- }
346
- }
347
- })
348
- })
349
- this.primitive.name = '_tile-color_'
350
- }
351
-
352
- const tileComandList = this.commandList || []
353
- const tileIdCommands = this.tileIdCommands || []
354
- const tileDepthCommands = this.tileDepthCommands || []
355
-
356
- if (!tileComandList.length) {
357
- const preCommandList = frameState.commandList
358
- frameState.commandList = tileComandList
359
-
360
- this.primitive.update(frameState)
361
-
362
- if (tileComandList.length) {
363
-
364
- const tileIdColor = this.tileId.color
365
- for (const tileComand of tileComandList) {
366
- tileComand.pass = Cesium.Pass.CESIUM_3D_TILE
367
-
368
- const tileIdCommand = Cesium.DrawCommand.shallowClone(tileComand)
369
- tileIdCommand.renderState = Cesium.RenderState.fromCache({
370
- id: 'tileId',
371
- blending: {
372
- enabled: false
373
- },
374
- depthTest: {
375
- enabled: false
376
- },
377
- depthMask: true,
378
- cull: {
379
- enabled: true
380
- }
381
- })
382
- tileIdCommand.layerType = 'tile-id'
383
- tileIdCommand.uniformMap = {
384
- ...tileComand.uniformMap
385
- }
386
- tileIdCommand.uniformMap.color_0 = function () {
387
- return tileIdColor
388
- }
389
- tileIdCommands.push(tileIdCommand)
390
- // 浅克隆一个副本,renderState替换成只写入深度和开启模板测试的版本,以支持Entity和GroundPrimitive等贴地对象
391
- const tileDepthCommand = Cesium.DrawCommand.shallowClone(tileComand)
392
- tileDepthCommand.pass = Cesium.Pass.CESIUM_3D_TILE
393
- tileDepthCommand.renderState = tileDepthRenderSate
394
- tileDepthCommands.layerType = 'tile-depth'
395
- tileDepthCommands.push(tileDepthCommand)
396
- }
397
- this.tileIdCommands = tileIdCommands
398
- this.tileDepthCommands = tileDepthCommands
399
- }
400
-
401
- this.commandList = tileComandList
402
- frameState.commandList = preCommandList
403
- }
404
-
405
- //瓦片范围
406
- if (tileset.showTileColor && tileComandList.length) {
407
- renderList.tileCommands.push(...tileComandList)
408
- }
409
-
410
- //更新瓦片状态,根据状态执行不同的处理过程
411
-
412
- //请求瓦片数据,解析pbf/mvt文件
413
- if (this.state == 'none' && tileset.numLoading <= tileset.maxLoading) {
414
- tileset.numLoading++
415
- this.state = 'loading'
416
- this.getSources(tileset)
417
- }
418
-
419
- //创建渲染图层实例
420
- if (this.state === 'loaded' && tileset.numInitializing < tileset.maxInitializing) {
421
- this.state = 'initializing'
422
- tileset.numInitializing++
423
- this.createRenderLayers(frameState, tileset)
424
- }
425
-
426
- if (this.state === 'ready') {
427
- let visualizerReady = true, layersReady = true
428
- //更新图层渲染器
429
- for (const visualizer of this.visualizers) {
430
- visualizer.update(frameState, tileset)
431
- if (visualizer.state === 'none') {
432
- visualizerReady = false
433
- }
434
- }
435
- for (const layer of this.layers) {
436
- layer.update(frameState, tileset)
437
- if (layer.visibility != 'none' && layer.state == 'none') {
438
- layersReady = false
439
- }
440
- }
441
- //标记瓦片是否可渲染
442
- this.renderable = visualizerReady && layersReady
443
- }
444
- }
445
-
446
- render(frameState, renderList, tileset) {
447
- if (!this.renderable) {
448
- return
449
- }
450
-
451
- const tileComandList = this.commandList
452
- const tileIdCommands = this.tileIdCommands
453
- const tileDepthCommands = this.tileDepthCommands
454
-
455
- //将渲染图层追加到相应的渲染队列(渲染队列内部按图层id分组,确保不同瓦片的同一个id图层都在一组内,然后按图层顺序逐组渲染)
456
- for (const layer of this.layers) {
457
- renderList.push(layer)
458
- }
459
-
460
- for (const visualizer of this.visualizers) {
461
- renderList.visualizers.push(visualizer)
462
- }
463
-
464
- //瓦片id纹理
465
- for (const tileIdCommand of tileIdCommands) {
466
- renderList.tileIdCommands.push(tileIdCommand)
467
- }
468
-
469
- //最后将瓦片深度写入主深度缓冲区,这样才能使Entity和GroundPrimitive等贴地对象能贴合瓦片内的矢量要素
470
- if (tileDepthCommands.length) {
471
- renderList.tileCommands.push(...tileDepthCommands)
472
- }
473
- }
474
-
475
- /**
476
- * 卸载瓦片,当瓦片过期(不可见,且符合其他过期规则)时,释放pbf/mvt解析数据、图层渲染对象等资源,重置瓦片状态
477
- */
478
- unload() {
479
- //释放用于展示瓦片范围的primitive
480
- if (this.primitive) {
481
- this.primitive.destroy()
482
- this.primitive = null
483
- }
484
- this.tileGeometry = null
485
-
486
- //清空瓦片克隆的 DrawCommand,不需要手动释放,相关资源在 primitive.destroy 执行时已经释放
487
- if (this.commandList) {
488
- this.commandList.length = 0
489
- }
490
- if (this.tileIdCommands) {
491
- this.tileIdCommands.length = 0
492
- }
493
- if (this.tileDepthCommands) {
494
- this.tileDepthCommands.length = 0
495
- }
496
-
497
- //销毁渲染图层,释放图层渲染资源
498
- for (const visualizer of this.visualizers) {
499
- visualizer.destroy()
500
- }
501
- this.visualizers.length = 0
502
-
503
- for (const layer of this.layers) {
504
- layer.destroy()
505
- }
506
- this.layers.length = 0
507
-
508
- //释放pbf/mvt解析数据
509
- this.sources = {}
510
-
511
- //重置瓦片状态
512
- this.state = 'none'
513
- }
514
-
515
- /**
516
- * 销毁瓦片,释放所有资源
517
- */
518
- destroy() {
519
- this.unload()
520
-
521
- this.tileId = null
522
- this.tilingScheme = null
523
- this.parent = null
524
- if (this.children) {
525
- for (const child of this.children) {
526
- child.destroy()
527
- }
528
- this.children.length = 0
529
- this.children = null
530
- }
531
- }
532
- }
1
+ import { VectorTileset } from './VectorTileset'
2
+ import * as MVT from '@mapbox/vector-tile'
3
+ import {
4
+ IRenderLayer,
5
+ ILayerVisualizer,
6
+ RenderLayers,
7
+ LayerVisualizers
8
+ } from './layers'
9
+ import { VectorTileRenderList } from './VectorTileRenderList'
10
+ import { ISource } from './sources/ISource'
11
+ import { EXTENT } from 'maplibre-gl/src/data/extent'
12
+ import Point from '@mapbox/point-geometry'
13
+ import { subdivideVertexLine } from 'maplibre-gl/src/render/subdivision'
14
+ import { granularitySettings } from './sources/granularitySettings'
15
+ import { warnOnce } from 'maplibre-gl/src/util/util'
16
+
17
+ let tileDepthRenderSate = null
18
+ let nextTileKey = 0
19
+ let levelZeroMaximumGeometricError = null
20
+ /**
21
+ * 计算指定LOD层级的最大几何误差,相当于动态计算3DTiles中geometricError,只是形瓦片的几何误差是一个层级的所有瓦片都相同(rectangle大小都一样)
22
+ * @param {number} z 瓦片层级(整数,不是地图的缩放级别)
23
+ * @param {Cesium.TilingScheme} tilingScheme
24
+ * @returns
25
+ */
26
+ function getLevelMaximumGeometricError(z, tilingScheme) {
27
+ if (levelZeroMaximumGeometricError === null) {
28
+ levelZeroMaximumGeometricError =
29
+ Cesium.TerrainProvider.getEstimatedLevelZeroGeometricErrorForAHeightmap(
30
+ tilingScheme.ellipsoid,
31
+ 128,
32
+ tilingScheme.getNumberOfXTilesAtLevel(0)
33
+ )
34
+ }
35
+ return levelZeroMaximumGeometricError / (1 << z)
36
+ }
37
+
38
+ /**
39
+ * LOD调度重要参数SSE的计算函数,从Cesium地形调度模块中提取出来的代码,调度逻辑和地形瓦片的一致
40
+ * @param {Cesium.FrameState} frameState
41
+ * @param {VectorTileLOD} tile
42
+ * @returns
43
+ */
44
+ function screenSpaceError(frameState, tile) {
45
+ const maxGeometricError = getLevelMaximumGeometricError(
46
+ tile.z,
47
+ tile.tilingScheme
48
+ )
49
+
50
+ const distance2 = tile.distanceToCamera
51
+ const height = frameState.context.drawingBufferHeight
52
+ const sseDenominator = frameState.camera.frustum.sseDenominator
53
+ let error = (maxGeometricError * height) / (distance2 * sseDenominator)
54
+ if (frameState.fog.enabled) {
55
+ error -=
56
+ Cesium.Math.fog(distance2, frameState.fog.density) * frameState.fog.sse
57
+ }
58
+ error /= frameState.pixelRatio
59
+ return error
60
+ }
61
+
62
+ function initConstants() {
63
+ if (tileDepthRenderSate !== null) return
64
+
65
+ tileDepthRenderSate = Cesium.RenderState.fromCache({
66
+ id: 'vt_tile-depth',
67
+ blending: Cesium.BlendingState.DISABLED,
68
+ depthTest: {
69
+ enabled: true
70
+ },
71
+ depthMask: true,
72
+ cull: {
73
+ enabled: true
74
+ },
75
+ stencilMask: Cesium.StencilConstants.CESIUM_3D_TILE_MASK,
76
+ stencilTest: {
77
+ backFunction: 519,
78
+ backOperation: { fail: 7680, zFail: 7680, zPass: 7681 },
79
+ enabled: true,
80
+ frontFunction: 519,
81
+ frontOperation: { fail: 7680, zFail: 7680, zPass: 7681 },
82
+ mask: 128,
83
+ reference: 128
84
+ },
85
+ colorMask: {
86
+ red: false,
87
+ green: false,
88
+ blue: false,
89
+ alpha: false
90
+ }
91
+ })
92
+ }
93
+
94
+ export class VectorTileLOD {
95
+ constructor(options) {
96
+ initConstants()
97
+
98
+ this.x = options.x
99
+ this.y = options.y
100
+ this.z = options.z
101
+ /**暂时用不到,但是还是记录父级瓦片 */
102
+ this.parent = options.parent
103
+ this.children = []
104
+
105
+ /**@type {Cesium.TilingScheme} */
106
+ this.tilingScheme = options.tilingScheme
107
+ this.rectangle = this.tilingScheme.tileXYToRectangle(this.x, this.y, this.z)
108
+ /**
109
+ * Cesium.TileBoundingRegion 不是公开的API,但是可以从Cesium地形调度相关模块源码学习,
110
+ */
111
+ this.tileBoundingRegion = new Cesium.TileBoundingRegion({
112
+ rectangle: this.rectangle,
113
+ minimumHeight: 0,
114
+ maximumHeight: 0,
115
+ ellipsoid: this.tilingScheme.ellipsoid,
116
+ computeBoundingVolumes: true
117
+ })
118
+
119
+ /**@type {IRenderLayer[]} */
120
+ this.layers = []
121
+ /**@type {ILayerVisualizer[]} */
122
+ this.visualizers = []
123
+ /**
124
+ * 保存pbf/mvt文件解析结果,准备创建图层(要素过滤)时候进一步读取要素,创建图层渲染对象时读取要素几何数据
125
+ * @type {Record<string,MVT.VectorTile>}
126
+ */
127
+ this.sources = {}
128
+
129
+ this.tileId = null
130
+ /**
131
+ * 记录最近一次访问时间(用渲染帧数表示),用于筛选过期瓦片
132
+ */
133
+ this.lastVisitTime = 0
134
+ /**
135
+ * 瓦片调度状态
136
+ * @type {'none'|'loading'|'loaded'|'ready'|'error'}
137
+ */
138
+ this.state = 'none'
139
+ this.renderable = false
140
+
141
+ this.tileId = {
142
+ x: this.x,
143
+ y: this.y,
144
+ z: this.z,
145
+ key: nextTileKey++,
146
+ color: Cesium.Color.fromRgba(nextTileKey - 1),
147
+ tileColor: Cesium.Color.fromRandom({
148
+ alpha: 1
149
+ })
150
+ }
151
+
152
+ const size = EXTENT * Math.pow(2, this.z),
153
+ x0 = EXTENT * this.x,
154
+ y0 = EXTENT * this.y
155
+ this.transformPoint = function (x, y, lonlat) {
156
+ lonlat[0] = ((x + x0) * 360) / size - 180
157
+ lonlat[1] =
158
+ (360 / Math.PI) *
159
+ Math.atan(Math.exp((1 - ((y + y0) * 2) / size) * Math.PI)) -
160
+ 90
161
+ return lonlat //[x, y]
162
+ }
163
+ }
164
+
165
+ createChildren() {
166
+ var tiles = [
167
+ {
168
+ x: this.x * 2,
169
+ y: this.y * 2 + 1,
170
+ z: this.z + 1
171
+ },
172
+ {
173
+ x: this.x * 2 + 1,
174
+ y: this.y * 2 + 1,
175
+ z: this.z + 1
176
+ },
177
+ {
178
+ x: this.x * 2,
179
+ y: this.y * 2,
180
+ z: this.z + 1
181
+ },
182
+ {
183
+ x: this.x * 2 + 1,
184
+ y: this.y * 2,
185
+ z: this.z + 1
186
+ }
187
+ ]
188
+
189
+ for (const { x, y, z } of tiles) {
190
+ var child = new VectorTileLOD({
191
+ x,
192
+ y,
193
+ z,
194
+ tilingScheme: this.tilingScheme,
195
+ parent: this
196
+ })
197
+ this.children.push(child)
198
+ }
199
+ }
200
+
201
+ /**
202
+ * @param {Cesium.FrameState} frameState
203
+ * @param {{visitChildren(child:VectorTileLOD):void;accept(tile:VectorTileLOD):void}} visitor
204
+ * @returns
205
+ */
206
+ visit(frameState, visitor) {
207
+ const tileBoundingRegion = this.tileBoundingRegion
208
+
209
+ //相机视锥剔除
210
+ this.distanceToCamera = tileBoundingRegion.distanceToCamera(frameState)
211
+ this.visibility =
212
+ frameState.cullingVolume.computeVisibility(tileBoundingRegion)
213
+ if (this.visibility == Cesium.Intersect.OUTSIDE) {
214
+ return
215
+ }
216
+
217
+ //性能优化:还可以进行地平线剔除更多被地球完全遮挡的瓦片
218
+
219
+ const maxSSE = frameState.maximumScreenSpaceError
220
+ const sse = screenSpaceError(frameState, this)
221
+ if (sse >= maxSSE) {
222
+ //继续遍历子级瓦片
223
+ visitor.visitChildren(this)
224
+ } else {
225
+ //使用当前瓦片渲染
226
+ visitor.accept(this)
227
+ }
228
+ }
229
+
230
+ /**
231
+ * @param {VectorTileset} tileset
232
+ */
233
+ async getSources(tileset) {
234
+ /**@type {Record<string,ISource>} */
235
+ const sourcesToLoad = {}
236
+ const style = tileset._styleJson
237
+
238
+ for (const styleLayer of style.layers) {
239
+ const sourceId = styleLayer.source
240
+ const source = tileset.sources[sourceId]
241
+ if (source && !sourcesToLoad[sourceId]) {
242
+ sourcesToLoad[sourceId] = source
243
+ }
244
+ }
245
+
246
+ const useWorker =
247
+ tileset._taskProcessor &&
248
+ Object.values(sourcesToLoad).every(s => s.type === 'vector')
249
+
250
+ if (useWorker) {
251
+ for (const sourceId in sourcesToLoad) {
252
+ const source = sourcesToLoad[sourceId]
253
+ try {
254
+ const data = await source.requestTileBuffer(
255
+ this.x,
256
+ this.y,
257
+ this.z,
258
+ tileset
259
+ )
260
+ if (data && data.buffer) {
261
+ this.sources[sourceId] = data
262
+ }
263
+ } catch (err) {}
264
+ }
265
+ this._workerBuffers = true
266
+ } else {
267
+ for (const sourceId in sourcesToLoad) {
268
+ const source = sourcesToLoad[sourceId]
269
+ try {
270
+ const tileData = await source.requestTile(
271
+ this.x,
272
+ this.y,
273
+ this.z,
274
+ tileset
275
+ )
276
+ if (tileData) {
277
+ this.sources[sourceId] = tileData
278
+ }
279
+ } catch (err) {}
280
+ }
281
+ }
282
+
283
+ tileset.numLoading--
284
+ this.state = 'loaded'
285
+ }
286
+
287
+ /**
288
+ * @param {Cesium.FrameRateMonitor} frameState
289
+ * @param {VectorTileset} tileset
290
+ */
291
+ async createRenderLayers(frameState, tileset) {
292
+ const sources = this.sources
293
+ const styleLayers = tileset._styleLayers
294
+ const renderLayers = this.layers
295
+ const visualizers = this.visualizers
296
+ /**@type {Record<string,ILayerVisualizer>} */
297
+ const visualizerMap = {}
298
+
299
+ for (const styleLayer of styleLayers) {
300
+ const sourceVectorTile = sources[styleLayer.source]
301
+ /**
302
+ * 按图层类型获取对应的渲染图层类
303
+ * @type {typeof IRenderLayer}
304
+ */
305
+ const RenderLayer = RenderLayers[styleLayer.type]
306
+ const LayerVisualizer = LayerVisualizers[styleLayer.type]
307
+ const isBackgroundLayer = styleLayer.type === 'background'
308
+ if (!RenderLayer) {
309
+ warnOnce('不支持图层类型' + styleLayer.type)
310
+ }
311
+ if ((!isBackgroundLayer && !sourceVectorTile) || !RenderLayer) continue
312
+
313
+ const features = []
314
+ if (!isBackgroundLayer) {
315
+ const sourceType =
316
+ styleLayer.source && tileset.sources[styleLayer.source].type
317
+ const sourceLayer =
318
+ sourceType == 'geojson' ? '_geojsonTileLayer' : styleLayer.sourceLayer
319
+ const vectorTileLayer = sourceVectorTile.layers[sourceLayer]
320
+ if (!vectorTileLayer) continue
321
+
322
+ //读取要素,并根据图层样式filter表达式进行过滤
323
+ //性能优化:同一帧读取和过滤大量要素,耗时较长,应该将其移到Web Worker
324
+ const featureCount = vectorTileLayer.length
325
+ for (let i = 0; i < featureCount; i++) {
326
+ //读取要素
327
+ const feature = vectorTileLayer.feature(i)
328
+ //过滤要素
329
+ if (
330
+ styleLayer.filter &&
331
+ !styleLayer.filter.filter({ zoom: this.z }, feature)
332
+ ) {
333
+ continue
334
+ }
335
+ //MLT的Feature类没有实现toGeoJSON方法,直接使用MVT的方法
336
+ if (!feature.toGeoJSON) {
337
+ feature.toGeoJSON = MVT.VectorTileFeature.prototype.toGeoJSON
338
+ }
339
+ features.push(feature)
340
+ }
341
+ if (!features.length) continue
342
+ }
343
+
344
+ //创建渲染图层
345
+ const renderLayer = new RenderLayer(features, styleLayer, this)
346
+ renderLayers.push(renderLayer)
347
+
348
+ //将渲染图层分配到对应类型的图层渲染器,图层渲染器应实现如下功能,以提升渲染性能:
349
+ //1、合批创建几何体、批次表和 DrawCommand;
350
+ //2、克隆合批DrwaCommand,创建副本,通过offset和count指定图层的绘制范围。
351
+ if (LayerVisualizer) {
352
+ let visualizer = visualizerMap[styleLayer.type]
353
+ if (!visualizer) {
354
+ visualizer = new LayerVisualizer(this)
355
+ visualizerMap[styleLayer.type] = visualizer
356
+ visualizers.push(visualizer)
357
+ }
358
+ visualizer.addLayer(features, renderLayer, frameState, tileset)
359
+ }
360
+ }
361
+
362
+ this.state = 'ready'
363
+ }
364
+
365
+ /**
366
+ * Web Worker 结果创建渲染图层与 Visualizer(仅几何数据来自 Worker)
367
+ * @param {object} result - Worker 返回的 { fill, line, symbol }
368
+ * @param {Cesium.FrameState} frameState
369
+ * @param {VectorTileset} tileset
370
+ */
371
+ createRenderLayersFromWorkerResult(result, frameState, tileset) {
372
+ if (result.error) {
373
+ this.state = 'error'
374
+ return
375
+ }
376
+ const styleLayers = tileset._styleLayers
377
+ const renderLayers = this.layers
378
+ const visualizers = this.visualizers
379
+ /**@type {Record<string,ILayerVisualizer>} */
380
+ const visualizerMap = {}
381
+
382
+ for (const item of result.fill || []) {
383
+ const styleLayer = styleLayers.find(l => l.id === item.layerId)
384
+ if (!styleLayer) continue
385
+ const RenderLayer = RenderLayers.fill
386
+ const LayerVisualizer = LayerVisualizers.fill
387
+ const renderLayer = new RenderLayer([], styleLayer, this)
388
+ renderLayers.push(renderLayer)
389
+ let visualizer = visualizerMap.fill
390
+ if (!visualizer) {
391
+ visualizer = new LayerVisualizer(this)
392
+ visualizerMap.fill = visualizer
393
+ visualizers.push(visualizer)
394
+ }
395
+ visualizer.addLayerFromWorkerResult(
396
+ item,
397
+ renderLayer,
398
+ frameState,
399
+ tileset
400
+ )
401
+ }
402
+
403
+ for (const item of result.line || []) {
404
+ const styleLayer = styleLayers.find(l => l.id === item.layerId)
405
+ if (!styleLayer) continue
406
+ const RenderLayer = RenderLayers.line
407
+ const LayerVisualizer = LayerVisualizers.line
408
+ const renderLayer = new RenderLayer([], styleLayer, this)
409
+ renderLayers.push(renderLayer)
410
+ let visualizer = visualizerMap.line
411
+ if (!visualizer) {
412
+ visualizer = new LayerVisualizer(this)
413
+ visualizerMap.line = visualizer
414
+ visualizers.push(visualizer)
415
+ }
416
+ visualizer.addLayerFromWorkerResult(
417
+ item,
418
+ renderLayer,
419
+ frameState,
420
+ tileset
421
+ )
422
+ }
423
+
424
+ for (const item of result.symbol || []) {
425
+ const styleLayer = styleLayers.find(l => l.id === item.layerId)
426
+ if (!styleLayer) continue
427
+ const RenderLayer = RenderLayers.symbol
428
+ const LayerVisualizer = LayerVisualizers.symbol
429
+ const renderLayer = new RenderLayer([], styleLayer, this)
430
+ renderLayers.push(renderLayer)
431
+ let visualizer = visualizerMap.symbol
432
+ if (!visualizer) {
433
+ visualizer = new LayerVisualizer(this)
434
+ visualizerMap.symbol = visualizer
435
+ visualizers.push(visualizer)
436
+ }
437
+ visualizer.addLayerFromWorkerResult(
438
+ item,
439
+ renderLayer,
440
+ frameState,
441
+ tileset
442
+ )
443
+ }
444
+
445
+ this.state = 'ready'
446
+ }
447
+
448
+ /**
449
+ * @param {Cesium.FrameState} frameState
450
+ * @param {VectorTileRenderList} renderList
451
+ * @param {VectorTileset} tileset
452
+ */
453
+ update(frameState, renderList, tileset) {
454
+ // 这里构建的 primitive 有如下用途:
455
+ // 1. showTileColor 设置为 true 时,展示瓦片范围,验证LOD调度效果
456
+ // 2. 生成绘制瓦片 id DrawCommand,在 VectorTileset 中实时更新瓦片id纹理,实现瓦片边界精准裁剪
457
+
458
+ if (!this.primitive) {
459
+ this.primitive = new Cesium.Primitive({
460
+ geometryInstances: new Cesium.GeometryInstance({
461
+ geometry: new Cesium.RectangleGeometry({
462
+ rectangle: this.rectangle
463
+ })
464
+ }),
465
+ compressVertices: false,
466
+ asynchronous: false,
467
+ appearance: new Cesium.MaterialAppearance({
468
+ flat: true,
469
+ translucent: false,
470
+ material: Cesium.Material.fromType('Color', {
471
+ color: Cesium.Color.fromAlpha(this.tileId.tileColor, 0.25)
472
+ }),
473
+ renderState: {
474
+ blending: Cesium.BlendingState.ALPHA_BLEND,
475
+ depthMask: false,
476
+ depthTest: {
477
+ enabled: true
478
+ },
479
+ cull: {
480
+ enabled: false
481
+ }
482
+ }
483
+ })
484
+ })
485
+ this.primitive.name = '_tile-color_'
486
+ }
487
+
488
+ const tileComandList = this.commandList || []
489
+ const tileIdCommands = this.tileIdCommands || []
490
+ const tileDepthCommands = this.tileDepthCommands || []
491
+
492
+ if (!tileComandList.length) {
493
+ const preCommandList = frameState.commandList
494
+ frameState.commandList = tileComandList
495
+
496
+ this.primitive.update(frameState)
497
+
498
+ if (tileComandList.length) {
499
+ const tileIdColor = this.tileId.color
500
+ for (const tileComand of tileComandList) {
501
+ tileComand.pass = Cesium.Pass.CESIUM_3D_TILE
502
+
503
+ const tileIdCommand = Cesium.DrawCommand.shallowClone(tileComand)
504
+ tileIdCommand.renderState = Cesium.RenderState.fromCache({
505
+ id: 'tileId',
506
+ blending: {
507
+ enabled: false
508
+ },
509
+ depthTest: {
510
+ enabled: false
511
+ },
512
+ depthMask: true,
513
+ cull: {
514
+ enabled: true
515
+ }
516
+ })
517
+ tileIdCommand.layerType = 'tile-id'
518
+ tileIdCommand.uniformMap = {
519
+ ...tileComand.uniformMap
520
+ }
521
+ tileIdCommand.uniformMap.color_0 = function () {
522
+ return tileIdColor
523
+ }
524
+ tileIdCommands.push(tileIdCommand)
525
+ // 浅克隆一个副本,renderState替换成只写入深度和开启模板测试的版本,以支持Entity和GroundPrimitive等贴地对象
526
+ const tileDepthCommand = Cesium.DrawCommand.shallowClone(tileComand)
527
+ tileDepthCommand.pass = Cesium.Pass.CESIUM_3D_TILE
528
+ tileDepthCommand.renderState = tileDepthRenderSate
529
+ tileDepthCommands.layerType = 'tile-depth'
530
+ tileDepthCommands.push(tileDepthCommand)
531
+ }
532
+ this.tileIdCommands = tileIdCommands
533
+ this.tileDepthCommands = tileDepthCommands
534
+ }
535
+
536
+ this.commandList = tileComandList
537
+ frameState.commandList = preCommandList
538
+ }
539
+
540
+ //瓦片范围
541
+ if (tileset.showTileColor && tileComandList.length) {
542
+ renderList.tileCommands.push(...tileComandList)
543
+ }
544
+
545
+ //更新瓦片状态,根据状态执行不同的处理过程
546
+
547
+ //请求瓦片数据,解析pbf/mvt文件
548
+ if (this.state == 'none' && tileset.numLoading <= tileset.maxLoading) {
549
+ tileset.numLoading++
550
+ this.state = 'loading'
551
+ this.getSources(tileset)
552
+ }
553
+
554
+ //创建渲染图层实例(主线程或 Worker 路径)
555
+ if (
556
+ this.state === 'loaded' &&
557
+ tileset.numInitializing < tileset.maxInitializing
558
+ ) {
559
+ if (this._workerBuffers && tileset._taskProcessor) {
560
+ const parameters = {
561
+ sources: {},
562
+ x: this.x,
563
+ y: this.y,
564
+ z: this.z,
565
+ extent: EXTENT,
566
+ styleLayers: tileset._styleLayers.map(sl => ({
567
+ id: sl.id,
568
+ type: sl.type,
569
+ source: sl.source,
570
+ sourceLayer: sl.sourceLayer ?? sl.data['source-layer'],
571
+ filter: sl.data.filter,
572
+ paint: sl.data.paint,
573
+ layout: sl.data.layout
574
+ }))
575
+ }
576
+ const transferableObjects = []
577
+ for (const sourceId in this.sources) {
578
+ const data = this.sources[sourceId]
579
+ if (data && data.buffer) {
580
+ parameters.sources[sourceId] = {
581
+ buffer: data.buffer,
582
+ encoding: data.encoding || 'mvt'
583
+ }
584
+ transferableObjects.push(data.buffer)
585
+ }
586
+ }
587
+ const promise = tileset._taskProcessor.scheduleTask(
588
+ parameters,
589
+ transferableObjects
590
+ )
591
+ if (Cesium.defined(promise)) {
592
+ this.state = 'initializing'
593
+ tileset.numInitializing++
594
+ promise
595
+ .then(result => {
596
+ this.createRenderLayersFromWorkerResult(
597
+ result,
598
+ frameState,
599
+ tileset
600
+ )
601
+ })
602
+ .catch(() => {
603
+ this.state = 'error'
604
+ })
605
+ }
606
+ } else {
607
+ this.state = 'initializing'
608
+ tileset.numInitializing++
609
+ this.createRenderLayers(frameState, tileset)
610
+ }
611
+ }
612
+
613
+ if (this.state === 'ready') {
614
+ let visualizerReady = true,
615
+ layersReady = true
616
+ //更新图层渲染器
617
+ for (const visualizer of this.visualizers) {
618
+ visualizer.update(frameState, tileset)
619
+ if (visualizer.state === 'none') {
620
+ visualizerReady = false
621
+ }
622
+ }
623
+ for (const layer of this.layers) {
624
+ layer.update(frameState, tileset)
625
+ if (layer.visibility != 'none' && layer.state == 'none') {
626
+ layersReady = false
627
+ }
628
+ }
629
+ //标记瓦片是否可渲染
630
+ this.renderable = visualizerReady && layersReady
631
+ }
632
+ }
633
+
634
+ render(frameState, renderList, tileset) {
635
+ if (!this.renderable) {
636
+ return
637
+ }
638
+
639
+ const tileComandList = this.commandList
640
+ const tileIdCommands = this.tileIdCommands
641
+ const tileDepthCommands = this.tileDepthCommands
642
+
643
+ //将渲染图层追加到相应的渲染队列(渲染队列内部按图层id分组,确保不同瓦片的同一个id图层都在一组内,然后按图层顺序逐组渲染)
644
+ for (const layer of this.layers) {
645
+ renderList.push(layer)
646
+ }
647
+
648
+ for (const visualizer of this.visualizers) {
649
+ renderList.visualizers.push(visualizer)
650
+ }
651
+
652
+ //瓦片id纹理
653
+ for (const tileIdCommand of tileIdCommands) {
654
+ renderList.tileIdCommands.push(tileIdCommand)
655
+ }
656
+
657
+ //最后将瓦片深度写入主深度缓冲区,这样才能使Entity和GroundPrimitive等贴地对象能贴合瓦片内的矢量要素
658
+ if (tileDepthCommands.length) {
659
+ renderList.tileCommands.push(...tileDepthCommands)
660
+ }
661
+ }
662
+
663
+ /**
664
+ * 卸载瓦片,当瓦片过期(不可见,且符合其他过期规则)时,释放pbf/mvt解析数据、图层渲染对象等资源,重置瓦片状态
665
+ */
666
+ unload() {
667
+ //释放用于展示瓦片范围的primitive
668
+ if (this.primitive) {
669
+ this.primitive.destroy()
670
+ this.primitive = null
671
+ }
672
+ this.tileGeometry = null
673
+
674
+ //清空瓦片克隆的 DrawCommand,不需要手动释放,相关资源在 primitive.destroy 执行时已经释放
675
+ if (this.commandList) {
676
+ this.commandList.length = 0
677
+ }
678
+ if (this.tileIdCommands) {
679
+ this.tileIdCommands.length = 0
680
+ }
681
+ if (this.tileDepthCommands) {
682
+ this.tileDepthCommands.length = 0
683
+ }
684
+
685
+ //销毁渲染图层,释放图层渲染资源
686
+ for (const visualizer of this.visualizers) {
687
+ visualizer.destroy()
688
+ }
689
+ this.visualizers.length = 0
690
+
691
+ for (const layer of this.layers) {
692
+ layer.destroy()
693
+ }
694
+ this.layers.length = 0
695
+
696
+ //释放pbf/mvt解析数据
697
+ this.sources = {}
698
+
699
+ //重置瓦片状态
700
+ this.state = 'none'
701
+ }
702
+
703
+ /**
704
+ * 销毁瓦片,释放所有资源
705
+ */
706
+ destroy() {
707
+ this.unload()
708
+
709
+ this.tileId = null
710
+ this.tilingScheme = null
711
+ this.parent = null
712
+ if (this.children) {
713
+ for (const child of this.children) {
714
+ child.destroy()
715
+ }
716
+ this.children.length = 0
717
+ this.children = null
718
+ }
719
+ }
720
+ }