@mesh3d/cesium-vectortile-gl 0.1.0
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/LICENSE.md +21 -0
- package/README.md +120 -0
- package/Source/Cesium.d.ts +2692 -0
- package/Source/VectorTileLOD.js +494 -0
- package/Source/VectorTileRenderList.js +65 -0
- package/Source/VectorTileset.js +309 -0
- package/Source/layers/BackgroundRenderLayer.js +82 -0
- package/Source/layers/FillRenderLayer.js +18 -0
- package/Source/layers/IRenderLayer.js +128 -0
- package/Source/layers/LineRenderLayer.js +94 -0
- package/Source/layers/SymbolRenderLayer.js +31 -0
- package/Source/layers/index.js +16 -0
- package/Source/layers/registerRenderLayer.js +24 -0
- package/Source/layers/visualizers/FillLayerVisualizer.js +420 -0
- package/Source/layers/visualizers/ILayerVisualizer.js +73 -0
- package/Source/layers/visualizers/LineLayerVisualizer.js +565 -0
- package/Source/layers/visualizers/SymbolLayerVisualizer.js +179 -0
- package/Source/sources/GeoJSONSource.js +46 -0
- package/Source/sources/ISource.js +39 -0
- package/Source/sources/VectorSource.js +45 -0
- package/Source/sources/granularitySettings.js +20 -0
- package/Source/sources/index.js +11 -0
- package/Source/sources/registerSource.js +19 -0
- package/Source/style/StyleLayer.js +43 -0
- package/Source/style/StyleLayerProperties.js +43 -0
- package/Source/style/index.js +2 -0
- package/dist/cvt-gl.js +10642 -0
- package/dist/cvt-gl.js.map +1 -0
- package/dist/cvt-gl.min.js +135 -0
- package/dist/cvt-gl.min.js.map +1 -0
- package/index.js +6 -0
- package/logo.svg +47 -0
- package/package.json +36 -0
|
@@ -0,0 +1,179 @@
|
|
|
1
|
+
import { VectorTileFeature } from "@mapbox/vector-tile"
|
|
2
|
+
import { ILayerVisualizer } from "./ILayerVisualizer"
|
|
3
|
+
import { SymbolRenderLayer } from "../SymbolRenderLayer"
|
|
4
|
+
import { warnOnce } from "maplibre-gl/src/util/util"
|
|
5
|
+
|
|
6
|
+
export class SymbolFeature {
|
|
7
|
+
constructor() {
|
|
8
|
+
this.featureId = 0
|
|
9
|
+
this.textColor = Cesium.Color.BLACK.clone()
|
|
10
|
+
this.textSize = 12
|
|
11
|
+
this.coordinates = []
|
|
12
|
+
}
|
|
13
|
+
}
|
|
14
|
+
|
|
15
|
+
/**@type {Cesium.Cartesian3} */
|
|
16
|
+
let scratchDirectionToEye = null
|
|
17
|
+
/**@type {Cesium.Cartesian3} */
|
|
18
|
+
let scratchSurfaceNormal = null
|
|
19
|
+
|
|
20
|
+
export class SymbolLayerVisualizer extends ILayerVisualizer {
|
|
21
|
+
constructor(layers, tile) {
|
|
22
|
+
if (scratchDirectionToEye === null) {
|
|
23
|
+
scratchDirectionToEye = new Cesium.Cartesian3()
|
|
24
|
+
scratchSurfaceNormal = new Cesium.Cartesian3()
|
|
25
|
+
}
|
|
26
|
+
|
|
27
|
+
super(layers, tile)
|
|
28
|
+
|
|
29
|
+
/**@type {Cesium.Label[]} */
|
|
30
|
+
this.labels = []
|
|
31
|
+
this.primitive = null
|
|
32
|
+
this.dotCutOff = 0.0035
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
/**
|
|
36
|
+
* 对符号进行地平线剔除
|
|
37
|
+
* @param {Cesium.Cartesian3} positionWC
|
|
38
|
+
* @param {Cesium.Cartesian3} cameraPositionWC
|
|
39
|
+
*/
|
|
40
|
+
isOccluded(cameraPositionWC, positionWC) {
|
|
41
|
+
/*
|
|
42
|
+
如下图,o为符号锚点,up为椭球面过锚点o的法线,tangent为过锚点o的切线,eye为相机位置。
|
|
43
|
+
可见,当锚点在地平线以下时,eye方向与up方向夹角小于90°。
|
|
44
|
+
up
|
|
45
|
+
^
|
|
46
|
+
|
|
|
47
|
+
o —— —— > tangent
|
|
48
|
+
\
|
|
49
|
+
\
|
|
50
|
+
eye
|
|
51
|
+
*/
|
|
52
|
+
const eyeDir = Cesium.Cartesian3.subtract(cameraPositionWC, positionWC, scratchDirectionToEye)
|
|
53
|
+
Cesium.Cartesian3.normalize(eyeDir, eyeDir)
|
|
54
|
+
const up = Cesium.Cartesian3.normalize(positionWC, scratchSurfaceNormal)
|
|
55
|
+
return Cesium.Cartesian3.dot(eyeDir, up) < this.dotCutOff
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
/**
|
|
59
|
+
* @param {VectorTileFeature[]} features
|
|
60
|
+
* @param {SymbolRenderLayer} layer
|
|
61
|
+
* @param {Cesium.frameState} frameState
|
|
62
|
+
* @param {VectorTileset} tileset
|
|
63
|
+
*/
|
|
64
|
+
addLayer(features, layer, frameState, tileset) {
|
|
65
|
+
const style = layer.style
|
|
66
|
+
const { tile, labels } = this
|
|
67
|
+
const rectangle = tile.rectangle
|
|
68
|
+
|
|
69
|
+
function addText(coord, text, font, textSize, textColor, outlineWidth, outlineColor) {
|
|
70
|
+
if (!Cesium.Rectangle.contains(rectangle, Cesium.Cartographic.fromDegrees(coord[0], coord[1]))) {
|
|
71
|
+
return
|
|
72
|
+
}
|
|
73
|
+
const label = new Cesium.Label({
|
|
74
|
+
position: Cesium.Cartesian3.fromDegrees(coord[0], coord[1]),
|
|
75
|
+
text,
|
|
76
|
+
font: textSize + 'px ' + font,
|
|
77
|
+
fillColor: textColor,
|
|
78
|
+
style: outlineWidth && Cesium.LabelStyle.FILL_AND_OUTLINE,
|
|
79
|
+
outlineWidth: outlineWidth * textSize,
|
|
80
|
+
outlineColor,
|
|
81
|
+
//禁用深度测试
|
|
82
|
+
disableDepthTestDistance: Infinity
|
|
83
|
+
})
|
|
84
|
+
labels.push(label)
|
|
85
|
+
layer.labels.push(label)
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
for (const sourceFeature of features) {
|
|
89
|
+
const feature = sourceFeature.toGeoJSON(tile.x, tile.y, tile.z)
|
|
90
|
+
if (!feature.geometry) continue
|
|
91
|
+
const properties = sourceFeature.properties
|
|
92
|
+
|
|
93
|
+
//读取图层样式属性
|
|
94
|
+
const iconImage = style.layout.getDataValue('icon-image', tile.z, sourceFeature)
|
|
95
|
+
const textField = style.layout.getDataValue('text-field', tile.z, sourceFeature)
|
|
96
|
+
let text = textField && style.layout.resolveTokens(properties, textField)
|
|
97
|
+
if (iconImage) {
|
|
98
|
+
warnOnce('symbol图层:不支持图标')
|
|
99
|
+
continue
|
|
100
|
+
}
|
|
101
|
+
if (!text) {
|
|
102
|
+
continue
|
|
103
|
+
}
|
|
104
|
+
const maxWidth = style.layout.getDataValue('text-max-width', tile.z, sourceFeature) * 3
|
|
105
|
+
const textRotationAlignment = style.layout.getDataValue('text-rotation-alignment', tile.z, sourceFeature)
|
|
106
|
+
const textPitchAlignment = style.layout.getDataValue('text-pitch-alignment', tile.z, sourceFeature)
|
|
107
|
+
if (text.length > maxWidth) {
|
|
108
|
+
warnOnce('symbol图层: 不支持 text-max-width,无自动换行效果')
|
|
109
|
+
}
|
|
110
|
+
if (textRotationAlignment === 'map') {
|
|
111
|
+
warnOnce('symbol图层:text-rotation-alignment 仅支持 viewport')
|
|
112
|
+
}
|
|
113
|
+
if (textPitchAlignment === 'map') {
|
|
114
|
+
warnOnce('symbol图层:text-pitch-alignment 仅支持 viewport')
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
const font = style.layout.getDataValue('text-font', tile.z, sourceFeature)
|
|
118
|
+
const textSize = style.layout.getDataValue('text-size', tile.z, sourceFeature)
|
|
119
|
+
const textColor = style.convertColor(style.paint.getDataValue('text-color', tile.z, sourceFeature))
|
|
120
|
+
const outlineColor = style.convertColor(style.paint.getDataValue('text-halo-color', tile.z, sourceFeature))
|
|
121
|
+
const outlineWidth = style.paint.getDataValue('text-halo-width', tile.z, sourceFeature)
|
|
122
|
+
|
|
123
|
+
const geometryType = feature.geometry.type
|
|
124
|
+
const coordinates = feature.geometry.coordinates
|
|
125
|
+
if (geometryType == 'Point') {
|
|
126
|
+
addText(coordinates, text, font, textSize, textColor, outlineWidth, outlineColor)
|
|
127
|
+
}
|
|
128
|
+
else if (geometryType == 'MultiPoint') {
|
|
129
|
+
coordinates.forEach(coord => {
|
|
130
|
+
addText(coord, text, font, textSize, textColor, outlineWidth, outlineColor)
|
|
131
|
+
})
|
|
132
|
+
}
|
|
133
|
+
else {
|
|
134
|
+
warnOnce('symbol图层:不支持符号沿线布局');
|
|
135
|
+
}
|
|
136
|
+
}
|
|
137
|
+
|
|
138
|
+
this.layers.push(layer)
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
createPrimitive() {
|
|
142
|
+
//所有图层的文字共用一个LabelCollection
|
|
143
|
+
//注意:这样文字就没有了“图层”的特征了,渲染顺序可能和样式配置的不一致
|
|
144
|
+
//优化:参考 maplibre-gl 的符号系统实现,但工作量巨大,如有需求建议使用商业版(Mesh-3D矢量地图引擎)
|
|
145
|
+
|
|
146
|
+
const primitive = new Cesium.LabelCollection()
|
|
147
|
+
for (let i = 0; i < this.labels.length; i++) {
|
|
148
|
+
this.labels[i] = primitive.add(this.labels[i])
|
|
149
|
+
}
|
|
150
|
+
this.primitive = primitive
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
update(frameState, tileset) {
|
|
154
|
+
if (!this.primitive && this.labels?.length) {
|
|
155
|
+
this.createPrimitive()
|
|
156
|
+
}
|
|
157
|
+
|
|
158
|
+
//我们禁用了 label 的深度测试,这里剔除中心位置在地球背面的符号
|
|
159
|
+
const cameraPositionWC = frameState.camera.positionWC
|
|
160
|
+
for (const label of this.labels) {
|
|
161
|
+
label.show = !this.isOccluded(cameraPositionWC, label.position)
|
|
162
|
+
}
|
|
163
|
+
|
|
164
|
+
//性能优化:这里应该进行自动避让处理
|
|
165
|
+
|
|
166
|
+
if (this.primitive) {
|
|
167
|
+
this.primitive.update(frameState)
|
|
168
|
+
}
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
destroy() {
|
|
172
|
+
this.primitive = this.primitive && this.primitive.destroy()
|
|
173
|
+
super.destroy()
|
|
174
|
+
}
|
|
175
|
+
|
|
176
|
+
isDestroyed() {
|
|
177
|
+
return false
|
|
178
|
+
}
|
|
179
|
+
}
|
|
@@ -0,0 +1,46 @@
|
|
|
1
|
+
import { ISource } from "./ISource";
|
|
2
|
+
import { registerSource } from "./registerSource";
|
|
3
|
+
import geojsonvt from "geojson-vt";
|
|
4
|
+
import { GeoJSONWrapper } from '@maplibre/vt-pbf'
|
|
5
|
+
import { EXTENT } from 'maplibre-gl/src/data/extent';
|
|
6
|
+
|
|
7
|
+
export class GeoJSONSource extends ISource {
|
|
8
|
+
constructor(styleSource, path) {
|
|
9
|
+
super(styleSource, path)
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
async init() {
|
|
13
|
+
/**@type {import("@maplibre/maplibre-gl-style-spec").GeoJSONSourceSpecification} */
|
|
14
|
+
const sourceParams = this.styleSource
|
|
15
|
+
let data = sourceParams.data
|
|
16
|
+
if (typeof data === 'string') {
|
|
17
|
+
const url = /^((http)|(https)|(data:)|\/)/.test(data) ? data : this.path + data
|
|
18
|
+
try {
|
|
19
|
+
data = await Cesium.Resource.fetchJson(url)
|
|
20
|
+
} catch (err) {
|
|
21
|
+
this.errorEvent.raiseEvent(err)
|
|
22
|
+
}
|
|
23
|
+
}
|
|
24
|
+
if (data && data.features?.length) {
|
|
25
|
+
this.tileIndex = new geojsonvt(data, {
|
|
26
|
+
extent: EXTENT,
|
|
27
|
+
buffer: sourceParams.buffer === undefined ? 128 : sourceParams.buffer,
|
|
28
|
+
tolerance: sourceParams.tolerance === sourceParams.tolerance ? 0.375 : sourceParams.tolerance
|
|
29
|
+
})
|
|
30
|
+
}
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
async requestTile(x, y, z) {
|
|
34
|
+
if (!this.tileIndex) return
|
|
35
|
+
try {
|
|
36
|
+
const geoJSONTile = this.tileIndex.getTile(z, x, y)
|
|
37
|
+
if (!geoJSONTile) return
|
|
38
|
+
const geojsonWrapper = new GeoJSONWrapper(geoJSONTile.features, { extent: EXTENT });
|
|
39
|
+
return geojsonWrapper
|
|
40
|
+
} catch (err) {
|
|
41
|
+
this.errorEvent.raiseEvent(err)
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
registerSource('geojson', GeoJSONSource)
|
|
@@ -0,0 +1,39 @@
|
|
|
1
|
+
import { VectorTile } from '@mapbox/vector-tile'
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 数据源基类,定义通用属性和必须实现的方法(init、requestTile)
|
|
5
|
+
* @see VectorSource
|
|
6
|
+
* @see GeoJSONSource
|
|
7
|
+
*/
|
|
8
|
+
export class ISource {
|
|
9
|
+
/**
|
|
10
|
+
* 构造数据源实例。注意:该构造函数由VectorTileset调用,请勿在其他模块直接调用
|
|
11
|
+
* @param {import('@maplibre/maplibre-gl-style-spec').SourceSpecification} styleSource
|
|
12
|
+
* @param {string} [path]
|
|
13
|
+
* @see VectorSource
|
|
14
|
+
* @see GeoJSONSource
|
|
15
|
+
*/
|
|
16
|
+
constructor(styleSource, path = '') {
|
|
17
|
+
/**@type {"vector"|"geojson"} */
|
|
18
|
+
this.type = styleSource.type
|
|
19
|
+
/**@type {import('@maplibre/maplibre-gl-style-spec').SourceSpecification} */
|
|
20
|
+
this.styleSource = styleSource
|
|
21
|
+
this.path = path
|
|
22
|
+
this.errorEvent = new Cesium.Event()
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
async init() { }
|
|
26
|
+
|
|
27
|
+
/**
|
|
28
|
+
* @param {number} x
|
|
29
|
+
* @param {number} y
|
|
30
|
+
* @param {number} z
|
|
31
|
+
* @returns {Promise<VectorTile>}
|
|
32
|
+
*/
|
|
33
|
+
requestTile(x, y, z) { }
|
|
34
|
+
|
|
35
|
+
destroy() {
|
|
36
|
+
this.styleSource = null
|
|
37
|
+
this.errorEvent = null
|
|
38
|
+
}
|
|
39
|
+
}
|
|
@@ -0,0 +1,45 @@
|
|
|
1
|
+
import { ISource } from "./ISource";
|
|
2
|
+
import { registerSource } from "./registerSource";
|
|
3
|
+
import { VectorTile } from '@mapbox/vector-tile'
|
|
4
|
+
import Pbf from 'pbf'
|
|
5
|
+
|
|
6
|
+
export class VectorSource extends ISource {
|
|
7
|
+
constructor(styleSource, path) {
|
|
8
|
+
super(styleSource, path)
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
async init() {
|
|
12
|
+
const sourceParams = this.styleSource
|
|
13
|
+
let url = sourceParams.url
|
|
14
|
+
if (url && !sourceParams.tiles) {
|
|
15
|
+
url = /^((http)|(https)|(data:)|\/)/.test(url) ? url : this.path + sourceParams.url
|
|
16
|
+
try {
|
|
17
|
+
const metadata = await Cesium.Resource.fetchJson(url)
|
|
18
|
+
for (const key in metadata) {
|
|
19
|
+
if (!sourceParams[key]) {
|
|
20
|
+
sourceParams[key] = metadata[key]
|
|
21
|
+
}
|
|
22
|
+
}
|
|
23
|
+
} catch (err) {
|
|
24
|
+
this.errorEvent.raiseEvent(err)
|
|
25
|
+
}
|
|
26
|
+
}
|
|
27
|
+
}
|
|
28
|
+
|
|
29
|
+
async requestTile(x, y, z) {
|
|
30
|
+
const sourceParams = this.styleSource
|
|
31
|
+
if (!sourceParams.tiles || !sourceParams.tiles.length) return
|
|
32
|
+
let tileUrl = sourceParams.tiles[0].replace('{x}', x).replace('{y}', y).replace('{z}', z)
|
|
33
|
+
tileUrl = /^((http)|(https)|(data:)|\/)/.test(tileUrl) ? tileUrl : this.path + tileUrl
|
|
34
|
+
|
|
35
|
+
try {
|
|
36
|
+
const tileBuf = await (fetch(tileUrl).then(res => res.arrayBuffer()));
|
|
37
|
+
const tileData = new VectorTile(new Pbf(tileBuf))
|
|
38
|
+
return tileData
|
|
39
|
+
} catch (err) {
|
|
40
|
+
this.errorEvent.raiseEvent(err)
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}
|
|
44
|
+
|
|
45
|
+
registerSource('vector', VectorSource)
|
|
@@ -0,0 +1,20 @@
|
|
|
1
|
+
import { SubdivisionGranularitySetting, SubdivisionGranularityExpression } from "maplibre-gl/src/render/subdivision_granularity_settings"
|
|
2
|
+
|
|
3
|
+
const granularitySettings = {
|
|
4
|
+
globe: new SubdivisionGranularitySetting({
|
|
5
|
+
fill: new SubdivisionGranularityExpression(128, 2),
|
|
6
|
+
line: new SubdivisionGranularityExpression(512, 0),
|
|
7
|
+
// Always keep at least some subdivision on raster tiles, etc,
|
|
8
|
+
// otherwise they will be visibly warped at high zooms (before mercator transition).
|
|
9
|
+
// This si not needed on fill, because fill geometry tends to already be
|
|
10
|
+
// highly tessellated and granular at high zooms.
|
|
11
|
+
tile: new SubdivisionGranularityExpression(128, 32),
|
|
12
|
+
// Stencil granularity must never be higher than fill granularity,
|
|
13
|
+
// otherwise we would get seams in the oceans at zoom levels where
|
|
14
|
+
// stencil has higher granularity than fill.
|
|
15
|
+
stencil: new SubdivisionGranularityExpression(128, 1),
|
|
16
|
+
circle: 3
|
|
17
|
+
})
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
export { granularitySettings }
|
|
@@ -0,0 +1,11 @@
|
|
|
1
|
+
|
|
2
|
+
import { ISource } from './ISource'
|
|
3
|
+
import { registerSource, Sources } from './registerSource'
|
|
4
|
+
import { VectorSource } from './VectorSource'
|
|
5
|
+
import { GeoJSONSource } from './GeoJSONSource'
|
|
6
|
+
|
|
7
|
+
export {
|
|
8
|
+
ISource, registerSource, Sources,
|
|
9
|
+
VectorSource,
|
|
10
|
+
GeoJSONSource
|
|
11
|
+
}
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import { ISource } from "./ISource"
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* @type {{[sourceType:string]:typeof ISource}}
|
|
5
|
+
*/
|
|
6
|
+
const Sources = {}
|
|
7
|
+
|
|
8
|
+
/**
|
|
9
|
+
* 注册数据源类型,设置数据源类
|
|
10
|
+
* @param {*} type
|
|
11
|
+
* @param {*} sourceCls
|
|
12
|
+
*/
|
|
13
|
+
function registerSource(type, sourceCls) {
|
|
14
|
+
Sources[type] = sourceCls
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
export {
|
|
18
|
+
Sources, registerSource
|
|
19
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { featureFilter, Color } from "@maplibre/maplibre-gl-style-spec";
|
|
2
|
+
import { StyleLayerProperties } from "./StyleLayerProperties";
|
|
3
|
+
|
|
4
|
+
export class StyleLayer {
|
|
5
|
+
/**
|
|
6
|
+
* @param {import('@maplibre/maplibre-gl-style-spec').LayerSpecification} layer
|
|
7
|
+
*/
|
|
8
|
+
constructor(layer) {
|
|
9
|
+
this.data = layer
|
|
10
|
+
this.type = layer.type
|
|
11
|
+
this.id = layer.id
|
|
12
|
+
this.minzoom = layer.minzoom || 0
|
|
13
|
+
this.maxzoom = layer.maxzoom || 24
|
|
14
|
+
this.source = layer.source
|
|
15
|
+
/**@type {string} */
|
|
16
|
+
this.sourceLayer = layer['source-layer']
|
|
17
|
+
/**@type {import("@maplibre/maplibre-gl-style-spec").FeatureFilter|null} */
|
|
18
|
+
this.filter = null
|
|
19
|
+
this.paint = new StyleLayerProperties('paint_' + layer.type, layer.paint)
|
|
20
|
+
this.layout = new StyleLayerProperties('layout_' + layer.type, layer.layout)
|
|
21
|
+
if (layer.filter) {
|
|
22
|
+
this.filter = featureFilter(layer.filter)
|
|
23
|
+
}
|
|
24
|
+
}
|
|
25
|
+
|
|
26
|
+
/**
|
|
27
|
+
* 转换图层样式颜色,内部进行预乘Alpha的逆处理,@maplibre/maplibre-gl-style-spec内部会自动对颜色进行premultiplyAlpha操作,直接使用会出现明显的色差
|
|
28
|
+
* @param {Color} styleColor
|
|
29
|
+
* @param {Cesium.Color} [result]
|
|
30
|
+
* @returns
|
|
31
|
+
*/
|
|
32
|
+
convertColor(styleColor, result) {
|
|
33
|
+
const alphaScalar = styleColor.a > 0 ? 1. / styleColor.a : 1
|
|
34
|
+
if (!result) {
|
|
35
|
+
result = new Cesium.Color()
|
|
36
|
+
}
|
|
37
|
+
result.red = styleColor.r * alphaScalar
|
|
38
|
+
result.green = styleColor.g * alphaScalar
|
|
39
|
+
result.blue = styleColor.b * alphaScalar
|
|
40
|
+
result.alpha = styleColor.a
|
|
41
|
+
return result
|
|
42
|
+
}
|
|
43
|
+
}
|
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
import { expression, latest } from '@maplibre/maplibre-gl-style-spec'
|
|
2
|
+
|
|
3
|
+
export class StyleLayerProperties {
|
|
4
|
+
constructor(groupName, styleProperties = {}) {
|
|
5
|
+
this.data = styleProperties
|
|
6
|
+
/**@type {Map<string,import('@maplibre/maplibre-gl-style-spec').StylePropertyExpression>} */
|
|
7
|
+
this.props = new Map()
|
|
8
|
+
|
|
9
|
+
const groupReference = latest[groupName]
|
|
10
|
+
for (const key in groupReference) {
|
|
11
|
+
if (Object.hasOwnProperty.call(groupReference, key)) {
|
|
12
|
+
const reference = groupReference[key];
|
|
13
|
+
const value = styleProperties[key];
|
|
14
|
+
const property = expression.normalizePropertyExpression(
|
|
15
|
+
value === undefined ? reference.default : value, reference,
|
|
16
|
+
)
|
|
17
|
+
this.props.set(key, property)
|
|
18
|
+
}
|
|
19
|
+
}
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
/**
|
|
23
|
+
* Replace tokens in a string template with values in an object
|
|
24
|
+
*
|
|
25
|
+
* @param properties - a key/value relationship between tokens and replacements
|
|
26
|
+
* @param text - the template string
|
|
27
|
+
* @returns the template with tokens replaced
|
|
28
|
+
*/
|
|
29
|
+
resolveTokens(properties, text) {
|
|
30
|
+
return text.replace(/{([^{}]+)}/g, (match, key) => {
|
|
31
|
+
return properties && key in properties ? String(properties[key]) : '';
|
|
32
|
+
});
|
|
33
|
+
}
|
|
34
|
+
getDataConstValue(name, zoom) {
|
|
35
|
+
const expr = this.props.get(name)
|
|
36
|
+
return expr && expr.evaluate({ zoom })
|
|
37
|
+
}
|
|
38
|
+
|
|
39
|
+
getDataValue(name, zoom, feature) {
|
|
40
|
+
const expr = this.props.get(name)
|
|
41
|
+
return expr && expr.evaluate({ zoom }, feature)
|
|
42
|
+
}
|
|
43
|
+
}
|