@mesh3d/cesium-vectortile-gl 0.4.6 → 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.
- package/README.md +29 -0
- package/Source/VectorTileset.js +61 -0
- package/Source/layers/BackgroundRenderLayer.js +15 -0
- package/Source/layers/FillRenderLayer.js +34 -0
- package/Source/layers/IRenderLayer.js +13 -0
- package/Source/layers/LineRenderLayer.js +43 -0
- package/Source/layers/SymbolRenderLayer.js +35 -0
- package/Source/layers/visualizers/FillLayerVisualizer.js +226 -9
- package/Source/layers/visualizers/LineLayerVisualizer.js +11 -2
- package/Source/layers/visualizers/SymbolLayerVisualizer.js +17 -3
- package/Source/style/StyleLayer.js +28 -0
- package/Source/style/StyleLayerProperties.js +22 -0
- package/logo.svg +46 -46
- package/package.json +6 -15
- package/style-edit.html +26 -0
- package/dist/THIRD-PARTY-LICENSES.txt +0 -772
- package/dist/cvt-gl-worker.js +0 -9274
- package/dist/cvt-gl-worker.js.map +0 -1
- package/dist/cvt-gl.js +0 -13706
- package/dist/cvt-gl.js.map +0 -1
- package/dist/cvt-gl.min.js +0 -135
- package/dist/cvt-gl.min.js.map +0 -1
package/README.md
CHANGED
|
@@ -13,6 +13,7 @@
|
|
|
13
13
|
- ✅ **GPU 精准剔除**:利用 FBO + RTT 技术生成瓦片 ID 纹理,在 GPU 中高效剔除不可见矢量片段
|
|
14
14
|
- ✅ **高度可扩展**:提供清晰接口,轻松接入新数据源(如 TopoJSON)或自定义图层类型(如热力图、流线)
|
|
15
15
|
- ✅ **文字符号自动避让**:使用 `maplibre-gl` `GridIndex`类,结合 Cesium.Label 屏幕空间位置和包围盒计算API,实现符号碰撞检测(自动避让)
|
|
16
|
+
- ✅ **样式动态修改**:提供`setLayoutProperty`、`setPaintProperty`、`setFilter`等`样式修改API`,可动态控制图层`显隐`、`颜色`、`透明度`
|
|
16
17
|
|
|
17
18
|
#### 🧠 技术栈亮点
|
|
18
19
|
|
|
@@ -83,6 +84,34 @@ const tileset = new VectorTileset({
|
|
|
83
84
|
viewer.scene.primitives.add(tileset)
|
|
84
85
|
```
|
|
85
86
|
|
|
87
|
+
### 样式修改示例
|
|
88
|
+
|
|
89
|
+
```js
|
|
90
|
+
tileset.readyEvent.addEventListener(() => {
|
|
91
|
+
// 1、修改绘制属性(颜色、透明度),不会触发强制刷新,实时变化
|
|
92
|
+
tileset.setPaintProperty('background', 'background-color', 'skyblue')
|
|
93
|
+
tileset.setPaintProperty('geolines', 'line-color', 'green')
|
|
94
|
+
tileset.setPaintProperty('us_states:fill', 'fill-color', 'red')
|
|
95
|
+
tileset.setPaintProperty('countries-label', 'text-color', 'blue')
|
|
96
|
+
|
|
97
|
+
// 2、修改布局属性,除 visibility 外,都会触发强制刷新
|
|
98
|
+
// 2.1、布局属性修改:图层显隐,不会触发强制刷新
|
|
99
|
+
tileset.setLayoutProperty('coastline', 'visibility', 'none')
|
|
100
|
+
// 2.2、布局属性修改(非visibility):字体大小修改,会触发强制刷新
|
|
101
|
+
tileset.setLayoutProperty('countries-label', 'text-size', {
|
|
102
|
+
stops: [
|
|
103
|
+
[2, 12],
|
|
104
|
+
[4, 16],
|
|
105
|
+
[6, 20]
|
|
106
|
+
]
|
|
107
|
+
})
|
|
108
|
+
|
|
109
|
+
// 3、修改过滤器。
|
|
110
|
+
//和setLayoutProperty一样(visibility例外)会触发强制刷新
|
|
111
|
+
tileset.setFilter('geolines', null)
|
|
112
|
+
})
|
|
113
|
+
```
|
|
114
|
+
|
|
86
115
|
### 可选:Web Worker 多线程瓦片处理
|
|
87
116
|
|
|
88
117
|
将瓦片解析、坐标转换、几何构建放到子线程,减轻主线程卡顿。不传 `workerUrl` 时仍走主线程。
|
package/Source/VectorTileset.js
CHANGED
|
@@ -33,6 +33,7 @@ export class VectorTileset {
|
|
|
33
33
|
this._tilesToRender = []
|
|
34
34
|
/**@type {StyleLayer[]} */
|
|
35
35
|
this._styleLayers = []
|
|
36
|
+
this._styleLayerIndexMap = new Map()
|
|
36
37
|
/**@type {VectorTileRenderList} */
|
|
37
38
|
this._renderList = new VectorTileRenderList(this._styleLayers)
|
|
38
39
|
this.numLoading = 0
|
|
@@ -95,6 +96,7 @@ export class VectorTileset {
|
|
|
95
96
|
//初始化样式图层
|
|
96
97
|
for (let i = 0; i < style.layers.length; i++) {
|
|
97
98
|
this._styleLayers[i] = new StyleLayer(style.layers[i])
|
|
99
|
+
this._styleLayerIndexMap.set(style.layers[i].id, i)
|
|
98
100
|
}
|
|
99
101
|
|
|
100
102
|
//创建顶级瓦片LOD
|
|
@@ -257,6 +259,65 @@ export class VectorTileset {
|
|
|
257
259
|
}
|
|
258
260
|
}
|
|
259
261
|
|
|
262
|
+
//样式编辑API
|
|
263
|
+
|
|
264
|
+
setLayoutProperty(layerId, name, value) {
|
|
265
|
+
const styleLayerIndexMap = this._styleLayerIndexMap
|
|
266
|
+
if (!styleLayerIndexMap.has(layerId)) {
|
|
267
|
+
warnOnce(`不存在图层:${layerId}`)
|
|
268
|
+
return false
|
|
269
|
+
}
|
|
270
|
+
const layerIndex = styleLayerIndexMap.get(layerId)
|
|
271
|
+
const styleLayer = this._styleLayers[layerIndex]
|
|
272
|
+
const changed = styleLayer.setLayoutProperty(name, value)
|
|
273
|
+
//强制更新
|
|
274
|
+
if (changed && name !== 'visibility') {
|
|
275
|
+
this._forceUpdate()
|
|
276
|
+
}
|
|
277
|
+
return changed
|
|
278
|
+
}
|
|
279
|
+
|
|
280
|
+
setPaintProperty(layerId, name, value) {
|
|
281
|
+
const styleLayerIndexMap = this._styleLayerIndexMap
|
|
282
|
+
if (!styleLayerIndexMap.has(layerId)) {
|
|
283
|
+
warnOnce(`不存在图层:${layerId}`)
|
|
284
|
+
return false
|
|
285
|
+
}
|
|
286
|
+
const layerIndex = styleLayerIndexMap.get(layerId)
|
|
287
|
+
const styleLayer = this._styleLayers[layerIndex]
|
|
288
|
+
return styleLayer.setPaintProperty(name, value)
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
setFilter(layerId, filter) {
|
|
292
|
+
const styleLayerIndexMap = this._styleLayerIndexMap
|
|
293
|
+
if (!styleLayerIndexMap.has(layerId)) {
|
|
294
|
+
warnOnce(`不存在图层:${layerId}`)
|
|
295
|
+
return false
|
|
296
|
+
}
|
|
297
|
+
const layerIndex = styleLayerIndexMap.get(layerId)
|
|
298
|
+
const styleLayer = this._styleLayers[layerIndex]
|
|
299
|
+
const changed = styleLayer.setFilter(filter)
|
|
300
|
+
if (changed) {
|
|
301
|
+
this._forceUpdate()
|
|
302
|
+
}
|
|
303
|
+
return changed
|
|
304
|
+
}
|
|
305
|
+
|
|
306
|
+
//强制更新
|
|
307
|
+
_forceUpdate() {
|
|
308
|
+
for (const cacheTile of this._cacheTiles) {
|
|
309
|
+
cacheTile.unload()
|
|
310
|
+
}
|
|
311
|
+
for (const cacheTile of this._tilesToRender) {
|
|
312
|
+
cacheTile.unload()
|
|
313
|
+
}
|
|
314
|
+
for (const cacheTile of this._tilesToUpdate) {
|
|
315
|
+
cacheTile.unload()
|
|
316
|
+
}
|
|
317
|
+
this._tilesToRender.length = 0
|
|
318
|
+
this._tilesToUpdate.length = 0
|
|
319
|
+
}
|
|
320
|
+
|
|
260
321
|
destroy() {
|
|
261
322
|
const scene = this.scene
|
|
262
323
|
const rootTiles = this._rootTiles
|
|
@@ -80,6 +80,21 @@ export class BackgroundRenderLayer extends IRenderLayer {
|
|
|
80
80
|
|
|
81
81
|
frameState.commandList = preCommandList
|
|
82
82
|
}
|
|
83
|
+
if (this.primitive && this.paintNeedsUpdate) {
|
|
84
|
+
const style = this.style,
|
|
85
|
+
tile = this.tile
|
|
86
|
+
const color = style.convertColor(
|
|
87
|
+
style.paint.getDataConstValue('background-color', tile.z)
|
|
88
|
+
)
|
|
89
|
+
const opacity = style.paint.getDataConstValue(
|
|
90
|
+
'background-opacity',
|
|
91
|
+
tile.z
|
|
92
|
+
)
|
|
93
|
+
color.alpha *= opacity
|
|
94
|
+
this.primitive.appearance.material.uniforms.color = color
|
|
95
|
+
|
|
96
|
+
this.paintNeedsUpdate = false
|
|
97
|
+
}
|
|
83
98
|
}
|
|
84
99
|
|
|
85
100
|
destroy() {
|
|
@@ -4,13 +4,47 @@ 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
|
|
10
15
|
*/
|
|
11
16
|
update(frameState, tileset) {
|
|
12
17
|
//可以在这里实现同步样式,动态更新图层颜色等样式
|
|
18
|
+
if (this.paintNeedsUpdate) {
|
|
19
|
+
const style = this.style,
|
|
20
|
+
tile = this.tile,
|
|
21
|
+
batchTable = this._batchTable
|
|
22
|
+
|
|
23
|
+
for (const feature of this.features) {
|
|
24
|
+
const fillColor = style.convertColor(
|
|
25
|
+
style.paint.getDataValue('fill-color', tile.z, feature)
|
|
26
|
+
)
|
|
27
|
+
const fillOpacity = style.paint.getDataValue(
|
|
28
|
+
'fill-opacity',
|
|
29
|
+
tile.z,
|
|
30
|
+
feature
|
|
31
|
+
)
|
|
32
|
+
feature.fillColor = fillColor
|
|
33
|
+
feature.fillOpacity = fillOpacity
|
|
34
|
+
|
|
35
|
+
const batchId = feature.batchId
|
|
36
|
+
const colorBytes = fillColor.toBytes()
|
|
37
|
+
colorBytes[3] = Math.floor(colorBytes[3] * fillOpacity)
|
|
38
|
+
batchTable.setBatchedAttribute(batchId, 0, {
|
|
39
|
+
x: colorBytes[0],
|
|
40
|
+
y: colorBytes[1],
|
|
41
|
+
z: colorBytes[2],
|
|
42
|
+
w: colorBytes[3]
|
|
43
|
+
})
|
|
44
|
+
}
|
|
13
45
|
|
|
46
|
+
this.paintNeedsUpdate = false
|
|
47
|
+
}
|
|
14
48
|
super.update(frameState, tileset)
|
|
15
49
|
}
|
|
16
50
|
}
|
|
@@ -82,6 +82,10 @@ export class IRenderLayer {
|
|
|
82
82
|
* @type {'none'|'done'|'error'}
|
|
83
83
|
*/
|
|
84
84
|
this.state = 'none'
|
|
85
|
+
/**
|
|
86
|
+
* 绘制(paint)属性版本号,用于监听绘制属性变化
|
|
87
|
+
*/
|
|
88
|
+
this.paintVersion = styleLayer.paintVersion
|
|
85
89
|
}
|
|
86
90
|
|
|
87
91
|
get id() {
|
|
@@ -92,6 +96,15 @@ export class IRenderLayer {
|
|
|
92
96
|
return this.style.type
|
|
93
97
|
}
|
|
94
98
|
|
|
99
|
+
get paintNeedsUpdate() {
|
|
100
|
+
return this.paintVersion !== this.style.paintVersion
|
|
101
|
+
}
|
|
102
|
+
set paintNeedsUpdate(val) {
|
|
103
|
+
if (!val) {
|
|
104
|
+
this.paintVersion = this.style.paintVersion
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
95
108
|
/**
|
|
96
109
|
* 更渲染图层,可在该方法内实现绘图命令构建、动态样式更新等图层渲染准备相关功能。该方法可以被子类重写或复用
|
|
97
110
|
* @param {Cesium.FrameState} frameState
|
|
@@ -5,6 +5,7 @@ import { IRenderLayer } from './IRenderLayer'
|
|
|
5
5
|
import { registerRenderLayer } from './registerRenderLayer'
|
|
6
6
|
import { VectorTileLOD } from '../VectorTileLOD'
|
|
7
7
|
import { LineLayerVisualizer } from './visualizers/LineLayerVisualizer'
|
|
8
|
+
import { warnOnce } from 'maplibre-gl/src/util/util'
|
|
8
9
|
|
|
9
10
|
export class LineRenderLayer extends IRenderLayer {
|
|
10
11
|
/**
|
|
@@ -91,7 +92,49 @@ export class LineRenderLayer extends IRenderLayer {
|
|
|
91
92
|
// }
|
|
92
93
|
// if (this.primitive && this.primitive.length) {
|
|
93
94
|
// this.primitive.update(frameState)
|
|
95
|
+
// this._batchTable = primitive._batchTable
|
|
94
96
|
// }
|
|
97
|
+
if (this.paintNeedsUpdate) {
|
|
98
|
+
const style = this.style,
|
|
99
|
+
tile = this.tile,
|
|
100
|
+
batchTable = this._batchTable
|
|
101
|
+
|
|
102
|
+
for (const feature of this.features) {
|
|
103
|
+
const lineWidth = style.paint.getDataValue(
|
|
104
|
+
'line-width',
|
|
105
|
+
tile.z,
|
|
106
|
+
feature
|
|
107
|
+
)
|
|
108
|
+
const lineColor = style.convertColor(
|
|
109
|
+
style.paint.getDataValue('line-color', tile.z, feature)
|
|
110
|
+
)
|
|
111
|
+
const lineOpacity = style.paint.getDataValue(
|
|
112
|
+
'line-opacity',
|
|
113
|
+
tile.z,
|
|
114
|
+
feature
|
|
115
|
+
)
|
|
116
|
+
|
|
117
|
+
if (lineWidth !== feature.lineWidth) {
|
|
118
|
+
warnOnce('不支持动态修改 line 图层样式属性:line-width')
|
|
119
|
+
}
|
|
120
|
+
|
|
121
|
+
feature.lineColor = lineColor
|
|
122
|
+
feature.lineOpacity = lineOpacity
|
|
123
|
+
feature.lineWidth = lineWidth
|
|
124
|
+
|
|
125
|
+
const batchId = feature.batchId
|
|
126
|
+
const colorBytes = lineColor.toBytes()
|
|
127
|
+
colorBytes[3] = Math.floor(colorBytes[3] * lineOpacity)
|
|
128
|
+
batchTable.setBatchedAttribute(batchId, 0, {
|
|
129
|
+
x: colorBytes[0],
|
|
130
|
+
y: colorBytes[1],
|
|
131
|
+
z: colorBytes[2],
|
|
132
|
+
w: colorBytes[3]
|
|
133
|
+
})
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
this.paintNeedsUpdate = false
|
|
137
|
+
}
|
|
95
138
|
super.update(frameState, tileset)
|
|
96
139
|
}
|
|
97
140
|
|
|
@@ -22,6 +22,41 @@ export class SymbolRenderLayer extends IRenderLayer {
|
|
|
22
22
|
*/
|
|
23
23
|
update(frameState, tileset) {
|
|
24
24
|
//TODO:动态更新符号样式
|
|
25
|
+
if (this.paintNeedsUpdate) {
|
|
26
|
+
const style = this.style,
|
|
27
|
+
tile = this.tile
|
|
28
|
+
|
|
29
|
+
for (let i = 0; i < this.features.length; i++) {
|
|
30
|
+
const feature = this.features[i]
|
|
31
|
+
const label = this.labels[i]
|
|
32
|
+
|
|
33
|
+
const textColor = style.convertColor(
|
|
34
|
+
style.paint.getDataValue('text-color', tile.z, feature)
|
|
35
|
+
)
|
|
36
|
+
const outlineColor = style.convertColor(
|
|
37
|
+
style.paint.getDataValue('text-halo-color', tile.z, feature)
|
|
38
|
+
)
|
|
39
|
+
const outlineWidth = style.paint.getDataValue(
|
|
40
|
+
'text-halo-width',
|
|
41
|
+
tile.z,
|
|
42
|
+
feature
|
|
43
|
+
)
|
|
44
|
+
|
|
45
|
+
feature.textColor = textColor
|
|
46
|
+
feature.outlineColor = outlineColor
|
|
47
|
+
feature.outlineWidth = outlineWidth
|
|
48
|
+
|
|
49
|
+
label.fillColor = textColor
|
|
50
|
+
label.style = outlineWidth && Cesium.LabelStyle.FILL_AND_OUTLINE
|
|
51
|
+
label.outlineWidth = outlineWidth * feature.textSize
|
|
52
|
+
label.outlineColor = outlineColor
|
|
53
|
+
|
|
54
|
+
label._baseFillColor = label.fillColor.clone()
|
|
55
|
+
label._baseOutlineColor = label.outlineColor.clone()
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
this.paintNeedsUpdate = false
|
|
59
|
+
}
|
|
25
60
|
|
|
26
61
|
super.update(frameState, tileset)
|
|
27
62
|
}
|
|
@@ -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,
|
|
@@ -92,6 +99,7 @@ export class FillLayerVisualizer extends ILayerVisualizer {
|
|
|
92
99
|
}
|
|
93
100
|
|
|
94
101
|
scope.addFeature(fillFeature, granularity)
|
|
102
|
+
layer.features.push(fillFeature)
|
|
95
103
|
|
|
96
104
|
featureId++
|
|
97
105
|
}
|
|
@@ -186,9 +194,12 @@ export class FillLayerVisualizer extends ILayerVisualizer {
|
|
|
186
194
|
*/
|
|
187
195
|
addFeature(feature, granularity) {
|
|
188
196
|
const geometryInstances = this.geometryInstances
|
|
189
|
-
const
|
|
197
|
+
const outlineGeometryInstances = this.outlineGeometryInstances
|
|
198
|
+
const { coordinates, fillColor, fillOpacity, fillOutlineColor } = feature
|
|
190
199
|
const colorBytes = fillColor.toBytes()
|
|
191
200
|
colorBytes[3] = Math.floor(colorBytes[3] * fillOpacity)
|
|
201
|
+
const outlineColorBytes = fillOutlineColor.toBytes()
|
|
202
|
+
outlineColorBytes[3] = Math.floor(outlineColorBytes[3] * fillOpacity)
|
|
192
203
|
|
|
193
204
|
// 使用 maplibre-gl 的 subdividePolygon 基于投影坐标进行细分,
|
|
194
205
|
// 而不是在转成世界坐标后再使用 Cesium.PolygonGeometry 构建,这样才能避免出现自相交、破面等现象。
|
|
@@ -198,7 +209,7 @@ export class FillLayerVisualizer extends ILayerVisualizer {
|
|
|
198
209
|
coordinates,
|
|
199
210
|
this.tile,
|
|
200
211
|
granularity,
|
|
201
|
-
|
|
212
|
+
true
|
|
202
213
|
)
|
|
203
214
|
const verticesFlattened = subdivisionRes.verticesFlattened
|
|
204
215
|
const coordDeg = [0, 0],
|
|
@@ -240,6 +251,14 @@ export class FillLayerVisualizer extends ILayerVisualizer {
|
|
|
240
251
|
: Uint8Array
|
|
241
252
|
)(subdivisionRes.indicesTriangles)
|
|
242
253
|
|
|
254
|
+
const outlineIndices = new (
|
|
255
|
+
vertCount > 65535
|
|
256
|
+
? Uint32Array
|
|
257
|
+
: vertCount > 255
|
|
258
|
+
? Uint16Array
|
|
259
|
+
: Uint8Array
|
|
260
|
+
)(subdivisionRes.indicesLineList.flat(3))
|
|
261
|
+
|
|
243
262
|
const geometry = new Cesium.Geometry({
|
|
244
263
|
attributes: {
|
|
245
264
|
position: {
|
|
@@ -265,6 +284,19 @@ export class FillLayerVisualizer extends ILayerVisualizer {
|
|
|
265
284
|
indices: indices,
|
|
266
285
|
boundingSphere: Cesium.BoundingSphere.fromVertices(positions)
|
|
267
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
|
+
})
|
|
268
300
|
|
|
269
301
|
const cartographic = Cesium.Cartographic.fromCartesian(
|
|
270
302
|
geometry.boundingSphere.center
|
|
@@ -294,6 +326,25 @@ export class FillLayerVisualizer extends ILayerVisualizer {
|
|
|
294
326
|
})
|
|
295
327
|
})
|
|
296
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)
|
|
297
348
|
}
|
|
298
349
|
|
|
299
350
|
createPrimitive() {
|
|
@@ -427,6 +478,143 @@ void main()
|
|
|
427
478
|
*/
|
|
428
479
|
onBatchTableCreated(batchTable) {
|
|
429
480
|
this._batchTable = batchTable
|
|
481
|
+
for (const layer of this.layers) {
|
|
482
|
+
layer._batchTable = batchTable
|
|
483
|
+
}
|
|
484
|
+
}
|
|
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
|
+
}
|
|
430
618
|
}
|
|
431
619
|
|
|
432
620
|
/**
|
|
@@ -452,14 +640,29 @@ void main()
|
|
|
452
640
|
for (let i = 0; i < this.layers.length; i++) {
|
|
453
641
|
const layer = this.layers[i]
|
|
454
642
|
const layerCommandList = (layer.commandList = [])
|
|
643
|
+
const passes = {
|
|
644
|
+
fill: 0,
|
|
645
|
+
outline: 0
|
|
646
|
+
}
|
|
455
647
|
|
|
456
|
-
for (let
|
|
457
|
-
const
|
|
458
|
-
|
|
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
|
+
}
|
|
660
|
+
|
|
661
|
+
const offset = offsets[pass],
|
|
662
|
+
count = counts[pass]
|
|
459
663
|
if (typeof offset !== 'number' || typeof count !== 'number') {
|
|
460
664
|
continue
|
|
461
665
|
}
|
|
462
|
-
const command = batchedCommandList[pass]
|
|
463
666
|
command.uniformMap.tileIdTexture = function () {
|
|
464
667
|
return tileset.tileIdTexture
|
|
465
668
|
}
|
|
@@ -493,6 +696,7 @@ void main()
|
|
|
493
696
|
|
|
494
697
|
if (!this.primitive && this.geometryInstances.length) {
|
|
495
698
|
this.createPrimitive()
|
|
699
|
+
this.createOutlinePrimitive()
|
|
496
700
|
}
|
|
497
701
|
|
|
498
702
|
if (this.primitive && this.state !== 'done' && this.state !== 'error') {
|
|
@@ -504,13 +708,18 @@ void main()
|
|
|
504
708
|
//执行 primitive 的 update ,直到生成了合批后的drawCommand
|
|
505
709
|
try {
|
|
506
710
|
this.primitive.update(frameState)
|
|
711
|
+
this.outlinePrimitive.update(frameState)
|
|
507
712
|
} catch (err) {
|
|
508
713
|
//如果报错,下一帧就不要再执行 update 了,以免重复打印错误信息
|
|
509
714
|
this.geometryInstances = []
|
|
715
|
+
this.outlineGeometryInstances = []
|
|
510
716
|
this.setState('error')
|
|
511
717
|
if (err.stack) console.trace(err.stack)
|
|
512
718
|
else console.error(err)
|
|
513
719
|
return
|
|
720
|
+
} finally {
|
|
721
|
+
//恢复系统的 commandList
|
|
722
|
+
frameState.commandList = preCommandList
|
|
514
723
|
}
|
|
515
724
|
|
|
516
725
|
//使用合批后的 drawCommand 创建副本,为渲染图层分配 drawCommand
|
|
@@ -518,14 +727,22 @@ void main()
|
|
|
518
727
|
this.createLayerCommands(batchedCommandList, tileset)
|
|
519
728
|
}
|
|
520
729
|
|
|
521
|
-
if (
|
|
730
|
+
if (
|
|
731
|
+
this.primitive._state === Cesium.PrimitiveState.FAILED ||
|
|
732
|
+
this.outlinePrimitive._state === Cesium.PrimitiveState.FAILED
|
|
733
|
+
) {
|
|
522
734
|
this.setState('error')
|
|
523
735
|
}
|
|
524
736
|
|
|
525
|
-
//恢复系统的 commandList
|
|
526
|
-
frameState.commandList = preCommandList
|
|
527
737
|
this.geometryInstances = []
|
|
528
738
|
}
|
|
739
|
+
|
|
740
|
+
if (this._batchTable && this._batchTable._batchValuesDirty) {
|
|
741
|
+
this._batchTable.update(frameState)
|
|
742
|
+
}
|
|
743
|
+
if (this._outlineBatchTable && this._outlineBatchTable._batchValuesDirty) {
|
|
744
|
+
this._outlineBatchTable.update(frameState)
|
|
745
|
+
}
|
|
529
746
|
}
|
|
530
747
|
|
|
531
748
|
destroy() {
|