@mesh3d/cesium-vectortile-gl 0.5.1 → 0.5.2

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.
@@ -4,6 +4,11 @@ import { registerRenderLayer } from './registerRenderLayer'
4
4
  import { FillLayerVisualizer } from './visualizers/FillLayerVisualizer'
5
5
 
6
6
  export class FillRenderLayer extends IRenderLayer {
7
+ constructor(sourceFeatures, styleLayer, tile) {
8
+ super(sourceFeatures, styleLayer, tile)
9
+ this.outlineOffsets = []
10
+ this.outlineCounts = []
11
+ }
7
12
  /**
8
13
  * @param {Cesium.FrameState} frameState
9
14
  * @param {VectorTileset} tileset
@@ -22,6 +22,7 @@ export class FillLayerVisualizer extends ILayerVisualizer {
22
22
  super(layers, tile)
23
23
 
24
24
  this.geometryInstances = []
25
+ this.outlineGeometryInstances = []
25
26
  this.primitive = null
26
27
  this.commandsReady = false
27
28
  }
@@ -67,6 +68,11 @@ export class FillLayerVisualizer extends ILayerVisualizer {
67
68
  sourceFeature
68
69
  )
69
70
 
71
+ const fillOutlineColor =
72
+ style.convertColor(
73
+ style.paint.getDataValue('fill-outline-color', tile.z, sourceFeature)
74
+ ) || fillColor
75
+
70
76
  //关键:对投影坐标细分,而不是使用cesium内置的细分
71
77
  const vtCoords = loadGeometry(sourceFeature)
72
78
  const polygons = classifyRings(vtCoords)
@@ -84,6 +90,7 @@ export class FillLayerVisualizer extends ILayerVisualizer {
84
90
  featureId,
85
91
  fillColor,
86
92
  fillOpacity,
93
+ fillOutlineColor,
87
94
  properties,
88
95
  //保存原始数据的要素id,后续可以用来支持 featureState 表达式,这个表达式可以实现选定要素高亮显示
89
96
  id: sourceFeatureId,
@@ -187,9 +194,12 @@ export class FillLayerVisualizer extends ILayerVisualizer {
187
194
  */
188
195
  addFeature(feature, granularity) {
189
196
  const geometryInstances = this.geometryInstances
190
- const { coordinates, fillColor, fillOpacity } = feature
197
+ const outlineGeometryInstances = this.outlineGeometryInstances
198
+ const { coordinates, fillColor, fillOpacity, fillOutlineColor } = feature
191
199
  const colorBytes = fillColor.toBytes()
192
200
  colorBytes[3] = Math.floor(colorBytes[3] * fillOpacity)
201
+ const outlineColorBytes = fillOutlineColor.toBytes()
202
+ outlineColorBytes[3] = Math.floor(outlineColorBytes[3] * fillOpacity)
193
203
 
194
204
  // 使用 maplibre-gl 的 subdividePolygon 基于投影坐标进行细分,
195
205
  // 而不是在转成世界坐标后再使用 Cesium.PolygonGeometry 构建,这样才能避免出现自相交、破面等现象。
@@ -199,7 +209,7 @@ export class FillLayerVisualizer extends ILayerVisualizer {
199
209
  coordinates,
200
210
  this.tile,
201
211
  granularity,
202
- false
212
+ true
203
213
  )
204
214
  const verticesFlattened = subdivisionRes.verticesFlattened
205
215
  const coordDeg = [0, 0],
@@ -241,6 +251,14 @@ export class FillLayerVisualizer extends ILayerVisualizer {
241
251
  : Uint8Array
242
252
  )(subdivisionRes.indicesTriangles)
243
253
 
254
+ const outlineIndices = new (
255
+ vertCount > 65535
256
+ ? Uint32Array
257
+ : vertCount > 255
258
+ ? Uint16Array
259
+ : Uint8Array
260
+ )(subdivisionRes.indicesLineList.flat(3))
261
+
244
262
  const geometry = new Cesium.Geometry({
245
263
  attributes: {
246
264
  position: {
@@ -266,6 +284,19 @@ export class FillLayerVisualizer extends ILayerVisualizer {
266
284
  indices: indices,
267
285
  boundingSphere: Cesium.BoundingSphere.fromVertices(positions)
268
286
  })
287
+ const outlineGeometry = new Cesium.Geometry({
288
+ attributes: {
289
+ position: {
290
+ componentDatatype: Cesium.ComponentDatatype.DOUBLE,
291
+ componentsPerAttribute: 3,
292
+ normalize: false,
293
+ values: positions
294
+ }
295
+ },
296
+ primitiveType: Cesium.PrimitiveType.LINES,
297
+ indices: outlineIndices,
298
+ boundingSphere: Cesium.BoundingSphere.fromVertices(positions)
299
+ })
269
300
 
270
301
  const cartographic = Cesium.Cartographic.fromCartesian(
271
302
  geometry.boundingSphere.center
@@ -295,6 +326,25 @@ export class FillLayerVisualizer extends ILayerVisualizer {
295
326
  })
296
327
  })
297
328
  geometryInstances.push(instance)
329
+
330
+ const outlineInstance = new Cesium.GeometryInstance({
331
+ geometry: outlineGeometry,
332
+ attributes: {
333
+ color: new Cesium.GeometryInstanceAttribute({
334
+ componentDatatype: Cesium.ComponentDatatype.UNSIGNED_BYTE,
335
+ componentsPerAttribute: 4,
336
+ normalize: true,
337
+ value: outlineColorBytes
338
+ })
339
+ },
340
+ //通过entity的形式暴露给Cesium pickEntity,这样点击时系统自带的inforbox可以弹出
341
+ id: new Cesium.Entity({
342
+ position: center,
343
+ id: feature.id,
344
+ properties: feature.properties
345
+ })
346
+ })
347
+ outlineGeometryInstances.push(outlineInstance)
298
348
  }
299
349
 
300
350
  createPrimitive() {
@@ -433,6 +483,140 @@ void main()
433
483
  }
434
484
  }
435
485
 
486
+ createOutlinePrimitive() {
487
+ const primitive = new Cesium.Primitive({
488
+ geometryInstances: this.outlineGeometryInstances,
489
+ asynchronous: !(
490
+ this.outlineGeometryInstances[0].geometry instanceof Cesium.Geometry
491
+ ),
492
+ appearance: new Cesium.PerInstanceColorAppearance({
493
+ flat: true,
494
+ translucent: false,
495
+ renderState: {
496
+ //这里设置是没有用的,只要 translucent 为 false,
497
+ //Cesium 内部都会覆盖成 true,所以我们需要在 DrawCommand 创建完成后再设置
498
+ depthMask: false
499
+ },
500
+ fragmentShaderSource: /*glsl*/ `
501
+ in vec4 v_color;
502
+
503
+ uniform vec4 tileId;
504
+ uniform sampler2D tileIdTexture;
505
+
506
+ void main()
507
+ {
508
+ vec2 id_st = gl_FragCoord.xy / czm_viewport.zw;
509
+ vec4 bgId = texture(tileIdTexture, id_st);
510
+ if (!all(equal(bgId, tileId)))
511
+ {
512
+ discard;
513
+ }
514
+ out_FragColor = v_color;
515
+ }
516
+ `
517
+ })
518
+ })
519
+
520
+ //通过定义 Primitive 私有变量 _geometries、_batchTable 的 setter 和 getter,
521
+ //监听合批几何体和批次表的创建:
522
+ //1、几何体创建完成后,根据 batchId 和 featureId,计算每个图层几何体的起始索引(offset)和索引数量(count)
523
+ //2、批次表创建完成后,保存备用
524
+ let scope = this
525
+ Object.defineProperties(primitive, {
526
+ _geometries: {
527
+ get() {
528
+ return this._geometries_
529
+ },
530
+ set(geometries) {
531
+ this._geometries_ = geometries
532
+ if (geometries) {
533
+ scope.onOutlineGeometriesLoaded(geometries)
534
+ } else {
535
+ scope = null
536
+ }
537
+ }
538
+ },
539
+ _batchTable: {
540
+ get() {
541
+ return this._batchTable_
542
+ },
543
+ set(batchTable) {
544
+ this._batchTable_ = batchTable
545
+ if (batchTable) {
546
+ scope.onOutlineBatchTableCreated(batchTable)
547
+ }
548
+ }
549
+ }
550
+ })
551
+
552
+ this.outlinePrimitive = primitive
553
+ }
554
+ /**
555
+ * 根据 batchId 和 featureId,计算每个图层几何体的起始索引(offset)和索引数量(count)
556
+ * @param {Cesium.Geometry[]} geometries
557
+ */
558
+ onOutlineGeometriesLoaded(geometries) {
559
+ //Cesium 几何体合批结果可能是多个几何体,对应多个 DrawCommand
560
+ for (let pass = 0; pass < geometries.length; pass++) {
561
+ const batches = {}
562
+ const geometry = geometries[pass]
563
+ const batchIds = geometry.attributes.batchId.values
564
+ const indices = geometry.indices
565
+
566
+ //提取每个批次的起始和结束索引
567
+ let currBatchId = -1
568
+ let currBatch = null
569
+ for (let i = 0; i < indices.length; i++) {
570
+ const vertIndex = indices[i]
571
+ const batchId = batchIds[vertIndex]
572
+ if (currBatchId !== batchId) {
573
+ currBatchId = batchId
574
+ currBatch = batches[currBatchId] = {
575
+ begin: i,
576
+ end: i
577
+ }
578
+ }
579
+ currBatch.end = i
580
+ }
581
+
582
+ //根据图层批次范围,提取图层几何体索引范围,即起始索引(offset)和索引数量(count)
583
+ for (const layer of this.layers) {
584
+ const { firstBatchId, lastBatchId } = layer
585
+ if (firstBatchId === -1 || lastBatchId === -1) {
586
+ continue
587
+ }
588
+
589
+ let begin = -1,
590
+ end = -1
591
+ for (let batchId = firstBatchId; batchId <= lastBatchId; batchId++) {
592
+ const batch = batches[batchId]
593
+ if (batch) {
594
+ if (begin === -1) begin = batch.begin
595
+ end = batch.end
596
+ }
597
+ }
598
+
599
+ if (begin === -1 || end === -1) {
600
+ continue
601
+ }
602
+
603
+ //起始和结束索引,索引数量需要加1
604
+ layer.outlineOffsets[pass] = begin
605
+ layer.outlineCounts[pass] = end - begin + 1
606
+ }
607
+ }
608
+ }
609
+ /**
610
+ * 保存 Cesium Primitive 创建的批次表。图层样式变化时,通过更新批次表传递到GPU,同步更新渲染效果
611
+ * @param {Cesium.BatchTable} batchTable
612
+ */
613
+ onOutlineBatchTableCreated(batchTable) {
614
+ this._outlineBatchTable = batchTable
615
+ for (const layer of this.layers) {
616
+ layer._outlineBatchTable = batchTable
617
+ }
618
+ }
619
+
436
620
  /**
437
621
  * 使用合批后的 drawCommand 创建副本,为渲染图层分配 drawCommand
438
622
  * @param {Cesium.DrawCommand[]} batchedCommandList
@@ -456,14 +640,29 @@ void main()
456
640
  for (let i = 0; i < this.layers.length; i++) {
457
641
  const layer = this.layers[i]
458
642
  const layerCommandList = (layer.commandList = [])
643
+ const passes = {
644
+ fill: 0,
645
+ outline: 0
646
+ }
647
+
648
+ for (let i = 0; i < batchedCommandList.length; i++) {
649
+ const command = batchedCommandList[i]
650
+ let pass, offsets, counts
651
+ if (command.primitiveType == Cesium.PrimitiveType.LINES) {
652
+ pass = passes.outline++
653
+ offsets = layer.outlineOffsets
654
+ counts = layer.outlineCounts
655
+ } else {
656
+ pass = passes.fill++
657
+ offsets = layer.offsets
658
+ counts = layer.counts
659
+ }
459
660
 
460
- for (let pass = 0; pass < batchedCommandList.length; pass++) {
461
- const offset = layer.offsets[pass],
462
- count = layer.counts[pass]
661
+ const offset = offsets[pass],
662
+ count = counts[pass]
463
663
  if (typeof offset !== 'number' || typeof count !== 'number') {
464
664
  continue
465
665
  }
466
- const command = batchedCommandList[pass]
467
666
  command.uniformMap.tileIdTexture = function () {
468
667
  return tileset.tileIdTexture
469
668
  }
@@ -497,6 +696,7 @@ void main()
497
696
 
498
697
  if (!this.primitive && this.geometryInstances.length) {
499
698
  this.createPrimitive()
699
+ this.createOutlinePrimitive()
500
700
  }
501
701
 
502
702
  if (this.primitive && this.state !== 'done' && this.state !== 'error') {
@@ -508,9 +708,11 @@ void main()
508
708
  //执行 primitive 的 update ,直到生成了合批后的drawCommand
509
709
  try {
510
710
  this.primitive.update(frameState)
711
+ this.outlinePrimitive.update(frameState)
511
712
  } catch (err) {
512
713
  //如果报错,下一帧就不要再执行 update 了,以免重复打印错误信息
513
714
  this.geometryInstances = []
715
+ this.outlineGeometryInstances = []
514
716
  this.setState('error')
515
717
  if (err.stack) console.trace(err.stack)
516
718
  else console.error(err)
@@ -525,7 +727,10 @@ void main()
525
727
  this.createLayerCommands(batchedCommandList, tileset)
526
728
  }
527
729
 
528
- if (this.primitive._state === Cesium.PrimitiveState.FAILED) {
730
+ if (
731
+ this.primitive._state === Cesium.PrimitiveState.FAILED ||
732
+ this.outlinePrimitive._state === Cesium.PrimitiveState.FAILED
733
+ ) {
529
734
  this.setState('error')
530
735
  }
531
736
 
@@ -535,6 +740,9 @@ void main()
535
740
  if (this._batchTable && this._batchTable._batchValuesDirty) {
536
741
  this._batchTable.update(frameState)
537
742
  }
743
+ if (this._outlineBatchTable && this._outlineBatchTable._batchValuesDirty) {
744
+ this._outlineBatchTable.update(frameState)
745
+ }
538
746
  }
539
747
 
540
748
  destroy() {
@@ -57,6 +57,7 @@ export class StyleLayer {
57
57
  * @returns
58
58
  */
59
59
  convertColor(styleColor, result) {
60
+ if (!styleColor) return
60
61
  const alphaScalar = styleColor.a > 0 ? 1 / styleColor.a : 1
61
62
  if (!result) {
62
63
  result = new Cesium.Color()
package/logo.svg CHANGED
@@ -1,47 +1,47 @@
1
- <svg width="400" height="400" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
2
- <!-- 背景透明 -->
3
- <rect width="400" height="400" fill="transparent"/>
4
-
5
- <!-- 地球:经纬线 + 瓦片网格 -->
6
- <g stroke="#2196F3" stroke-width="1.5" fill="none" opacity="0.8">
7
- <!-- 外圆(赤道) -->
8
- <circle cx="200" cy="200" r="70" />
9
-
10
- <!-- 经线(3条) -->
11
- <path d="M200,130 A70,70 0 0,1 200,270" />
12
- <path d="M200,130 A70,70 0 0,0 200,270" />
13
- <line x1="130" y1="200" x2="270" y2="200" />
14
-
15
- <!-- 瓦片网格(示意 MVT 分块) -->
16
- <rect x="165" y="165" width="35" height="35" stroke-dasharray="2,2" opacity="0.6"/>
17
- <rect x="200" y="165" width="35" height="35" stroke-dasharray="2,2" opacity="0.6"/>
18
- <rect x="165" y="200" width="35" height="35" stroke-dasharray="2,2" opacity="0.6"/>
19
- </g>
20
-
21
- <!-- Mapbox 风格水滴(POI) -->
22
- <path d="M200,165
23
- C205,160 210,165 210,170
24
- C210,190 200,200 200,200
25
- C200,200 190,190 190,170
26
- C190,165 195,160 200,165 Z"
27
- fill="#2196F3" opacity="0.95"/>
28
-
29
- <!-- 矢量数据流(从水滴延伸出的 MVT 折线) -->
30
- <path d="M200,200
31
- L210,210
32
- L230,205
33
- L250,220
34
- L270,210"
35
- fill="none"
36
- stroke="#2196F3"
37
- stroke-width="2.5"
38
- stroke-linecap="round"
39
- stroke-linejoin="round"/>
40
-
41
- <!-- 项目名称 -->
42
- <text x="200" y="330" text-anchor="middle"
43
- font-family="ui-monospace, SFMono-Regular, 'JetBrains Mono', monospace"
44
- font-size="24" fill="#2196F3" font-weight="600">
45
- CVT-GL
46
- </text>
1
+ <svg width="400" height="400" viewBox="0 0 400 400" xmlns="http://www.w3.org/2000/svg">
2
+ <!-- 背景透明 -->
3
+ <rect width="400" height="400" fill="transparent"/>
4
+
5
+ <!-- 地球:经纬线 + 瓦片网格 -->
6
+ <g stroke="#2196F3" stroke-width="1.5" fill="none" opacity="0.8">
7
+ <!-- 外圆(赤道) -->
8
+ <circle cx="200" cy="200" r="70" />
9
+
10
+ <!-- 经线(3条) -->
11
+ <path d="M200,130 A70,70 0 0,1 200,270" />
12
+ <path d="M200,130 A70,70 0 0,0 200,270" />
13
+ <line x1="130" y1="200" x2="270" y2="200" />
14
+
15
+ <!-- 瓦片网格(示意 MVT 分块) -->
16
+ <rect x="165" y="165" width="35" height="35" stroke-dasharray="2,2" opacity="0.6"/>
17
+ <rect x="200" y="165" width="35" height="35" stroke-dasharray="2,2" opacity="0.6"/>
18
+ <rect x="165" y="200" width="35" height="35" stroke-dasharray="2,2" opacity="0.6"/>
19
+ </g>
20
+
21
+ <!-- Mapbox 风格水滴(POI) -->
22
+ <path d="M200,165
23
+ C205,160 210,165 210,170
24
+ C210,190 200,200 200,200
25
+ C200,200 190,190 190,170
26
+ C190,165 195,160 200,165 Z"
27
+ fill="#2196F3" opacity="0.95"/>
28
+
29
+ <!-- 矢量数据流(从水滴延伸出的 MVT 折线) -->
30
+ <path d="M200,200
31
+ L210,210
32
+ L230,205
33
+ L250,220
34
+ L270,210"
35
+ fill="none"
36
+ stroke="#2196F3"
37
+ stroke-width="2.5"
38
+ stroke-linecap="round"
39
+ stroke-linejoin="round"/>
40
+
41
+ <!-- 项目名称 -->
42
+ <text x="200" y="330" text-anchor="middle"
43
+ font-family="ui-monospace, SFMono-Regular, 'JetBrains Mono', monospace"
44
+ font-size="24" fill="#2196F3" font-weight="600">
45
+ CVT-GL
46
+ </text>
47
47
  </svg>
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@mesh3d/cesium-vectortile-gl",
3
- "version": "0.5.1",
3
+ "version": "0.5.2",
4
4
  "description": "CesiumJS 矢量瓦片渲染库",
5
5
  "main": "dist/cvt-gl.js",
6
6
  "module": "index.js",