@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.
- package/.gitattributes +11 -0
- package/.gitconfig +3 -0
- package/.husky/pre-commit +1 -0
- package/.prettierignore +5 -0
- package/.vscode/settings.json +25 -0
- package/LICENSE.md +203 -203
- package/README.md +202 -167
- package/Source/Cesium.d.ts +2692 -2691
- package/Source/VectorTileLOD.js +720 -532
- package/Source/VectorTileRenderList.js +70 -70
- package/Source/VectorTileset.js +473 -447
- package/Source/layers/BackgroundRenderLayer.js +91 -89
- package/Source/layers/FillRenderLayer.js +18 -18
- package/Source/layers/IRenderLayer.js +160 -152
- package/Source/layers/LineRenderLayer.js +104 -94
- package/Source/layers/SymbolRenderLayer.js +30 -31
- package/Source/layers/index.js +23 -16
- package/Source/layers/registerRenderLayer.js +24 -24
- package/Source/layers/visualizers/FillLayerVisualizer.js +542 -426
- package/Source/layers/visualizers/ILayerVisualizer.js +90 -94
- package/Source/layers/visualizers/LineLayerVisualizer.js +702 -571
- package/Source/layers/visualizers/SymbolLayerVisualizer.js +514 -244
- package/Source/sources/GeoJSONSource.js +53 -46
- package/Source/sources/ISource.js +39 -39
- package/Source/sources/VectorSource.js +94 -52
- package/Source/sources/granularitySettings.js +23 -20
- package/Source/sources/index.js +6 -11
- package/Source/sources/registerSource.js +17 -19
- package/Source/style/StyleLayer.js +43 -43
- package/Source/style/StyleLayerProperties.js +44 -43
- package/Source/style/index.js +2 -2
- package/Source/symbol/SymbolPlacements.js +117 -88
- package/Source/workers/VectorTileWorker.js +41 -0
- package/Source/workers/ellipsoid.js +47 -0
- package/Source/workers/processTileTask.js +329 -0
- package/Source/workers/styleEvaluator.js +168 -0
- package/benchmark.html +148 -0
- package/dist/cvt-gl-worker.js +9274 -0
- package/dist/cvt-gl-worker.js.map +1 -0
- package/dist/cvt-gl.js +2570 -2001
- package/dist/cvt-gl.js.map +1 -1
- package/dist/cvt-gl.min.js +3 -3
- package/dist/cvt-gl.min.js.map +1 -1
- package/eslint.config.mjs +58 -0
- package/index.js +9 -6
- package/mlt.html +26 -25
- package/package.json +64 -41
- package/prettier.config.mjs +30 -0
- package/vite.config.mjs +43 -0
- package/vite.worker.config.mjs +31 -0
- package/worker.html +26 -0
|
@@ -0,0 +1,168 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* Worker 内样式求值:paint 表达式求值、颜色转 [r,g,b,a] 字节。
|
|
3
|
+
* 不依赖 Cesium,仅使用 @maplibre/maplibre-gl-style-spec。
|
|
4
|
+
*/
|
|
5
|
+
import { expression, latest } from '@maplibre/maplibre-gl-style-spec'
|
|
6
|
+
|
|
7
|
+
const paintFillRef = latest.paint_fill
|
|
8
|
+
const paintLineRef = latest.paint_line
|
|
9
|
+
const layoutSymbolRef = latest.layout_symbol
|
|
10
|
+
const paintSymbolRef = latest.paint_symbol
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* 构建图层 paint 的表达式 Map(仅包含需要的 key)
|
|
14
|
+
* @param {object} paint - 原始 paint 对象
|
|
15
|
+
* @param {object} groupRef - latest.paint_fill 或 paint_line
|
|
16
|
+
* @returns {Map<string, object>}
|
|
17
|
+
*/
|
|
18
|
+
function buildPaintProps(paint, groupRef) {
|
|
19
|
+
const props = new Map()
|
|
20
|
+
if (!paint || !groupRef) return props
|
|
21
|
+
for (const key of Object.keys(groupRef)) {
|
|
22
|
+
const reference = groupRef[key]
|
|
23
|
+
const value = paint[key]
|
|
24
|
+
const property = expression.normalizePropertyExpression(
|
|
25
|
+
value === undefined ? reference.default : value,
|
|
26
|
+
reference
|
|
27
|
+
)
|
|
28
|
+
props.set(key, property)
|
|
29
|
+
}
|
|
30
|
+
return props
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
/**
|
|
34
|
+
* 求值 paint 属性
|
|
35
|
+
* @param {Map<string, object>} props
|
|
36
|
+
* @param {string} name
|
|
37
|
+
* @param {number} zoom
|
|
38
|
+
* @param {object} feature
|
|
39
|
+
* @returns {*}
|
|
40
|
+
*/
|
|
41
|
+
function getDataValue(props, name, zoom, feature) {
|
|
42
|
+
const expr = props.get(name)
|
|
43
|
+
return expr ? expr.evaluate({ zoom }, feature) : undefined
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
/**
|
|
47
|
+
* Fill 图层 paint 求值(fill-color, fill-opacity)
|
|
48
|
+
* @param {object} styleLayer - 可序列化的 styleLayer(含 paint)
|
|
49
|
+
* @param {number} zoom
|
|
50
|
+
* @param {object} feature
|
|
51
|
+
* @returns {{ fillColor: { r, g, b, a }, fillOpacity: number }}
|
|
52
|
+
*/
|
|
53
|
+
export function evaluateFillPaint(styleLayer, zoom, feature) {
|
|
54
|
+
const props = buildPaintProps(styleLayer.paint, paintFillRef)
|
|
55
|
+
const fillColor = getDataValue(props, 'fill-color', zoom, feature)
|
|
56
|
+
const fillOpacity = getDataValue(props, 'fill-opacity', zoom, feature) ?? 1
|
|
57
|
+
return {
|
|
58
|
+
fillColor: fillColor || { r: 0, g: 0, b: 0, a: 1 },
|
|
59
|
+
fillOpacity
|
|
60
|
+
}
|
|
61
|
+
}
|
|
62
|
+
|
|
63
|
+
/**
|
|
64
|
+
* Line 图层 paint 求值(line-width, line-color, line-opacity)
|
|
65
|
+
* @param {object} styleLayer
|
|
66
|
+
* @param {number} zoom
|
|
67
|
+
* @param {object} feature
|
|
68
|
+
* @returns {{ lineWidth: number, lineColor: object, lineOpacity: number }}
|
|
69
|
+
*/
|
|
70
|
+
export function evaluateLinePaint(styleLayer, zoom, feature) {
|
|
71
|
+
const props = buildPaintProps(styleLayer.paint, paintLineRef)
|
|
72
|
+
const lineWidth = getDataValue(props, 'line-width', zoom, feature) ?? 1
|
|
73
|
+
const lineColor = getDataValue(props, 'line-color', zoom, feature) || {
|
|
74
|
+
r: 0,
|
|
75
|
+
g: 0,
|
|
76
|
+
b: 0,
|
|
77
|
+
a: 1
|
|
78
|
+
}
|
|
79
|
+
const lineOpacity = getDataValue(props, 'line-opacity', zoom, feature) ?? 1
|
|
80
|
+
return { lineWidth, lineColor, lineOpacity }
|
|
81
|
+
}
|
|
82
|
+
|
|
83
|
+
/**
|
|
84
|
+
* maplibre Color (r,g,b,a 0-1) → Uint8Array [r,g,b,a],与主线程 StyleLayer.convertColor 一致(premultiply 逆处理)
|
|
85
|
+
* @param {object} c - { r, g, b, a }
|
|
86
|
+
* @param {number} [opacity=1] - 额外透明度(如 fillOpacity/lineOpacity)
|
|
87
|
+
* @returns {Uint8Array}
|
|
88
|
+
*/
|
|
89
|
+
export function colorToBytes(c, opacity = 1) {
|
|
90
|
+
const a = c.a != null ? c.a : 1
|
|
91
|
+
const alphaScalar = a > 0 ? 1 / a : 1
|
|
92
|
+
const out = new Uint8Array(4)
|
|
93
|
+
out[0] = Math.round((c.r != null ? c.r : 0) * alphaScalar * 255)
|
|
94
|
+
out[1] = Math.round((c.g != null ? c.g : 0) * alphaScalar * 255)
|
|
95
|
+
out[2] = Math.round((c.b != null ? c.b : 0) * alphaScalar * 255)
|
|
96
|
+
out[3] = Math.floor(a * opacity * 255)
|
|
97
|
+
return out
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
/**
|
|
101
|
+
* 构建 layout/paint 的表达式 Map(symbol 用)
|
|
102
|
+
*/
|
|
103
|
+
function buildProps(styleProperties, groupRef) {
|
|
104
|
+
const props = new Map()
|
|
105
|
+
if (!styleProperties || !groupRef) return props
|
|
106
|
+
for (const key of Object.keys(groupRef)) {
|
|
107
|
+
const reference = groupRef[key]
|
|
108
|
+
const value = styleProperties[key]
|
|
109
|
+
const property = expression.normalizePropertyExpression(
|
|
110
|
+
value === undefined ? reference.default : value,
|
|
111
|
+
reference
|
|
112
|
+
)
|
|
113
|
+
props.set(key, property)
|
|
114
|
+
}
|
|
115
|
+
return props
|
|
116
|
+
}
|
|
117
|
+
|
|
118
|
+
/**
|
|
119
|
+
* Symbol 图层 layout 求值(text-field, text-font, text-size, text-anchor, text-offset 等)
|
|
120
|
+
*/
|
|
121
|
+
export function evaluateSymbolLayout(styleLayer, zoom, feature) {
|
|
122
|
+
const layout = styleLayer.layout || {}
|
|
123
|
+
const props = buildProps(layout, layoutSymbolRef)
|
|
124
|
+
const get = name => getDataValue(props, name, zoom, feature)
|
|
125
|
+
const textField = get('text-field')
|
|
126
|
+
let text = textField
|
|
127
|
+
if (typeof text === 'string') {
|
|
128
|
+
text = resolveTokens(feature.properties || {}, text)
|
|
129
|
+
} else if (text && text.sections) {
|
|
130
|
+
for (const section of text.sections) {
|
|
131
|
+
section.text = resolveTokens(feature.properties || {}, section.text)
|
|
132
|
+
}
|
|
133
|
+
text = text.toString()
|
|
134
|
+
}
|
|
135
|
+
const textTransform = get('text-transform')
|
|
136
|
+
if (textTransform === 'uppercase') text = String(text).toUpperCase()
|
|
137
|
+
else if (textTransform === 'lowercase') text = String(text).toLowerCase()
|
|
138
|
+
return {
|
|
139
|
+
text: text || '',
|
|
140
|
+
font: get('text-font') || 'Open Sans Regular, Arial Unicode MS Regular',
|
|
141
|
+
textSize: get('text-size') ?? 16,
|
|
142
|
+
textAnchor: get('text-anchor') || 'center',
|
|
143
|
+
textOffset: get('text-offset') || [0, 0]
|
|
144
|
+
}
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
/**
|
|
148
|
+
* Symbol 图层 paint 求值(text-color, text-halo-color, text-halo-width)
|
|
149
|
+
*/
|
|
150
|
+
export function evaluateSymbolPaint(styleLayer, zoom, feature) {
|
|
151
|
+
const paint = styleLayer.paint || {}
|
|
152
|
+
const props = buildProps(paint, paintSymbolRef)
|
|
153
|
+
const get = name => getDataValue(props, name, zoom, feature)
|
|
154
|
+
const textColor = get('text-color') || { r: 0, g: 0, b: 0, a: 1 }
|
|
155
|
+
const haloColor = get('text-halo-color') || { r: 0, g: 0, b: 0, a: 1 }
|
|
156
|
+
const haloWidth = get('text-halo-width') ?? 0
|
|
157
|
+
return {
|
|
158
|
+
textColor,
|
|
159
|
+
outlineColor: haloColor,
|
|
160
|
+
outlineWidth: haloWidth
|
|
161
|
+
}
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
function resolveTokens(properties, text) {
|
|
165
|
+
return String(text).replace(/\{([^{}]+)\}/g, (match, key) =>
|
|
166
|
+
properties && key in properties ? String(properties[key]) : ''
|
|
167
|
+
)
|
|
168
|
+
}
|
package/benchmark.html
ADDED
|
@@ -0,0 +1,148 @@
|
|
|
1
|
+
<html lang="zh-cn">
|
|
2
|
+
<head>
|
|
3
|
+
<meta charset="UTF-8" />
|
|
4
|
+
<meta name="viewport" content="width=device-width, initial-scale=1.0" />
|
|
5
|
+
<title>Worker 与主线程性能对比</title>
|
|
6
|
+
<link
|
|
7
|
+
rel="stylesheet"
|
|
8
|
+
href="./node_modules/cesium/Build/Cesium/Widgets/widgets.css"
|
|
9
|
+
/>
|
|
10
|
+
<script src="./node_modules/cesium/Build/CesiumUnminified/Cesium.js"></script>
|
|
11
|
+
<style>
|
|
12
|
+
html,
|
|
13
|
+
body {
|
|
14
|
+
margin: 0;
|
|
15
|
+
padding: 0;
|
|
16
|
+
font-family: sans-serif;
|
|
17
|
+
}
|
|
18
|
+
#toolbar {
|
|
19
|
+
position: fixed;
|
|
20
|
+
top: 0;
|
|
21
|
+
left: 0;
|
|
22
|
+
right: 0;
|
|
23
|
+
padding: 8px 12px;
|
|
24
|
+
background: #1a1a2e;
|
|
25
|
+
color: #eee;
|
|
26
|
+
z-index: 100;
|
|
27
|
+
display: flex;
|
|
28
|
+
align-items: center;
|
|
29
|
+
gap: 12px;
|
|
30
|
+
}
|
|
31
|
+
#toolbar button {
|
|
32
|
+
padding: 8px 16px;
|
|
33
|
+
background: #16213e;
|
|
34
|
+
border: 1px solid #0f3460;
|
|
35
|
+
color: #eee;
|
|
36
|
+
cursor: pointer;
|
|
37
|
+
border-radius: 4px;
|
|
38
|
+
}
|
|
39
|
+
#toolbar button:hover {
|
|
40
|
+
background: #0f3460;
|
|
41
|
+
}
|
|
42
|
+
#bench-row {
|
|
43
|
+
position: fixed;
|
|
44
|
+
top: 48px;
|
|
45
|
+
left: 0;
|
|
46
|
+
right: 0;
|
|
47
|
+
bottom: 200px;
|
|
48
|
+
display: flex;
|
|
49
|
+
gap: 4px;
|
|
50
|
+
}
|
|
51
|
+
.viewer-half {
|
|
52
|
+
flex: 1;
|
|
53
|
+
position: relative;
|
|
54
|
+
min-width: 0;
|
|
55
|
+
}
|
|
56
|
+
.viewer-half .label {
|
|
57
|
+
position: absolute;
|
|
58
|
+
top: 8px;
|
|
59
|
+
left: 8px;
|
|
60
|
+
padding: 4px 8px;
|
|
61
|
+
background: rgba(0, 0, 0, 0.7);
|
|
62
|
+
color: #fff;
|
|
63
|
+
font-size: 12px;
|
|
64
|
+
z-index: 10;
|
|
65
|
+
border-radius: 4px;
|
|
66
|
+
}
|
|
67
|
+
#viewer-main,
|
|
68
|
+
#viewer-worker {
|
|
69
|
+
width: 100%;
|
|
70
|
+
height: 100%;
|
|
71
|
+
}
|
|
72
|
+
#report {
|
|
73
|
+
position: fixed;
|
|
74
|
+
left: 0;
|
|
75
|
+
right: 0;
|
|
76
|
+
bottom: 0;
|
|
77
|
+
height: 200px;
|
|
78
|
+
padding: 12px;
|
|
79
|
+
overflow: auto;
|
|
80
|
+
background: #0d1117;
|
|
81
|
+
color: #c9d1d9;
|
|
82
|
+
font-size: 13px;
|
|
83
|
+
white-space: pre-wrap;
|
|
84
|
+
}
|
|
85
|
+
#report pre {
|
|
86
|
+
margin: 0;
|
|
87
|
+
}
|
|
88
|
+
</style>
|
|
89
|
+
</head>
|
|
90
|
+
|
|
91
|
+
<body>
|
|
92
|
+
<div id="toolbar">
|
|
93
|
+
<button type="button" id="run">运行测试</button>
|
|
94
|
+
<label
|
|
95
|
+
style="display: flex; align-items: center; gap: 6px; cursor: pointer"
|
|
96
|
+
>
|
|
97
|
+
<input type="checkbox" id="swapMode" />
|
|
98
|
+
<span>调换左右模式</span>
|
|
99
|
+
</label>
|
|
100
|
+
<span id="status"
|
|
101
|
+
>左侧=主线程,右侧=Worker。15 秒内各 100 个 Box(边长
|
|
102
|
+
50000)加载与路径漫游。</span
|
|
103
|
+
>
|
|
104
|
+
</div>
|
|
105
|
+
<div id="bench-row">
|
|
106
|
+
<div class="viewer-half">
|
|
107
|
+
<span class="label" id="label-left">主线程</span>
|
|
108
|
+
<div id="viewer-main"></div>
|
|
109
|
+
</div>
|
|
110
|
+
<div class="viewer-half">
|
|
111
|
+
<span class="label" id="label-right">Worker</span>
|
|
112
|
+
<div id="viewer-worker"></div>
|
|
113
|
+
</div>
|
|
114
|
+
</div>
|
|
115
|
+
<div id="report" role="log">
|
|
116
|
+
运行后此处显示左侧主线程与右侧 Worker 的帧时间(分开)及对比结果。
|
|
117
|
+
</div>
|
|
118
|
+
<script type="module">
|
|
119
|
+
import { runBenchmark } from './examples/benchmark.js'
|
|
120
|
+
const runBtn = document.getElementById('run')
|
|
121
|
+
const swapCheckbox = document.getElementById('swapMode')
|
|
122
|
+
const labelLeft = document.getElementById('label-left')
|
|
123
|
+
const labelRight = document.getElementById('label-right')
|
|
124
|
+
const status = document.getElementById('status')
|
|
125
|
+
const reportEl = document.getElementById('report')
|
|
126
|
+
|
|
127
|
+
function updateLabels() {
|
|
128
|
+
const swap = swapCheckbox.checked
|
|
129
|
+
labelLeft.textContent = swap ? 'Worker' : '主线程'
|
|
130
|
+
labelRight.textContent = swap ? '主线程' : 'Worker'
|
|
131
|
+
}
|
|
132
|
+
swapCheckbox.addEventListener('change', updateLabels)
|
|
133
|
+
updateLabels()
|
|
134
|
+
|
|
135
|
+
runBtn.addEventListener('click', async () => {
|
|
136
|
+
runBtn.disabled = true
|
|
137
|
+
const swap = swapCheckbox.checked
|
|
138
|
+
status.textContent = swap
|
|
139
|
+
? '运行中:先左侧 Worker 15 秒,再右侧主线程 15 秒,结果分开并对比…'
|
|
140
|
+
: '运行中:先左侧主线程 15 秒,再右侧 Worker 15 秒,结果分开并对比…'
|
|
141
|
+
reportEl.textContent = ''
|
|
142
|
+
await runBenchmark(swap)
|
|
143
|
+
status.textContent = '测试完成。'
|
|
144
|
+
runBtn.disabled = false
|
|
145
|
+
})
|
|
146
|
+
</script>
|
|
147
|
+
</body>
|
|
148
|
+
</html>
|