cesium-xs-sdk 1.0.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/README.md +104 -0
- package/dist/cesium-xs-sdk.cjs.js +7387 -0
- package/dist/cesium-xs-sdk.cjs.js.map +1 -0
- package/dist/cesium-xs-sdk.esm.js +7383 -0
- package/dist/cesium-xs-sdk.esm.js.map +1 -0
- package/dist/cesium-xs-sdk.umd.js +2 -0
- package/dist/cesium-xs-sdk.umd.js.map +1 -0
- package/package.json +78 -0
- package/src/core/Config.js +176 -0
- package/src/core/ModuleManager.js +157 -0
- package/src/index.d.ts +358 -0
- package/src/index.js +59 -0
- package/src/modules/EditorModule.js +370 -0
- package/src/modules/EventModule.js +211 -0
- package/src/modules/GraphicModule.js +1037 -0
- package/src/modules/PlotGraphicType.js +43 -0
- package/src/modules/SceneModule.js +600 -0
- package/src/modules/tools/BaseTool.js +155 -0
- package/src/modules/tools/DeleteTool.js +252 -0
- package/src/modules/tools/DrawTool.js +961 -0
- package/src/modules/tools/EditTool.js +1585 -0
- package/src/modules/tools/SelectTool.js +526 -0
- package/src/react.d.ts +18 -0
- package/src/react.js +49 -0
- package/src/utils/CoordinateUtil.js +63 -0
- package/src/utils/GeoCalcUtil.js +95 -0
- package/src/utils/PlotGeometryUtil.js +1134 -0
- package/src/vue.d.ts +18 -0
- package/src/vue.js +76 -0
- package/src/vue2.d.ts +20 -0
- package/src/vue2.js +75 -0
|
@@ -0,0 +1,43 @@
|
|
|
1
|
+
/**
|
|
2
|
+
* 标绘图元类型枚举
|
|
3
|
+
* 对应 cesium-plot-js 支持的军事标绘类型
|
|
4
|
+
*/
|
|
5
|
+
export const PlotGraphicType = {
|
|
6
|
+
// 面型标绘
|
|
7
|
+
TRIANGLE: 'triangle',
|
|
8
|
+
SECTOR: 'sector',
|
|
9
|
+
ELLIPSE: 'ellipse',
|
|
10
|
+
FINE_ARROW: 'fineArrow',
|
|
11
|
+
ASSAULT_DIRECTION: 'assaultDirection',
|
|
12
|
+
ATTACK_ARROW: 'attackArrow',
|
|
13
|
+
SWALLOWTAIL_ATTACK_ARROW: 'swallowtailAttackArrow',
|
|
14
|
+
SQUAD_COMBAT: 'squadCombat',
|
|
15
|
+
SWALLOWTAIL_SQUAD_COMBAT: 'swallowtailSquadCombat',
|
|
16
|
+
DOUBLE_ARROW: 'doubleArrow',
|
|
17
|
+
|
|
18
|
+
// 线型标绘
|
|
19
|
+
STRAIGHT_ARROW: 'straightArrow',
|
|
20
|
+
CURVED_ARROW: 'curvedArrow',
|
|
21
|
+
CURVE: 'curve'
|
|
22
|
+
};
|
|
23
|
+
|
|
24
|
+
/**
|
|
25
|
+
* 标绘图元分类
|
|
26
|
+
*/
|
|
27
|
+
export const PlotGraphicCategory = {
|
|
28
|
+
POLYGON: 'polygon',
|
|
29
|
+
LINE: 'line'
|
|
30
|
+
};
|
|
31
|
+
|
|
32
|
+
/**
|
|
33
|
+
* 判断标绘类型是否为线型
|
|
34
|
+
* @param {string} plotType PlotGraphicType
|
|
35
|
+
* @returns {boolean}
|
|
36
|
+
*/
|
|
37
|
+
export function isLinePlotType(plotType) {
|
|
38
|
+
return [
|
|
39
|
+
PlotGraphicType.STRAIGHT_ARROW,
|
|
40
|
+
PlotGraphicType.CURVED_ARROW,
|
|
41
|
+
PlotGraphicType.CURVE
|
|
42
|
+
].includes(plotType);
|
|
43
|
+
}
|
|
@@ -0,0 +1,600 @@
|
|
|
1
|
+
import { Config } from '../core/Config.js';
|
|
2
|
+
|
|
3
|
+
/**
|
|
4
|
+
* 图层类型枚举
|
|
5
|
+
* @readonly
|
|
6
|
+
* @enum {string}
|
|
7
|
+
*/
|
|
8
|
+
export const LayerType = Object.freeze({
|
|
9
|
+
/** XYZ 瓦片服务 */
|
|
10
|
+
XYZ: 'xyz',
|
|
11
|
+
/** WMTS 瓦片服务 */
|
|
12
|
+
WMTS: 'wmts',
|
|
13
|
+
/** WMS 动态地图服务 */
|
|
14
|
+
WMS: 'wms',
|
|
15
|
+
/** TMS 瓦片服务 */
|
|
16
|
+
TMS: 'tms',
|
|
17
|
+
/** 单张图片 */
|
|
18
|
+
SINGLE_IMAGE: 'single_image',
|
|
19
|
+
/** 地形数据 */
|
|
20
|
+
TERRAIN: 'terrain',
|
|
21
|
+
/** 3D Tiles 模型数据集 */
|
|
22
|
+
'3DTILES': '3dtiles'
|
|
23
|
+
});
|
|
24
|
+
|
|
25
|
+
|
|
26
|
+
// ========== 私有工厂函数(文件内部) ==========
|
|
27
|
+
|
|
28
|
+
/**
|
|
29
|
+
* 根据 URL 自动推断图层类型
|
|
30
|
+
* @param {string} url 服务地址
|
|
31
|
+
* @returns {string} LayerType 值
|
|
32
|
+
*/
|
|
33
|
+
function autoDetectType(url) {
|
|
34
|
+
const u = url.toLowerCase();
|
|
35
|
+
|
|
36
|
+
// 1. 3DTiles — 以 .json 结尾或包含 3dtiles/3d_tiles
|
|
37
|
+
if (/\.json(?:[?#]|$)/.test(u) || u.includes('3dtiles') || u.includes('3d_tiles')) {
|
|
38
|
+
return LayerType['3DTILES'];
|
|
39
|
+
}
|
|
40
|
+
|
|
41
|
+
// 2. 地形 — 包含 terrain / dem / gdem / 水深 / elevation 关键词
|
|
42
|
+
if (/\/(?:terrain|dem|gdem|水深|elevation|ele)(\/|$)/.test(u) || /[?&#]terrain=/.test(u)) {
|
|
43
|
+
return LayerType.TERRAIN;
|
|
44
|
+
}
|
|
45
|
+
|
|
46
|
+
// 3. WMTS — 包含 wmts 关键词 或 TileMatrixSet 参数
|
|
47
|
+
if (u.includes('wmts') || u.includes('tilematrixset')) {
|
|
48
|
+
return LayerType.WMTS;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
// 4. WMS — 包含 wms 关键词 或 GetMap 请求参数
|
|
52
|
+
if (u.includes('wms') || u.includes('getmap') || u.includes('request=getmap')) {
|
|
53
|
+
return LayerType.WMS;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
// 5. TMS — 包含 tms 关键词 或 {-y} 反转瓦片
|
|
57
|
+
if (u.includes('tms') || u.includes('{-y}')) {
|
|
58
|
+
return LayerType.TMS;
|
|
59
|
+
}
|
|
60
|
+
|
|
61
|
+
// 6. XYZ — URL 模板包含 {z} {x} {y}
|
|
62
|
+
if (/\{[xyz]\}/.test(u)) {
|
|
63
|
+
return LayerType.XYZ;
|
|
64
|
+
}
|
|
65
|
+
|
|
66
|
+
// 7. 单张影像 — 有常见图片后缀且无瓦片模板
|
|
67
|
+
if (/\.(?:png|jpe?g|tiff?|gif|bmp|img)(?:[?#]|$)/.test(u)) {
|
|
68
|
+
return LayerType.SINGLE_IMAGE;
|
|
69
|
+
}
|
|
70
|
+
|
|
71
|
+
// 默认按 XYZ 处理(最常见的瓦片格式)
|
|
72
|
+
return LayerType.XYZ;
|
|
73
|
+
}
|
|
74
|
+
|
|
75
|
+
/**
|
|
76
|
+
* 根据类型创建对应的 Cesium Provider
|
|
77
|
+
* @param {string} type LayerType 值
|
|
78
|
+
* @param {string} url 服务地址
|
|
79
|
+
* @param {Object} opts 用户传入的配置
|
|
80
|
+
* @returns {Object} Cesium Provider 实例
|
|
81
|
+
*/
|
|
82
|
+
function createProviderByType(type, url, opts) {
|
|
83
|
+
switch (type) {
|
|
84
|
+
case LayerType.XYZ:
|
|
85
|
+
case LayerType.TMS:
|
|
86
|
+
// XYZ / TMS 均使用 UrlTemplateImageryProvider
|
|
87
|
+
return new Cesium.UrlTemplateImageryProvider({ url, ...opts });
|
|
88
|
+
|
|
89
|
+
case LayerType.WMTS:
|
|
90
|
+
return new Cesium.WebMapTileServiceImageryProvider({
|
|
91
|
+
url,
|
|
92
|
+
layer: opts.layer,
|
|
93
|
+
style: opts.style || 'default',
|
|
94
|
+
format: opts.format || 'image/jpeg',
|
|
95
|
+
tileMatrixSetID: opts.tileMatrixSetID || 'EPSG:4326',
|
|
96
|
+
...opts
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
case LayerType.WMS:
|
|
100
|
+
return new Cesium.WebMapServiceImageryProvider({
|
|
101
|
+
url,
|
|
102
|
+
layers: opts.layers || '',
|
|
103
|
+
parameters: { transparent: true, format: 'image/png', ...opts.parameters },
|
|
104
|
+
...opts
|
|
105
|
+
});
|
|
106
|
+
|
|
107
|
+
case LayerType.SINGLE_IMAGE:
|
|
108
|
+
return new Cesium.SingleTileImageryProvider({
|
|
109
|
+
url,
|
|
110
|
+
rectangle: opts.extent ? Cesium.Rectangle.fromDegrees(...opts.extent) : undefined,
|
|
111
|
+
credit: opts.credit || '',
|
|
112
|
+
...opts
|
|
113
|
+
});
|
|
114
|
+
|
|
115
|
+
case LayerType.TERRAIN:
|
|
116
|
+
// fromUrl 返回 Promise,由 addProviderToViewer 统一 await
|
|
117
|
+
return Cesium.CesiumTerrainProvider.fromUrl(url, {
|
|
118
|
+
requestWaterMask: opts.requestWaterMask ?? false,
|
|
119
|
+
requestVertexNormals: opts.requestVertexNormals ?? false,
|
|
120
|
+
...opts
|
|
121
|
+
});
|
|
122
|
+
|
|
123
|
+
case LayerType['3DTILES']:
|
|
124
|
+
return new Cesium.Cesium3DTileset({
|
|
125
|
+
url,
|
|
126
|
+
maximumScreenSpaceError: opts.maximumScreenSpaceError ?? 16,
|
|
127
|
+
maximumMemoryUsage: opts.maximumMemoryUsage ?? 512,
|
|
128
|
+
...opts
|
|
129
|
+
});
|
|
130
|
+
|
|
131
|
+
default:
|
|
132
|
+
throw new Error(`SceneModule: 不支持的图层类型 "${type}",可用值: ${Object.values(LayerType).join(', ')}`);
|
|
133
|
+
}
|
|
134
|
+
}
|
|
135
|
+
|
|
136
|
+
/**
|
|
137
|
+
* 将 Provider 添加到 Viewer 并返回句柄
|
|
138
|
+
* terrain 类型返回 provider 自身(不是 layer)
|
|
139
|
+
* 3dtiles 类型返回 tileset
|
|
140
|
+
* 其余返回 ImageryLayer
|
|
141
|
+
* @param {string} type LayerType
|
|
142
|
+
* @param {Object} viewer Cesium.Viewer
|
|
143
|
+
* @param {Object} providerType Cesium Provider
|
|
144
|
+
* @param {boolean} visible 是否可见
|
|
145
|
+
* @returns {Object} layer/tileset/provider
|
|
146
|
+
*/
|
|
147
|
+
async function addProviderToViewer(type, viewer, providerType, visible) {
|
|
148
|
+
if (type === LayerType.TERRAIN) {
|
|
149
|
+
const provider = await providerType
|
|
150
|
+
viewer.terrainProvider = provider;
|
|
151
|
+
return provider;
|
|
152
|
+
}
|
|
153
|
+
if (type === LayerType['3DTILES']) {
|
|
154
|
+
const tileset = viewer.scene.primitives.add(providerType);
|
|
155
|
+
tileset.show = visible ?? true;
|
|
156
|
+
return tileset;
|
|
157
|
+
}
|
|
158
|
+
const layer = viewer.imageryLayers.addImageryProvider(providerType);
|
|
159
|
+
layer.show = visible ?? true;
|
|
160
|
+
return layer;
|
|
161
|
+
}
|
|
162
|
+
|
|
163
|
+
|
|
164
|
+
// ========== 图层包装器 ==========
|
|
165
|
+
|
|
166
|
+
/**
|
|
167
|
+
* 影像图层包装器
|
|
168
|
+
*/
|
|
169
|
+
class ImageryLayerWrapper {
|
|
170
|
+
constructor(sceneModule, layer, layerId) {
|
|
171
|
+
this._layer = layer;
|
|
172
|
+
this._sceneModule = sceneModule;
|
|
173
|
+
this._layerId = layerId;
|
|
174
|
+
this._pickable = true;
|
|
175
|
+
this._eventEnabled = true;
|
|
176
|
+
}
|
|
177
|
+
|
|
178
|
+
setVisible(visible) {
|
|
179
|
+
this._layer.show = visible;
|
|
180
|
+
return this;
|
|
181
|
+
}
|
|
182
|
+
|
|
183
|
+
setAlpha(alpha) {
|
|
184
|
+
this._layer.alpha = alpha;
|
|
185
|
+
return this;
|
|
186
|
+
}
|
|
187
|
+
|
|
188
|
+
setBrightness(brightness) {
|
|
189
|
+
this._layer.brightness = brightness;
|
|
190
|
+
return this;
|
|
191
|
+
}
|
|
192
|
+
|
|
193
|
+
setContrast(contrast) {
|
|
194
|
+
this._layer.contrast = contrast;
|
|
195
|
+
return this;
|
|
196
|
+
}
|
|
197
|
+
|
|
198
|
+
setHue(hue) {
|
|
199
|
+
this._layer.hue = hue;
|
|
200
|
+
return this;
|
|
201
|
+
}
|
|
202
|
+
|
|
203
|
+
setSaturation(saturation) {
|
|
204
|
+
this._layer.saturation = saturation;
|
|
205
|
+
return this;
|
|
206
|
+
}
|
|
207
|
+
|
|
208
|
+
setGamma(gamma) {
|
|
209
|
+
this._layer.gamma = gamma;
|
|
210
|
+
return this;
|
|
211
|
+
}
|
|
212
|
+
|
|
213
|
+
enableEdit(enabled) {
|
|
214
|
+
this._pickable = enabled;
|
|
215
|
+
return this;
|
|
216
|
+
}
|
|
217
|
+
|
|
218
|
+
enableEvent(enabled) {
|
|
219
|
+
this._eventEnabled = enabled;
|
|
220
|
+
if (this._layer.imageryProvider && this._layer.imageryProvider.enablePickFeatures) {
|
|
221
|
+
this._layer.imageryProvider.enablePickFeatures = enabled;
|
|
222
|
+
}
|
|
223
|
+
return this;
|
|
224
|
+
}
|
|
225
|
+
|
|
226
|
+
getExtent() {
|
|
227
|
+
const provider = this._layer.imageryProvider;
|
|
228
|
+
if (provider && provider.rectangle) {
|
|
229
|
+
const rect = provider.rectangle;
|
|
230
|
+
return {
|
|
231
|
+
west: Cesium.Math.toDegrees(rect.west),
|
|
232
|
+
south: Cesium.Math.toDegrees(rect.south),
|
|
233
|
+
east: Cesium.Math.toDegrees(rect.east),
|
|
234
|
+
north: Cesium.Math.toDegrees(rect.north)
|
|
235
|
+
};
|
|
236
|
+
}
|
|
237
|
+
return null;
|
|
238
|
+
}
|
|
239
|
+
|
|
240
|
+
getRange() {
|
|
241
|
+
const provider = this._layer.imageryProvider;
|
|
242
|
+
return {
|
|
243
|
+
minLevel: provider.minimumLevel || 0,
|
|
244
|
+
maxLevel: provider.maximumLevel || 18
|
|
245
|
+
};
|
|
246
|
+
}
|
|
247
|
+
|
|
248
|
+
getLayer() {
|
|
249
|
+
return this._layer;
|
|
250
|
+
}
|
|
251
|
+
|
|
252
|
+
remove() {
|
|
253
|
+
this._sceneModule.removeLayer(this._layerId);
|
|
254
|
+
return this;
|
|
255
|
+
}
|
|
256
|
+
}
|
|
257
|
+
|
|
258
|
+
/**
|
|
259
|
+
* 地形图层包装器
|
|
260
|
+
*/
|
|
261
|
+
class TerrainLayerWrapper {
|
|
262
|
+
constructor(sceneModule, provider, layerId) {
|
|
263
|
+
this._provider = provider;
|
|
264
|
+
this._sceneModule = sceneModule;
|
|
265
|
+
this._layerId = layerId;
|
|
266
|
+
this._visible = true;
|
|
267
|
+
}
|
|
268
|
+
|
|
269
|
+
setVisible(visible) {
|
|
270
|
+
this._visible = visible;
|
|
271
|
+
return this;
|
|
272
|
+
}
|
|
273
|
+
|
|
274
|
+
getExtent() {
|
|
275
|
+
if (this._provider.rectangle) {
|
|
276
|
+
const rect = this._provider.rectangle;
|
|
277
|
+
return {
|
|
278
|
+
west: Cesium.Math.toDegrees(rect.west),
|
|
279
|
+
south: Cesium.Math.toDegrees(rect.south),
|
|
280
|
+
east: Cesium.Math.toDegrees(rect.east),
|
|
281
|
+
north: Cesium.Math.toDegrees(rect.north)
|
|
282
|
+
};
|
|
283
|
+
}
|
|
284
|
+
return { west: -180, south: -85, east: 180, north: 85 };
|
|
285
|
+
}
|
|
286
|
+
|
|
287
|
+
getProvider() {
|
|
288
|
+
return this._provider;
|
|
289
|
+
}
|
|
290
|
+
|
|
291
|
+
remove() {
|
|
292
|
+
this._sceneModule.removeLayer(this._layerId);
|
|
293
|
+
return this;
|
|
294
|
+
}
|
|
295
|
+
}
|
|
296
|
+
|
|
297
|
+
/**
|
|
298
|
+
* 3DTiles 图层包装器
|
|
299
|
+
*/
|
|
300
|
+
class TilesetLayerWrapper {
|
|
301
|
+
constructor(sceneModule, tileset, layerId) {
|
|
302
|
+
this._tileset = tileset;
|
|
303
|
+
this._sceneModule = sceneModule;
|
|
304
|
+
this._layerId = layerId;
|
|
305
|
+
}
|
|
306
|
+
|
|
307
|
+
setVisible(visible) {
|
|
308
|
+
this._tileset.show = visible;
|
|
309
|
+
return this;
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
setStyle(style) {
|
|
313
|
+
this._tileset.style = style;
|
|
314
|
+
return this;
|
|
315
|
+
}
|
|
316
|
+
|
|
317
|
+
getExtent() {
|
|
318
|
+
if (this._tileset.boundingSphere) {
|
|
319
|
+
const bs = this._tileset.boundingSphere;
|
|
320
|
+
const cartographic = Cesium.Cartographic.fromCartesian(bs.center);
|
|
321
|
+
const rect = this._tileset.rectangle;
|
|
322
|
+
if (rect) {
|
|
323
|
+
return {
|
|
324
|
+
west: Cesium.Math.toDegrees(rect.west),
|
|
325
|
+
south: Cesium.Math.toDegrees(rect.south),
|
|
326
|
+
east: Cesium.Math.toDegrees(rect.east),
|
|
327
|
+
north: Cesium.Math.toDegrees(rect.north),
|
|
328
|
+
center: [
|
|
329
|
+
Cesium.Math.toDegrees(cartographic.longitude),
|
|
330
|
+
Cesium.Math.toDegrees(cartographic.latitude)
|
|
331
|
+
]
|
|
332
|
+
};
|
|
333
|
+
}
|
|
334
|
+
}
|
|
335
|
+
return null;
|
|
336
|
+
}
|
|
337
|
+
|
|
338
|
+
locateTo() {
|
|
339
|
+
return this._sceneModule.viewer.zoomTo(this._tileset);
|
|
340
|
+
}
|
|
341
|
+
|
|
342
|
+
getTileset() {
|
|
343
|
+
return this._tileset;
|
|
344
|
+
}
|
|
345
|
+
|
|
346
|
+
remove() {
|
|
347
|
+
this._sceneModule.removeLayer(this._layerId);
|
|
348
|
+
return this;
|
|
349
|
+
}
|
|
350
|
+
}
|
|
351
|
+
|
|
352
|
+
|
|
353
|
+
// ========== SceneModule ==========
|
|
354
|
+
|
|
355
|
+
/**
|
|
356
|
+
* 场景+图层管理模块
|
|
357
|
+
*/
|
|
358
|
+
export class SceneModule {
|
|
359
|
+
constructor(viewer) {
|
|
360
|
+
this.viewer = viewer;
|
|
361
|
+
this.config = Config.getInstance().getConfig();
|
|
362
|
+
this.layerMap = new Map(); // 图层缓存(ID -> Wrapper 实例)
|
|
363
|
+
}
|
|
364
|
+
|
|
365
|
+
// ========== 坐标转换 ==========
|
|
366
|
+
|
|
367
|
+
lonLatToWorld(lon, lat, height = 0) {
|
|
368
|
+
return Cesium.Cartesian3.fromDegrees(lon, lat, height);
|
|
369
|
+
}
|
|
370
|
+
|
|
371
|
+
worldToLonLat(cartesian) {
|
|
372
|
+
const cartographic = Cesium.Cartographic.fromCartesian(cartesian);
|
|
373
|
+
return [
|
|
374
|
+
Cesium.Math.toDegrees(cartographic.longitude),
|
|
375
|
+
Cesium.Math.toDegrees(cartographic.latitude),
|
|
376
|
+
cartographic.height
|
|
377
|
+
];
|
|
378
|
+
}
|
|
379
|
+
|
|
380
|
+
screenToWorld(x, y) {
|
|
381
|
+
const screenPosition = new Cesium.Cartesian2(x, y);
|
|
382
|
+
if (this.viewer.scene.pickPositionSupported) {
|
|
383
|
+
const precisePosition = this.viewer.scene.pickPosition(screenPosition);
|
|
384
|
+
if (precisePosition) return precisePosition;
|
|
385
|
+
}
|
|
386
|
+
const ray = this.viewer.camera.getPickRay(screenPosition);
|
|
387
|
+
const globePosition = this.viewer.scene.globe.pick(ray, this.viewer.scene);
|
|
388
|
+
if (globePosition) return globePosition;
|
|
389
|
+
return this.viewer.camera.pickEllipsoid(screenPosition, Cesium.Ellipsoid.WGS84);
|
|
390
|
+
}
|
|
391
|
+
|
|
392
|
+
worldToScreen(cartesian) {
|
|
393
|
+
return this.viewer.scene.cartesianToCanvasCoordinates(cartesian);
|
|
394
|
+
}
|
|
395
|
+
|
|
396
|
+
screenToLonLat(x, y) {
|
|
397
|
+
const worldCoord = this.screenToWorld(x, y);
|
|
398
|
+
return worldCoord ? this.worldToLonLat(worldCoord) : null;
|
|
399
|
+
}
|
|
400
|
+
|
|
401
|
+
lonLatToScreen(lon, lat, height = 0) {
|
|
402
|
+
const worldCoord = this.lonLatToWorld(lon, lat, height);
|
|
403
|
+
return this.worldToScreen(worldCoord);
|
|
404
|
+
}
|
|
405
|
+
|
|
406
|
+
lonLatToCartographic(lon, lat, height = 0) {
|
|
407
|
+
return new Cesium.Cartographic(Cesium.Math.toRadians(lon), Cesium.Math.toRadians(lat), height);
|
|
408
|
+
}
|
|
409
|
+
|
|
410
|
+
cartographicToLonLat(cartographic) {
|
|
411
|
+
return [
|
|
412
|
+
Cesium.Math.toDegrees(cartographic.longitude),
|
|
413
|
+
Cesium.Math.toDegrees(cartographic.latitude),
|
|
414
|
+
cartographic.height
|
|
415
|
+
];
|
|
416
|
+
}
|
|
417
|
+
|
|
418
|
+
// ========== 统一图层工厂 ==========
|
|
419
|
+
|
|
420
|
+
/**
|
|
421
|
+
* 通用图层创建工厂(自动从 URL 推断类型)
|
|
422
|
+
* @param {string} url 服务地址
|
|
423
|
+
* @param {Object} [options={}] 配置选项
|
|
424
|
+
* @param {string} [options.type] 强制指定图层类型,见 {@link LayerType},不提供则自动推断
|
|
425
|
+
* @param {string} [options.layerId] 图层唯一 ID,不提供则自动生成
|
|
426
|
+
* @param {boolean} [options.visible=true] 是否可见
|
|
427
|
+
* @param {number} [options.minimumLevel=0] 最小层级
|
|
428
|
+
* @param {number} [options.maximumLevel=18] 最大层级
|
|
429
|
+
* @param {string} [options.credit=''] 版权信息
|
|
430
|
+
* @param {Array<number>} [options.extent] 范围 [west, south, east, north](单张影像用)
|
|
431
|
+
* @param {string} [options.layer] WMTS 层名
|
|
432
|
+
* @param {string} [options.style] WMTS 样式
|
|
433
|
+
* @param {string} [options.format] WMTS 格式
|
|
434
|
+
* @param {string} [options.tileMatrixSetID] WMTS 瓦片矩阵集
|
|
435
|
+
* @param {string} [options.layers] WMS 图层名
|
|
436
|
+
* @param {Object} [options.parameters] WMS 额外参数
|
|
437
|
+
* @param {boolean} [options.requestWaterMask] 地形请求水面遮罩
|
|
438
|
+
* @param {boolean} [options.requestVertexNormals] 地形请求顶点法线
|
|
439
|
+
* @param {number} [options.maximumScreenSpaceError] 3DTiles 最大屏幕空间误差
|
|
440
|
+
* @param {number} [options.maximumMemoryUsage] 3DTiles 最大内存占用(MB)
|
|
441
|
+
* @param {boolean} [options.autoLocate=true] 3DTiles 创建后自动定位
|
|
442
|
+
* @returns {ImageryLayerWrapper|TerrainLayerWrapper|TilesetLayerWrapper} 图层包装器
|
|
443
|
+
* @example
|
|
444
|
+
* // 自动推断类型(推荐)
|
|
445
|
+
* sceneModule.createLayer('https://tile.openstreetmap.org/{z}/{x}/{y}.png', { layerId: 'osm' });
|
|
446
|
+
* sceneModule.createLayer('https://example.com/tileset.json');
|
|
447
|
+
* sceneModule.createLayer('https://example.com/terrain');
|
|
448
|
+
*
|
|
449
|
+
* // 显式指定类型(覆盖自动推断)
|
|
450
|
+
* sceneModule.createLayer('https://example.com/wmts', { type: LayerType.WMTS, layer: 'vec' });
|
|
451
|
+
*/
|
|
452
|
+
async createLayer(url, options = {}) {
|
|
453
|
+
const type = options.type ?? autoDetectType(url);
|
|
454
|
+
const layerId = options.layerId || `${type}_${Date.now()}`;
|
|
455
|
+
|
|
456
|
+
if (this.layerMap.has(layerId)) {
|
|
457
|
+
console.warn(`图层 ${layerId} 已存在`);
|
|
458
|
+
return this.layerMap.get(layerId);
|
|
459
|
+
}
|
|
460
|
+
|
|
461
|
+
const visible = options.visible !== false;
|
|
462
|
+
const provider = createProviderByType(type, url, options);
|
|
463
|
+
const layerOrEntity = await addProviderToViewer(type, this.viewer, provider, visible);
|
|
464
|
+
const wrapper = this.#wrapLayer(type, layerOrEntity, layerId);
|
|
465
|
+
|
|
466
|
+
// 3DTiles 自动定位(readyPromise 已移除,改用 initialTilesLoaded 一次性事件)
|
|
467
|
+
if (type === LayerType['3DTILES'] && options.autoLocate !== false) {
|
|
468
|
+
const removeListener = layerOrEntity.initialTilesLoaded.addEventListener(() => {
|
|
469
|
+
removeListener();
|
|
470
|
+
this.viewer.zoomTo(layerOrEntity);
|
|
471
|
+
});
|
|
472
|
+
}
|
|
473
|
+
|
|
474
|
+
this.layerMap.set(layerId, wrapper);
|
|
475
|
+
console.log(`${type} 图层 ${layerId} 创建成功`);
|
|
476
|
+
return wrapper;
|
|
477
|
+
}
|
|
478
|
+
|
|
479
|
+
/** 根据类型实例化对应包装器 */
|
|
480
|
+
#wrapLayer(type, layerOrEntity, layerId) {
|
|
481
|
+
if (type === LayerType.TERRAIN) {
|
|
482
|
+
return new TerrainLayerWrapper(this, layerOrEntity, layerId);
|
|
483
|
+
}
|
|
484
|
+
if (type === LayerType['3DTILES']) {
|
|
485
|
+
return new TilesetLayerWrapper(this, layerOrEntity, layerId);
|
|
486
|
+
}
|
|
487
|
+
return new ImageryLayerWrapper(this, layerOrEntity, layerId);
|
|
488
|
+
}
|
|
489
|
+
|
|
490
|
+
// ========== 图层管理 ==========
|
|
491
|
+
|
|
492
|
+
/**
|
|
493
|
+
* 删除图层
|
|
494
|
+
* @param {string} layerId 图层 ID
|
|
495
|
+
* @returns {boolean} 是否删除成功
|
|
496
|
+
*/
|
|
497
|
+
removeLayer(layerId) {
|
|
498
|
+
if (!this.layerMap.has(layerId)) {
|
|
499
|
+
console.warn(`图层 ${layerId} 不存在`);
|
|
500
|
+
return false;
|
|
501
|
+
}
|
|
502
|
+
|
|
503
|
+
const layer = this.layerMap.get(layerId);
|
|
504
|
+
|
|
505
|
+
if (layer instanceof ImageryLayerWrapper) {
|
|
506
|
+
this.viewer.imageryLayers.remove(layer.getLayer());
|
|
507
|
+
} else if (layer instanceof TerrainLayerWrapper) {
|
|
508
|
+
// 地形无法直接删除,重置为默认椭球地形
|
|
509
|
+
this.viewer.terrainProvider = new Cesium.EllipsoidTerrainProvider();
|
|
510
|
+
} else if (layer instanceof TilesetLayerWrapper) {
|
|
511
|
+
this.viewer.scene.primitives.remove(layer.getTileset());
|
|
512
|
+
}
|
|
513
|
+
|
|
514
|
+
this.layerMap.delete(layerId);
|
|
515
|
+
return true;
|
|
516
|
+
}
|
|
517
|
+
|
|
518
|
+
/**
|
|
519
|
+
* 获取图层
|
|
520
|
+
* @param {string} layerId 图层 ID
|
|
521
|
+
* @returns {ImageryLayerWrapper|TerrainLayerWrapper|TilesetLayerWrapper|undefined}
|
|
522
|
+
*/
|
|
523
|
+
getLayer(layerId) {
|
|
524
|
+
return this.layerMap.get(layerId);
|
|
525
|
+
}
|
|
526
|
+
|
|
527
|
+
// ========== 场景定位 ==========
|
|
528
|
+
|
|
529
|
+
/**
|
|
530
|
+
* 相机飞到指定经纬度
|
|
531
|
+
* @param {Object} params 飞行参数
|
|
532
|
+
* @param {number} params.lon 经度
|
|
533
|
+
* @param {number} params.lat 纬度
|
|
534
|
+
* @param {number} [params.height=2000] 相机高度
|
|
535
|
+
* @param {number} [params.heading=0] 偏航角
|
|
536
|
+
* @param {number} [params.pitch=-90] 俯仰角
|
|
537
|
+
* @param {number} [params.roll=0] 翻滚角
|
|
538
|
+
* @param {number} [params.duration=3.0] 飞行时长(秒)
|
|
539
|
+
* @returns {Promise<boolean>}
|
|
540
|
+
*/
|
|
541
|
+
flyToLonLat(params = {}) {
|
|
542
|
+
const {
|
|
543
|
+
lon, lat,
|
|
544
|
+
height = 2000,
|
|
545
|
+
heading = 0,
|
|
546
|
+
pitch = -90,
|
|
547
|
+
roll = 0,
|
|
548
|
+
duration = 3.0
|
|
549
|
+
} = params;
|
|
550
|
+
|
|
551
|
+
if (!Cesium.defined(lon) || !Cesium.defined(lat)) {
|
|
552
|
+
console.warn('flyToLonLat: 必须提供 lon、lat');
|
|
553
|
+
return Promise.resolve(false);
|
|
554
|
+
}
|
|
555
|
+
|
|
556
|
+
return this.viewer.camera.flyTo({
|
|
557
|
+
destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
|
|
558
|
+
orientation: {
|
|
559
|
+
heading: Cesium.Math.toRadians(heading),
|
|
560
|
+
pitch: Cesium.Math.toRadians(pitch),
|
|
561
|
+
roll: Cesium.Math.toRadians(roll)
|
|
562
|
+
},
|
|
563
|
+
duration
|
|
564
|
+
});
|
|
565
|
+
}
|
|
566
|
+
|
|
567
|
+
/**
|
|
568
|
+
* 设置相机视角(立即跳转,无飞行动画)
|
|
569
|
+
* @param {Object} params
|
|
570
|
+
* @param {number} params.lon 经度
|
|
571
|
+
* @param {number} params.lat 纬度
|
|
572
|
+
* @param {number} [params.height=500] 相机高度
|
|
573
|
+
* @param {number} [params.heading=0] 偏航角(默认北向)
|
|
574
|
+
* @param {number} [params.pitch=-90] 俯仰角(默认俯视)
|
|
575
|
+
* @param {number} [params.roll=0] 翻滚角
|
|
576
|
+
*/
|
|
577
|
+
setView(params = {}) {
|
|
578
|
+
const {
|
|
579
|
+
lon, lat,
|
|
580
|
+
height = 500,
|
|
581
|
+
heading = 0,
|
|
582
|
+
pitch = -90,
|
|
583
|
+
roll = 0
|
|
584
|
+
} = params;
|
|
585
|
+
|
|
586
|
+
if (!Cesium.defined(lon) || !Cesium.defined(lat)) {
|
|
587
|
+
console.warn('setView: 必须提供 lon、lat');
|
|
588
|
+
return;
|
|
589
|
+
}
|
|
590
|
+
|
|
591
|
+
this.viewer.camera.setView({
|
|
592
|
+
destination: Cesium.Cartesian3.fromDegrees(lon, lat, height),
|
|
593
|
+
orientation: {
|
|
594
|
+
heading: Cesium.Math.toRadians(heading),
|
|
595
|
+
pitch: Cesium.Math.toRadians(pitch),
|
|
596
|
+
roll: Cesium.Math.toRadians(roll)
|
|
597
|
+
}
|
|
598
|
+
});
|
|
599
|
+
}
|
|
600
|
+
}
|