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.
@@ -0,0 +1,1037 @@
1
+ import { Config } from '../core/Config.js';
2
+ import { PlotGeometryUtil } from '../utils/PlotGeometryUtil.js';
3
+ import { PlotGraphicType, isLinePlotType } from './PlotGraphicType.js';
4
+
5
+ /**
6
+ * 点图元包装器
7
+ */
8
+ class PointWrapper {
9
+ constructor(graphicModule, entity) {
10
+ this._entity = entity;
11
+ this._graphicModule = graphicModule;
12
+ }
13
+
14
+ setPosition(coords) {
15
+ this._entity.position = Cesium.Cartesian3.fromDegrees(...coords);
16
+ return this;
17
+ }
18
+
19
+ setColor(color) {
20
+ this._entity.point.color = color;
21
+ return this;
22
+ }
23
+
24
+ setPixelSize(size) {
25
+ this._entity.point.pixelSize = size;
26
+ return this;
27
+ }
28
+
29
+ setVisible(visible) {
30
+ this._entity.show = visible;
31
+ return this;
32
+ }
33
+
34
+ setHeightReference(ref) {
35
+ this._entity.point.heightReference = ref;
36
+ return this;
37
+ }
38
+
39
+ setDisableDepthTestDistance(distance) {
40
+ this._entity.point.disableDepthTestDistance = distance;
41
+ return this;
42
+ }
43
+
44
+
45
+ setOutlineColor(color) {
46
+ this._entity.point.outlineColor = color;
47
+ return this;
48
+ }
49
+
50
+ setOutlineWidth(width) {
51
+ this._entity.point.outlineWidth = width;
52
+ return this;
53
+ }
54
+
55
+ setName(name) {
56
+ this._entity.id = name;
57
+ return this;
58
+ }
59
+
60
+ // 名称标签相关
61
+ setNameVisible(visible) {
62
+ if (!this._entity.label) {
63
+ this._entity.label = new Cesium.LabelGraphics();
64
+ }
65
+ this._entity.label.show = visible;
66
+ return this;
67
+ }
68
+
69
+ setText(text, options = {}) {
70
+ if (!this._entity.label) {
71
+ this._entity.label = new Cesium.LabelGraphics();
72
+ }
73
+ this._entity.label.text = text;
74
+
75
+ // 应用配置选项
76
+ this._entity.label.disableDepthTestDistance = options.disableDepthTestDistance ?? Number.POSITIVE_INFINITY;
77
+ this._entity.label.verticalOrigin = options.verticalOrigin ?? Cesium.VerticalOrigin.BOTTOM;
78
+ this._entity.label.eyeOffset = options.eyeOffset ?? new Cesium.Cartesian3(0, 0, -10);
79
+ this._entity.label.heightReference = options.heightReference ?? Cesium.HeightReference.RELATIVE_TO_GROUND;
80
+
81
+ return this;
82
+ }
83
+
84
+ setFont(font) {
85
+ if (!this._entity.label) {
86
+ this._entity.label = new Cesium.LabelGraphics();
87
+ }
88
+ this._entity.label.font = font;
89
+ return this;
90
+ }
91
+
92
+ setTextColor(color) {
93
+ if (!this._entity.label) {
94
+ this._entity.label = new Cesium.LabelGraphics();
95
+ }
96
+ this._entity.label.fillColor = color;
97
+ return this;
98
+ }
99
+
100
+ setTextOutlineColor(color) {
101
+ if (!this._entity.label) {
102
+ this._entity.label = new Cesium.LabelGraphics();
103
+ }
104
+ this._entity.label.outlineColor = color;
105
+ return this;
106
+ }
107
+
108
+ setTextOutlineWidth(width) {
109
+ if (!this._entity.label) {
110
+ this._entity.label = new Cesium.LabelGraphics();
111
+ }
112
+ this._entity.label.outlineWidth = width;
113
+ return this;
114
+ }
115
+
116
+ setAlignType(alignType) {
117
+ if (!this._entity.label) {
118
+ this._entity.label = new Cesium.LabelGraphics();
119
+ }
120
+ // alignType: 0=CENTER, 1=LEFT, 2=RIGHT, 3=TOP, 4=BOTTOM
121
+ const horizontalOrigins = [Cesium.HorizontalOrigin.CENTER, Cesium.HorizontalOrigin.LEFT, Cesium.HorizontalOrigin.RIGHT];
122
+ const verticalOrigins = [Cesium.VerticalOrigin.CENTER, Cesium.VerticalOrigin.CENTER, Cesium.VerticalOrigin.CENTER, Cesium.VerticalOrigin.TOP, Cesium.VerticalOrigin.BOTTOM];
123
+ this._entity.label.horizontalOrigin = horizontalOrigins[alignType] || Cesium.HorizontalOrigin.CENTER;
124
+ this._entity.label.verticalOrigin = verticalOrigins[alignType] || Cesium.VerticalOrigin.CENTER;
125
+ return this;
126
+ }
127
+
128
+ setTextPixelOffset(offset) {
129
+ if (!this._entity.label) {
130
+ this._entity.label = new Cesium.LabelGraphics();
131
+ }
132
+ this._entity.label.pixelOffset = offset;
133
+ return this;
134
+ }
135
+
136
+ setTextScale(scale) {
137
+ if (!this._entity.label) {
138
+ this._entity.label = new Cesium.LabelGraphics();
139
+ }
140
+ this._entity.label.scale = scale;
141
+ return this;
142
+ }
143
+
144
+ remove() {
145
+ this._graphicModule.removeGraphic(this._entity.id);
146
+ return this;
147
+ }
148
+ }
149
+
150
+ /**
151
+ * 线图元包装器
152
+ */
153
+ class LineWrapper {
154
+ constructor(graphicModule, entity) {
155
+ this._entity = entity;
156
+ this._graphicModule = graphicModule;
157
+ }
158
+
159
+ setPositions(coords) {
160
+ this._entity.polyline.positions = Cesium.Cartesian3.fromDegreesArrayHeights(
161
+ coords.flatMap(coord => [coord[0], coord[1], coord[2] || 0])
162
+ );
163
+ return this;
164
+ }
165
+
166
+ setColor(color) {
167
+ this._entity.polyline.material = color;
168
+ return this;
169
+ }
170
+
171
+ setWidth(width) {
172
+ this._entity.polyline.width = width;
173
+ return this;
174
+ }
175
+
176
+ setMaterial(material) {
177
+ this._entity.polyline.material = material;
178
+ return this;
179
+ }
180
+
181
+ setVisible(visible) {
182
+ this._entity.show = visible;
183
+ return this;
184
+ }
185
+
186
+ setClampToGround(clamp) {
187
+ this._entity.polyline.clampToGround = clamp;
188
+ return this;
189
+ }
190
+
191
+ setName(name) {
192
+ this._entity.id = name;
193
+ return this;
194
+ }
195
+
196
+ /**
197
+ * 在图元上附加标签,标签位置随几何变化自动更新
198
+ * @param {string} text 标签文字
199
+ * @param {object} options 标签样式 { font, color, outlineColor, outlineWidth, pixelOffset, horizontalOrigin, verticalOrigin }
200
+ * @returns {LineWrapper}
201
+ */
202
+ setLabel(text, options = {}) {
203
+ if (!this._entity.label) {
204
+ this._entity.label = new Cesium.LabelGraphics();
205
+ }
206
+ this._entity.label.text = text;
207
+ this._entity.label.font = options.font || '14px Microsoft YaHei';
208
+ this._entity.label.fillColor = options.color || Cesium.Color.WHITE;
209
+ this._entity.label.outlineColor = options.outlineColor || Cesium.Color.BLACK;
210
+ this._entity.label.outlineWidth = options.outlineWidth ?? 1;
211
+ this._entity.label.show = true;
212
+ this._entity.label.pixelOffset = options.pixelOffset || new Cesium.Cartesian2(0, -10);
213
+ this._entity.label.horizontalOrigin = options.horizontalOrigin ?? Cesium.HorizontalOrigin.CENTER;
214
+ this._entity.label.verticalOrigin = options.verticalOrigin ?? Cesium.VerticalOrigin.BOTTOM;
215
+
216
+ // 使用 CallbackProperty 动态计算标签锚点,随编辑/平移自动更新
217
+ const self = this;
218
+ this._entity.position = new Cesium.CallbackProperty(function () {
219
+ const anchor = self._computeLabelAnchor();
220
+ return Cesium.Cartesian3.fromDegrees(anchor[0], anchor[1], anchor[2] || 0);
221
+ }, false);
222
+
223
+ return this;
224
+ }
225
+
226
+ /**
227
+ * 计算标签锚点 [lon, lat, height]
228
+ * 标绘图元使用控制点重心,普通线图元使用顶点平均
229
+ * @private
230
+ */
231
+ _computeLabelAnchor() {
232
+ if (this._entity._isPlotGraphic && this._entity._controlPoints?.length) {
233
+ const cps = this._entity._controlPoints;
234
+ let x = 0, y = 0, z = 0;
235
+ cps.forEach(cp => {
236
+ x += cp[0];
237
+ y += cp[1];
238
+ z += cp[2] || 0;
239
+ });
240
+ return [x / cps.length, y / cps.length, z / cps.length];
241
+ }
242
+
243
+ const positions = this._entity.polyline?.positions?.getValue(Cesium.JulianDate.now());
244
+ if (positions && positions.length > 0) {
245
+ let lon = 0, lat = 0, height = 0;
246
+ positions.forEach(p => {
247
+ const c = Cesium.Cartographic.fromCartesian(p);
248
+ lon += Cesium.Math.toDegrees(c.longitude);
249
+ lat += Cesium.Math.toDegrees(c.latitude);
250
+ height += c.height || 0;
251
+ });
252
+ const count = positions.length;
253
+ return [lon / count, lat / count, height / count];
254
+ }
255
+
256
+ return [0, 0, 0];
257
+ }
258
+
259
+ /**
260
+ * 设置为参数化标绘图元
261
+ * @param {string} plotType PlotGraphicType
262
+ * @param {Array} controlPoints 控制点 [lon, lat, height][]
263
+ * @param {object} options 标绘参数
264
+ * @returns {LineWrapper}
265
+ */
266
+ setPlotType(plotType, controlPoints, options = {}) {
267
+ this._entity._isPlotGraphic = true;
268
+ this._entity._plotType = plotType;
269
+ this._entity._controlPoints = JSON.parse(JSON.stringify(controlPoints));
270
+
271
+ const factoryName = `create${this._capitalizePlotType(plotType)}`;
272
+ if (!PlotGeometryUtil[factoryName]) {
273
+ console.warn(`未知标绘类型: ${plotType}`);
274
+ return this;
275
+ }
276
+
277
+ const positions = PlotGeometryUtil[factoryName](controlPoints, options);
278
+ this.setPositions(positions);
279
+ return this;
280
+ }
281
+
282
+ /**
283
+ * 更新标绘图元控制点并重算几何
284
+ * @param {Array} controlPoints 控制点 [lon, lat, height][]
285
+ * @param {object} options 标绘参数
286
+ * @returns {LineWrapper}
287
+ */
288
+ setControlPoints(controlPoints, options = {}) {
289
+ if (!this._entity._isPlotGraphic) {
290
+ console.warn('非标绘图元,请使用 setPositions');
291
+ return this;
292
+ }
293
+ return this.setPlotType(this._entity._plotType, controlPoints, options);
294
+ }
295
+
296
+ /**
297
+ * 将标绘类型名转为工厂方法名
298
+ * @private
299
+ */
300
+ _capitalizePlotType(plotType) {
301
+ return plotType
302
+ .replace(/-([a-z])/g, (g) => g[1].toUpperCase())
303
+ .replace(/^./, s => s.toUpperCase());
304
+ }
305
+
306
+ remove() {
307
+ this._graphicModule.removeGraphic(this._entity.id);
308
+ return this;
309
+ }
310
+ }
311
+
312
+ /**
313
+ * 面图元包装器
314
+ */
315
+ class PolygonWrapper {
316
+ constructor(graphicModule, entity) {
317
+ this._entity = entity;
318
+ this._graphicModule = graphicModule;
319
+ }
320
+
321
+ setPositions(coords) {
322
+ this._entity.polygon.hierarchy = new Cesium.PolygonHierarchy(
323
+ Cesium.Cartesian3.fromDegreesArrayHeights(
324
+ coords.flatMap(coord => [coord[0], coord[1], coord[2] || 0])
325
+ )
326
+ );
327
+ return this;
328
+ }
329
+
330
+ setColor(color) {
331
+ this._entity.polygon.material = color;
332
+ return this;
333
+ }
334
+
335
+ setOutlineColor(color) {
336
+ this._entity.polygon.outlineColor = color;
337
+ return this;
338
+ }
339
+
340
+ setOutlineWidth(width) {
341
+ this._entity.polygon.outlineWidth = width;
342
+ return this;
343
+ }
344
+
345
+ setVisible(visible) {
346
+ this._entity.show = visible;
347
+ return this;
348
+ }
349
+
350
+ setClampToGround(clamp) {
351
+ this._entity.polygon.clampToGround = clamp;
352
+ return this;
353
+ }
354
+
355
+ setHeightReference(ref) {
356
+ this._entity.polygon.heightReference = ref;
357
+ return this;
358
+ }
359
+
360
+ setName(name) {
361
+ this._entity.id = name;
362
+ return this;
363
+ }
364
+
365
+ /**
366
+ * 在图元上附加标签,标签位置随几何变化自动更新
367
+ * @param {string} text 标签文字
368
+ * @param {object} options 标签样式 { font, color, outlineColor, outlineWidth, pixelOffset, horizontalOrigin, verticalOrigin }
369
+ * @returns {PolygonWrapper}
370
+ */
371
+ setLabel(text, options = {}) {
372
+ if (!this._entity.label) {
373
+ this._entity.label = new Cesium.LabelGraphics();
374
+ }
375
+ this._entity.label.text = text;
376
+ this._entity.label.font = options.font || '14px Microsoft YaHei';
377
+ this._entity.label.fillColor = options.color || Cesium.Color.WHITE;
378
+ this._entity.label.outlineColor = options.outlineColor || Cesium.Color.BLACK;
379
+ this._entity.label.outlineWidth = options.outlineWidth ?? 1;
380
+ this._entity.label.show = true;
381
+ this._entity.label.pixelOffset = options.pixelOffset || new Cesium.Cartesian2(0, -10);
382
+ this._entity.label.horizontalOrigin = options.horizontalOrigin ?? Cesium.HorizontalOrigin.CENTER;
383
+ this._entity.label.verticalOrigin = options.verticalOrigin ?? Cesium.VerticalOrigin.BOTTOM;
384
+
385
+ // 使用 CallbackProperty 动态计算标签锚点,随编辑/平移自动更新
386
+ const self = this;
387
+ this._entity.position = new Cesium.CallbackProperty(function () {
388
+ const anchor = self._computeLabelAnchor();
389
+ return Cesium.Cartesian3.fromDegrees(anchor[0], anchor[1], anchor[2] || 0);
390
+ }, false);
391
+
392
+ return this;
393
+ }
394
+
395
+ /**
396
+ * 计算标签锚点 [lon, lat, height]
397
+ * 扇形固定在圆心,其他标绘使用控制点重心,普通面图元使用顶点重心
398
+ * @private
399
+ */
400
+ _computeLabelAnchor() {
401
+ if (this._entity._isPlotGraphic && this._entity._controlPoints?.length) {
402
+ const cps = this._entity._controlPoints;
403
+ if (this._entity._plotType === PlotGraphicType.SECTOR ||
404
+ this._entity._plotType === PlotGraphicType.ELLIPSE) {
405
+ return cps[0];
406
+ }
407
+ let x = 0, y = 0, z = 0;
408
+ cps.forEach(cp => {
409
+ x += cp[0];
410
+ y += cp[1];
411
+ z += cp[2] || 0;
412
+ });
413
+ return [x / cps.length, y / cps.length, z / cps.length];
414
+ }
415
+
416
+ const hierarchy = this._entity.polygon?.hierarchy?.getValue(Cesium.JulianDate.now());
417
+ if (hierarchy && hierarchy.positions && hierarchy.positions.length > 0) {
418
+ let lon = 0, lat = 0, height = 0;
419
+ hierarchy.positions.forEach(p => {
420
+ const c = Cesium.Cartographic.fromCartesian(p);
421
+ lon += Cesium.Math.toDegrees(c.longitude);
422
+ lat += Cesium.Math.toDegrees(c.latitude);
423
+ height += c.height || 0;
424
+ });
425
+ const count = hierarchy.positions.length;
426
+ return [lon / count, lat / count, height / count];
427
+ }
428
+
429
+ return [0, 0, 0];
430
+ }
431
+
432
+ /**
433
+ * 设置为参数化标绘图元
434
+ * @param {string} plotType PlotGraphicType
435
+ * @param {Array} controlPoints 控制点 [lon, lat, height][]
436
+ * @param {object} options 标绘参数
437
+ * @returns {PolygonWrapper}
438
+ */
439
+ setPlotType(plotType, controlPoints, options = {}) {
440
+ this._entity._isPlotGraphic = true;
441
+ this._entity._plotType = plotType;
442
+ this._entity._controlPoints = JSON.parse(JSON.stringify(controlPoints));
443
+
444
+ const factoryName = `create${this._capitalizePlotType(plotType)}`;
445
+ if (!PlotGeometryUtil[factoryName]) {
446
+ console.warn(`未知标绘类型: ${plotType}`);
447
+ return this;
448
+ }
449
+
450
+ const positions = PlotGeometryUtil[factoryName](controlPoints, options);
451
+ this.setPositions(positions);
452
+ return this;
453
+ }
454
+
455
+ /**
456
+ * 更新标绘图元控制点并重算几何
457
+ * @param {Array} controlPoints 控制点 [lon, lat, height][]
458
+ * @param {object} options 标绘参数
459
+ * @returns {PolygonWrapper}
460
+ */
461
+ setControlPoints(controlPoints, options = {}) {
462
+ if (!this._entity._isPlotGraphic) {
463
+ console.warn('非标绘图元,请使用 setPositions');
464
+ return this;
465
+ }
466
+ return this.setPlotType(this._entity._plotType, controlPoints, options);
467
+ }
468
+
469
+ /**
470
+ * 将标绘类型名转为工厂方法名
471
+ * @private
472
+ */
473
+ _capitalizePlotType(plotType) {
474
+ return plotType
475
+ .replace(/-([a-z])/g, (g) => g[1].toUpperCase())
476
+ .replace(/^./, s => s.toUpperCase());
477
+ }
478
+
479
+ remove() {
480
+ this._graphicModule.removeGraphic(this._entity.id);
481
+ return this;
482
+ }
483
+ }
484
+
485
+ /**
486
+ * 模型图元包装器
487
+ */
488
+ class ModelWrapper {
489
+ constructor(graphicModule, entity) {
490
+ this._entity = entity;
491
+ this._graphicModule = graphicModule;
492
+ }
493
+
494
+ setPosition(coords) {
495
+ this._entity.position = Cesium.Cartesian3.fromDegrees(...coords);
496
+ return this;
497
+ }
498
+
499
+ setUrl(url) {
500
+ this._entity.model.uri = url;
501
+ return this;
502
+ }
503
+
504
+ setScale(scale) {
505
+ this._entity.model.scale = scale;
506
+ return this;
507
+ }
508
+
509
+ setMinimumPixelSize(size) {
510
+ this._entity.model.minimumPixelSize = size;
511
+ return this;
512
+ }
513
+
514
+ setVisible(visible) {
515
+ this._entity.show = visible;
516
+ return this;
517
+ }
518
+
519
+ setColor(color) {
520
+ this._entity.model.color = color;
521
+ return this;
522
+ }
523
+
524
+ setName(name) {
525
+ this._entity.id = name;
526
+ return this;
527
+ }
528
+
529
+ remove() {
530
+ this._graphicModule.removeGraphic(this._entity.id);
531
+ return this;
532
+ }
533
+ }
534
+
535
+ /**
536
+ * 文本图元包装器
537
+ */
538
+ class TextWrapper {
539
+ constructor(graphicModule, entity) {
540
+ this._entity = entity;
541
+ this._graphicModule = graphicModule;
542
+ }
543
+
544
+ setPosition(coords) {
545
+ this._entity.position = Cesium.Cartesian3.fromDegrees(...coords);
546
+ return this;
547
+ }
548
+
549
+ setText(text) {
550
+ this._entity.label.text = text;
551
+
552
+ return this;
553
+ }
554
+
555
+ setFont(font) {
556
+ this._entity.label.font = font;
557
+ return this;
558
+ }
559
+
560
+ setColor(color) {
561
+ this._entity.label.fillColor = color;
562
+ return this;
563
+ }
564
+
565
+ setOutlineColor(color) {
566
+ this._entity.label.outlineColor = color;
567
+ return this;
568
+ }
569
+
570
+ setOutlineWidth(width) {
571
+ this._entity.label.outlineWidth = width;
572
+ return this;
573
+ }
574
+
575
+ setVisible(visible) {
576
+ this._entity.show = visible;
577
+ return this;
578
+ }
579
+
580
+ setPixelOffset(offset) {
581
+ this._entity.label.pixelOffset = offset;
582
+ return this;
583
+ }
584
+
585
+ setScale(scale) {
586
+ this._entity.label.scale = scale;
587
+ return this;
588
+ }
589
+
590
+ setAlignType(alignType) {
591
+ const horizontalOrigins = [Cesium.HorizontalOrigin.CENTER, Cesium.HorizontalOrigin.LEFT, Cesium.HorizontalOrigin.RIGHT];
592
+ const verticalOrigins = [Cesium.VerticalOrigin.CENTER, Cesium.VerticalOrigin.CENTER, Cesium.VerticalOrigin.CENTER, Cesium.VerticalOrigin.TOP, Cesium.VerticalOrigin.BOTTOM];
593
+ this._entity.label.horizontalOrigin = horizontalOrigins[alignType] || Cesium.HorizontalOrigin.CENTER;
594
+ this._entity.label.verticalOrigin = verticalOrigins[alignType] || Cesium.VerticalOrigin.CENTER;
595
+ return this;
596
+ }
597
+
598
+ setName(name) {
599
+ this._entity.id = name;
600
+ return this;
601
+ }
602
+
603
+ remove() {
604
+ this._graphicModule.removeGraphic(this._entity.id);
605
+ return this;
606
+ }
607
+ }
608
+
609
+ /**
610
+ * 图元模块(对应平台 IGraphicModule)
611
+ * 职责:点/线/面/模型的参数化创建、编辑、销毁
612
+ */
613
+ export class GraphicModule {
614
+ constructor(viewer) {
615
+ this.viewer = viewer;
616
+ this.config = Config.getInstance().getConfig();
617
+ this.graphicMap = new Map(); // 图元缓存(ID -> 实体实例)
618
+ this.wrapperMap = new Map(); // 包装器缓存(ID -> Wrapper 实例)
619
+ }
620
+
621
+ /**
622
+ * 创建点图元
623
+ * @param {string} graphicId 图元唯一ID
624
+ * @returns {PointWrapper} 点图元包装器
625
+ */
626
+ createPoint(graphicId) {
627
+ if (this.wrapperMap.has(graphicId)) {
628
+ console.warn(`图元 ${graphicId} 已存在`);
629
+ return this.wrapperMap.get(graphicId);
630
+ }
631
+
632
+ const defaultStyle = this.config.graphic?.point || { pixelSize: 8, color: Cesium.Color.RED };
633
+
634
+ const entityOptions = {
635
+ id: graphicId,
636
+ position: Cesium.Cartesian3.fromDegrees(0, 0, 0),
637
+ point: {
638
+ ...defaultStyle,
639
+ outlineColor: Cesium.Color.WHITE,
640
+ outlineWidth: 1
641
+ }
642
+ };
643
+
644
+ const entity = this.viewer.entities.add(entityOptions);
645
+ this.graphicMap.set(graphicId, entity);
646
+
647
+ const wrapper = new PointWrapper(this, entity);
648
+ this.wrapperMap.set(graphicId, wrapper);
649
+ console.log(`点图元 ${graphicId} 创建成功`);
650
+
651
+ return wrapper;
652
+ }
653
+
654
+ /**
655
+ * 创建点图元(旧方法别名,建议迁移到 createPoint)
656
+ * @deprecated
657
+ */
658
+ CreatePoint(graphicId) {
659
+ return this.createPoint(graphicId);
660
+ }
661
+
662
+ /**
663
+ * 创建线图元
664
+ * @param {string} graphicId 图元唯一ID
665
+ * @returns {LineWrapper} 线图元包装器
666
+ */
667
+ createLine(graphicId) {
668
+ if (this.wrapperMap.has(graphicId)) {
669
+ console.warn(`图元 ${graphicId} 已存在`);
670
+ return this.wrapperMap.get(graphicId);
671
+ }
672
+
673
+ const defaultStyle = this.config.graphic?.line || { width: 2, material: Cesium.Color.BLUE };
674
+
675
+ const entityOptions = {
676
+ id: graphicId,
677
+ polyline: {
678
+ positions: Cesium.Cartesian3.fromDegreesArrayHeights([]),
679
+ clampToGround: true,
680
+ ...defaultStyle
681
+ }
682
+ };
683
+
684
+ const entity = this.viewer.entities.add(entityOptions);
685
+ this.graphicMap.set(graphicId, entity);
686
+
687
+ const wrapper = new LineWrapper(this, entity);
688
+ this.wrapperMap.set(graphicId, wrapper);
689
+ console.log(`线图元 ${graphicId} 创建成功`);
690
+
691
+ return wrapper;
692
+ }
693
+
694
+ /**
695
+ * 创建线图元(旧方法别名,建议迁移到 createLine)
696
+ * @deprecated
697
+ */
698
+ CreateLine(graphicId) {
699
+ return this.createLine(graphicId);
700
+ }
701
+
702
+ /**
703
+ * 创建面图元
704
+ * @param {string} graphicId 图元唯一ID
705
+ * @returns {PolygonWrapper} 面图元包装器
706
+ */
707
+ createPolygon(graphicId) {
708
+ if (this.wrapperMap.has(graphicId)) {
709
+ console.warn(`图元 ${graphicId} 已存在`);
710
+ return this.wrapperMap.get(graphicId);
711
+ }
712
+
713
+ const defaultStyle = this.config.graphic?.polygon || { color: Cesium.Color.GREEN.withAlpha(0.5) };
714
+
715
+ const entityOptions = {
716
+ id: graphicId,
717
+ polygon: {
718
+ hierarchy: new Cesium.PolygonHierarchy([]),
719
+ clampToGround: true,
720
+ outlineColor: Cesium.Color.WHITE,
721
+ outlineWidth: 1,
722
+ ...defaultStyle
723
+ }
724
+ };
725
+
726
+ const entity = this.viewer.entities.add(entityOptions);
727
+ this.graphicMap.set(graphicId, entity);
728
+
729
+ const wrapper = new PolygonWrapper(this, entity);
730
+ this.wrapperMap.set(graphicId, wrapper);
731
+ console.log(`面图元 ${graphicId} 创建成功`);
732
+
733
+ return wrapper;
734
+ }
735
+
736
+ /**
737
+ * 创建面图元(旧方法别名,建议迁移到 createPolygon)
738
+ * @deprecated
739
+ */
740
+ CreatePolygon(graphicId) {
741
+ return this.createPolygon(graphicId);
742
+ }
743
+
744
+ /**
745
+ * 创建模型图元
746
+ * @param {string} graphicId 图元唯一ID
747
+ * @returns {ModelWrapper} 模型图元包装器
748
+ */
749
+ createModel(graphicId) {
750
+ if (this.wrapperMap.has(graphicId)) {
751
+ console.warn(`图元 ${graphicId} 已存在`);
752
+ return this.wrapperMap.get(graphicId);
753
+ }
754
+
755
+ const entityOptions = {
756
+ id: graphicId,
757
+ position: Cesium.Cartesian3.fromDegrees(0, 0, 0),
758
+ model: {
759
+ uri: "",
760
+ scale: 1.0,
761
+ minimumPixelSize: 128
762
+ }
763
+ };
764
+
765
+ const entity = this.viewer.entities.add(entityOptions);
766
+ this.graphicMap.set(graphicId, entity);
767
+
768
+ const wrapper = new ModelWrapper(this, entity);
769
+ this.wrapperMap.set(graphicId, wrapper);
770
+ console.log(`模型图元 ${graphicId} 创建成功`);
771
+
772
+ return wrapper;
773
+ }
774
+
775
+ /**
776
+ * 创建模型图元(旧方法别名,建议迁移到 createModel)
777
+ * @deprecated
778
+ */
779
+ CreateModel(graphicId) {
780
+ return this.createModel(graphicId);
781
+ }
782
+
783
+ /**
784
+ * 创建文本图元
785
+ * @param {string} graphicId 图元唯一ID
786
+ * @returns {TextWrapper} 文本图元包装器
787
+ */
788
+ createText(graphicId) {
789
+ if (this.wrapperMap.has(graphicId)) {
790
+ console.warn(`图元 ${graphicId} 已存在`);
791
+ return this.wrapperMap.get(graphicId);
792
+ }
793
+
794
+ const entityOptions = {
795
+ id: graphicId,
796
+ position: Cesium.Cartesian3.fromDegrees(0, 0, 0),
797
+ label: {
798
+ text: "",
799
+ font: "16px Microsoft YaHei",
800
+ fillColor: Cesium.Color.WHITE,
801
+ outlineColor: Cesium.Color.BLACK,
802
+ outlineWidth: 2,
803
+ style: Cesium.LabelStyle.FILL_AND_OUTLINE,
804
+ verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
805
+ horizontalOrigin: Cesium.HorizontalOrigin.CENTER
806
+ }
807
+ };
808
+
809
+ const entity = this.viewer.entities.add(entityOptions);
810
+ this.graphicMap.set(graphicId, entity);
811
+
812
+ const wrapper = new TextWrapper(this, entity);
813
+ this.wrapperMap.set(graphicId, wrapper);
814
+ console.log(`文本图元 ${graphicId} 创建成功`);
815
+
816
+ return wrapper;
817
+ }
818
+
819
+ /**
820
+ * 创建文本图元(旧方法别名,建议迁移到 createText)
821
+ * @deprecated
822
+ */
823
+ CreateText(graphicId) {
824
+ return this.createText(graphicId);
825
+ }
826
+
827
+ /**
828
+ * 创建参数化标绘图元
829
+ * @param {string} graphicId 图元唯一ID
830
+ * @param {string} plotType PlotGraphicType
831
+ * @param {Array} controlPoints 控制点 [lon, lat, height][]
832
+ * @returns {PolygonWrapper|LineWrapper} 图元包装器
833
+ */
834
+ createPlotGraphic(graphicId, plotType, controlPoints = []) {
835
+ if (this.wrapperMap.has(graphicId)) {
836
+ console.warn(`图元 ${graphicId} 已存在`);
837
+ return this.wrapperMap.get(graphicId);
838
+ }
839
+
840
+ const isLine = isLinePlotType(plotType);
841
+ const wrapper = isLine
842
+ ? this.createLine(graphicId)
843
+ : this.createPolygon(graphicId);
844
+
845
+ if (controlPoints.length > 0) {
846
+ const options = this._getPlotOptions(plotType);
847
+ wrapper.setPlotType(plotType, controlPoints, options);
848
+ } else {
849
+ wrapper._entity._isPlotGraphic = true;
850
+ wrapper._entity._plotType = plotType;
851
+ wrapper._entity._controlPoints = [];
852
+ }
853
+
854
+ console.log(`标绘图元 ${graphicId}(${plotType})创建成功`);
855
+ return wrapper;
856
+ }
857
+
858
+ /**
859
+ * 创建直箭头
860
+ * @param {string} graphicId 图元ID
861
+ * @param {Array} controlPoints 控制点
862
+ * @returns {PolygonWrapper}
863
+ */
864
+ createFineArrow(graphicId, controlPoints = []) {
865
+ return this.createPlotGraphic(graphicId, PlotGraphicType.FINE_ARROW, controlPoints);
866
+ }
867
+
868
+ /**
869
+ * 创建突击方向箭头
870
+ * @param {string} graphicId 图元ID
871
+ * @param {Array} controlPoints 控制点
872
+ * @returns {PolygonWrapper}
873
+ */
874
+ createAssaultDirection(graphicId, controlPoints = []) {
875
+ return this.createPlotGraphic(graphicId, PlotGraphicType.ASSAULT_DIRECTION, controlPoints);
876
+ }
877
+
878
+ /**
879
+ * 创建细直箭头
880
+ * @param {string} graphicId 图元ID
881
+ * @param {Array} controlPoints 控制点
882
+ * @returns {LineWrapper}
883
+ */
884
+ createStraightArrow(graphicId, controlPoints = []) {
885
+ return this.createPlotGraphic(graphicId, PlotGraphicType.STRAIGHT_ARROW, controlPoints);
886
+ }
887
+
888
+ /**
889
+ * 创建三角形
890
+ * @param {string} graphicId 图元ID
891
+ * @param {Array} controlPoints 控制点
892
+ * @returns {PolygonWrapper}
893
+ */
894
+ createTriangle(graphicId, controlPoints = []) {
895
+ return this.createPlotGraphic(graphicId, PlotGraphicType.TRIANGLE, controlPoints);
896
+ }
897
+
898
+ /**
899
+ * 创建扇形
900
+ * @param {string} graphicId 图元ID
901
+ * @param {Array} center 圆心 [lon, lat, height]
902
+ * @param {number} radius 半径(米)
903
+ * @param {number} startAngle 起始角度(度,0=正东,逆时针增大)
904
+ * @param {number} endAngle 结束角度(度)
905
+ * @returns {PolygonWrapper}
906
+ */
907
+ createSector(graphicId, center, radius, startAngle, endAngle) {
908
+ // 转换为三点控制点,与内部编辑逻辑保持一致
909
+ const startAngleRad = (startAngle || 0) * Math.PI / 180;
910
+ const endAngleRad = (endAngle === undefined ? 90 : endAngle) * Math.PI / 180;
911
+ const pnt2 = PlotGeometryUtil._getPointAtBearingAndDistance(center, radius, startAngleRad);
912
+ const pnt3 = PlotGeometryUtil._getPointAtBearingAndDistance(center, radius, endAngleRad);
913
+ const controlPoints = [center, pnt2, pnt3];
914
+
915
+ return this.createPlotGraphic(graphicId, PlotGraphicType.SECTOR, controlPoints);
916
+ }
917
+
918
+ /**
919
+ * 创建椭圆
920
+ * @param {string} graphicId 图元ID
921
+ * @param {Array} controlPoints 控制点,3 点 [center, majorEnd, minorEnd]
922
+ * @returns {PolygonWrapper}
923
+ */
924
+ createEllipse(graphicId, controlPoints = []) {
925
+ return this.createPlotGraphic(graphicId, PlotGraphicType.ELLIPSE, controlPoints);
926
+ }
927
+
928
+ /**
929
+ * 创建攻击箭头
930
+ * @param {string} graphicId 图元ID
931
+ * @param {Array} controlPoints 控制点,≥3 点
932
+ * @returns {PolygonWrapper}
933
+ */
934
+ createAttackArrow(graphicId, controlPoints = []) {
935
+ return this.createPlotGraphic(graphicId, PlotGraphicType.ATTACK_ARROW, controlPoints);
936
+ }
937
+
938
+ /**
939
+ * 创建燕尾攻击箭头
940
+ * @param {string} graphicId 图元ID
941
+ * @param {Array} controlPoints 控制点,≥3 点
942
+ * @returns {PolygonWrapper}
943
+ */
944
+ createSwallowtailAttackArrow(graphicId, controlPoints = []) {
945
+ return this.createPlotGraphic(graphicId, PlotGraphicType.SWALLOWTAIL_ATTACK_ARROW, controlPoints);
946
+ }
947
+
948
+ /**
949
+ * 创建集结箭头
950
+ * @param {string} graphicId 图元ID
951
+ * @param {Array} controlPoints 控制点,≥2 点
952
+ * @returns {PolygonWrapper}
953
+ */
954
+ createSquadCombat(graphicId, controlPoints = []) {
955
+ return this.createPlotGraphic(graphicId, PlotGraphicType.SQUAD_COMBAT, controlPoints);
956
+ }
957
+
958
+ /**
959
+ * 创建燕尾集结箭头
960
+ * @param {string} graphicId 图元ID
961
+ * @param {Array} controlPoints 控制点,≥2 点
962
+ * @returns {PolygonWrapper}
963
+ */
964
+ createSwallowtailSquadCombat(graphicId, controlPoints = []) {
965
+ return this.createPlotGraphic(graphicId, PlotGraphicType.SWALLOWTAIL_SQUAD_COMBAT, controlPoints);
966
+ }
967
+
968
+ /**
969
+ * 创建双箭头
970
+ * @param {string} graphicId 图元ID
971
+ * @param {Array} controlPoints 控制点,3~5 点
972
+ * @returns {PolygonWrapper}
973
+ */
974
+ createDoubleArrow(graphicId, controlPoints = []) {
975
+ return this.createPlotGraphic(graphicId, PlotGraphicType.DOUBLE_ARROW, controlPoints);
976
+ }
977
+
978
+ /**
979
+ * 创建曲线箭头
980
+ * @param {string} graphicId 图元ID
981
+ * @param {Array} controlPoints 控制点,≥2 点
982
+ * @returns {LineWrapper}
983
+ */
984
+ createCurvedArrow(graphicId, controlPoints = []) {
985
+ return this.createPlotGraphic(graphicId, PlotGraphicType.CURVED_ARROW, controlPoints);
986
+ }
987
+
988
+ /**
989
+ * 创建曲线
990
+ * @param {string} graphicId 图元ID
991
+ * @param {Array} controlPoints 控制点,≥2 点
992
+ * @returns {LineWrapper}
993
+ */
994
+ createCurve(graphicId, controlPoints = []) {
995
+ return this.createPlotGraphic(graphicId, PlotGraphicType.CURVE, controlPoints);
996
+ }
997
+
998
+ /**
999
+ * 获取标绘类型默认参数
1000
+ * 当前返回空对象,算法参数使用 PlotGeometryUtil 内部默认值
1001
+ * 后续可扩展为从 Config 读取各标绘类型的算法参数
1002
+ * @private
1003
+ */
1004
+ _getPlotOptions(plotType) {
1005
+ const plotConfig = this.config.graphic?.plot;
1006
+ if (!plotConfig) return {};
1007
+ // 当前仅支持按线/面读取通用样式,算法参数暂不提供配置
1008
+ return {};
1009
+ }
1010
+
1011
+ /**
1012
+ * 删除图元
1013
+ * @param {string} graphicId 图元ID
1014
+ * @returns {boolean} 是否删除成功
1015
+ */
1016
+ removeGraphic(graphicId) {
1017
+ if (!this.graphicMap.has(graphicId)) {
1018
+ console.warn(`图元 ${graphicId} 不存在`);
1019
+ return false;
1020
+ }
1021
+
1022
+ const entity = this.graphicMap.get(graphicId);
1023
+ this.viewer.entities.remove(entity);
1024
+ this.graphicMap.delete(graphicId);
1025
+ this.wrapperMap.delete(graphicId);
1026
+ return true;
1027
+ }
1028
+
1029
+ /**
1030
+ * 获取图元
1031
+ * @param {string} graphicId 图元ID
1032
+ * @returns {Cesium.Entity|undefined} 图元实体
1033
+ */
1034
+ getGraphic(graphicId) {
1035
+ return this.graphicMap.get(graphicId);
1036
+ }
1037
+ }