cesium-xs-sdk 1.0.13 → 1.0.15

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.
@@ -3198,15 +3198,32 @@ class GraphicModule {
3198
3198
  const positions = this._getEntityPositions(entity);
3199
3199
  if (positions.length < 3) return false;
3200
3200
 
3201
- const closedPositions = [...positions, positions[0]];
3202
3201
  const outlineId = `${graphicId}_outline`;
3203
3202
  const color = options.color ?? Cesium$f.Color.WHITE;
3204
3203
  const width = options.width ?? 2;
3205
3204
 
3205
+ // 用 CallbackProperty 实时跟随主 polygon 的 hierarchy,
3206
+ // 这样 SDK 内部 EditTool 拖拽顶点时,外边框会自动跟随移动。
3207
+ const mainEntity = entity;
3208
+ const positionsCallback = new Cesium$f.CallbackProperty(() => {
3209
+ const hierarchy = mainEntity.polygon?.hierarchy;
3210
+ if (!hierarchy) return [];
3211
+ let positions;
3212
+ if (typeof hierarchy.getValue === 'function') {
3213
+ const value = hierarchy.getValue(Cesium$f.JulianDate.now());
3214
+ positions = value?.positions || [];
3215
+ } else {
3216
+ positions = hierarchy.positions || [];
3217
+ }
3218
+ if (!positions || positions.length < 3) return positions || [];
3219
+ // 闭合:首点再加到末尾
3220
+ return [...positions, positions[0]];
3221
+ }, false);
3222
+
3206
3223
  this.viewer.entities.add({
3207
3224
  id: outlineId,
3208
3225
  polyline: {
3209
- positions: closedPositions,
3226
+ positions: positionsCallback,
3210
3227
  width,
3211
3228
  material: color,
3212
3229
  clampToGround: true,
@@ -9198,17 +9215,38 @@ class ControlModule {
9198
9215
  }
9199
9216
  }
9200
9217
 
9201
- const toDataUri = (svg) => `data:image/svg+xml;charset=utf-8,${encodeURIComponent(svg)}`;
9202
-
9203
- /**
9204
- * 军标内置默认图标。
9205
- * SDK 内置常见军标类型的默认图标,业务层可通过 imgUrl / useDefaultIcon 参数覆盖或追加。
9206
- */
9207
- const DEFAULT_SYMBOL_IMAGES = {
9208
- 'small-radio': toDataUri(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="80" height="80"><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><circle cx="36.93" cy="48.5" r="22" style="fill:none;stroke:red;stroke-miterlimit:10;stroke-width:4.535433070866142px"/><polyline points="42.56 25.87 53.06 9.51 54.06 27.5 65.07 10.37" style="fill:none;stroke:red;stroke-linejoin:bevel;stroke-width:4.535433070866142px"/><rect width="80" height="80" style="fill:red;opacity:0"/></g></g></svg>`),
9209
- 'large-radio': toDataUri(`<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="80" height="80"><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><polygon points="39.96 28.87 51.18 48.3 62.4 67.73 39.96 67.73 17.53 67.73 28.75 48.3 39.96 28.87" style="fill:none;stroke:red;stroke-miterlimit:10;stroke-width:4.535433070866142px"/><polyline points="39.96 28.64 50.46 12.27 51.46 30.27 62.47 13.14" style="fill:none;stroke:red;stroke-linejoin:bevel;stroke-width:4.535433070866142px"/><rect width="80" height="80" style="fill:red;opacity:0"/></g></g></svg>`),
9218
+ /**
9219
+ * 军标内置默认图标。
9220
+ * SDK 内置常见军标类型的默认图标,业务层可通过 imgUrl / useDefaultIcon 参数覆盖或追加。
9221
+ */
9222
+ const DEFAULT_SYMBOL_SVGS = {
9223
+ 'small-radio': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="80" height="80"><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><circle cx="36.93" cy="48.5" r="22" style="fill:none;stroke:red;stroke-miterlimit:10;stroke-width:4.535433070866142px"/><polyline points="42.56 25.87 53.06 9.51 54.06 27.5 65.07 10.37" style="fill:none;stroke:red;stroke-linejoin:bevel;stroke-width:4.535433070866142px"/><rect width="80" height="80" style="fill:red;opacity:0"/></g></g></svg>`,
9224
+ 'large-radio': `<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 80 80" width="80" height="80"><g id="图层_2" data-name="图层 2"><g id="图层_1-2" data-name="图层 1"><polygon points="39.96 28.87 51.18 48.3 62.4 67.73 39.96 67.73 17.53 67.73 28.75 48.3 39.96 28.87" style="fill:none;stroke:red;stroke-miterlimit:10;stroke-width:4.535433070866142px"/><polyline points="39.96 28.64 50.46 12.27 51.46 30.27 62.47 13.14" style="fill:none;stroke:red;stroke-linejoin:bevel;stroke-width:4.535433070866142px"/><rect width="80" height="80" style="fill:red;opacity:0"/></g></g></svg>`,
9210
9225
  };
9211
9226
 
9227
+ /** Blob URL 缓存,避免重复创建 */
9228
+ const blobUrlCache = new Map();
9229
+
9230
+ /**
9231
+ * 获取内置军标图标的 Blob URL(比 data URI 更利于 Cesium 材质加载)
9232
+ * @param {string} type 军标类型
9233
+ * @returns {string | null}
9234
+ */
9235
+ function getDefaultSymbolImage(type) {
9236
+ const svg = DEFAULT_SYMBOL_SVGS[type];
9237
+ if (!svg) return null;
9238
+ if (blobUrlCache.has(type)) return blobUrlCache.get(type);
9239
+ try {
9240
+ const blob = new Blob([svg], { type: 'image/svg+xml;charset=utf-8' });
9241
+ const url = URL.createObjectURL(blob);
9242
+ blobUrlCache.set(type, url);
9243
+ return url;
9244
+ } catch (e) {
9245
+ console.warn('生成军标图标 Blob URL 失败', e);
9246
+ return null;
9247
+ }
9248
+ }
9249
+
9212
9250
  const Cesium$2 = getCesium();
9213
9251
 
9214
9252
  /**
@@ -9227,10 +9265,21 @@ class MilitarySymbolModule {
9227
9265
  this.eventModule = null;
9228
9266
 
9229
9267
  this._editMode = false;
9268
+ this._editSymbolId = null;
9269
+ /** @type {Map<string, {move: Cesium.Entity, scale: Cesium.Entity, rotate: Cesium.Entity}>} */
9270
+ this._editHandles = new Map();
9230
9271
  this._draggingId = null;
9272
+ this._draggingHandle = null;
9273
+ this._dragStartScreen = null;
9274
+ this._dragStartScale = 1;
9275
+ this._dragStartRotation = 0;
9276
+ this._dragStartDist = 0;
9277
+ this._dragStartAngle = 0;
9231
9278
  this._dragObserverIds = [];
9232
9279
  /** @type {Set<Function>} */
9233
9280
  this._positionChangedCallbacks = new Set();
9281
+ /** @type {Set<Function>} */
9282
+ this._transformChangedCallbacks = new Set();
9234
9283
  }
9235
9284
 
9236
9285
  /**
@@ -9287,7 +9336,7 @@ class MilitarySymbolModule {
9287
9336
  _resolveImage(id, options) {
9288
9337
  const globalMode = this._defaultIconMode || this.config.militarySymbol?.defaultIconMode;
9289
9338
  const mode = options.iconMode || globalMode || 'default';
9290
- const defaultImage = DEFAULT_SYMBOL_IMAGES[options.type];
9339
+ const defaultImage = getDefaultSymbolImage(options.type);
9291
9340
  const registeredImage = this.defaultImageMap.get(options.type);
9292
9341
  const imgUrl = options.imgUrl || registeredImage || null;
9293
9342
 
@@ -9330,7 +9379,10 @@ class MilitarySymbolModule {
9330
9379
  _loadImage(url) {
9331
9380
  return new Promise((resolve, reject) => {
9332
9381
  const img = new Image();
9333
- img.crossOrigin = 'anonymous';
9382
+ // data URI / blob URL 等同源资源不需要 crossOrigin;跨域 http 资源需要
9383
+ if (url && /^https?:\/\//.test(url)) {
9384
+ img.crossOrigin = 'anonymous';
9385
+ }
9334
9386
  img.onload = () => resolve(img);
9335
9387
  img.onerror = (e) => reject(e);
9336
9388
  img.src = url;
@@ -9384,6 +9436,131 @@ class MilitarySymbolModule {
9384
9436
  return this.symbolMap.has(id) ? id : null;
9385
9437
  }
9386
9438
 
9439
+ /**
9440
+ * 根据鼠标拾取结果判断是否为编辑控制点
9441
+ * @private
9442
+ */
9443
+ _getHandleType(pickedEntity) {
9444
+ if (!pickedEntity) return null;
9445
+ const id = typeof pickedEntity.id === 'string' ? pickedEntity.id : pickedEntity.id?.id;
9446
+ if (!id) return null;
9447
+ if (id.endsWith('_move')) return 'move';
9448
+ if (id.endsWith('_scale')) return 'scale';
9449
+ if (id.endsWith('_rotate')) return 'rotate';
9450
+ return null;
9451
+ }
9452
+
9453
+ /**
9454
+ * 计算编辑控制点的三维坐标
9455
+ * @private
9456
+ */
9457
+ _getHandlePositions(position) {
9458
+ const center = Cesium$2.Cartesian3.fromDegrees(position[0], position[1], position[2] || 0);
9459
+ const matrix = Cesium$2.Transforms.eastNorthUpToFixedFrame(center);
9460
+ const offsetMeters = 120;
9461
+ const scaleOffset = Cesium$2.Matrix4.multiplyByPoint(
9462
+ matrix,
9463
+ new Cesium$2.Cartesian3(offsetMeters, 0, 0),
9464
+ new Cesium$2.Cartesian3()
9465
+ );
9466
+ const rotateOffset = Cesium$2.Matrix4.multiplyByPoint(
9467
+ matrix,
9468
+ new Cesium$2.Cartesian3(0, offsetMeters, 0),
9469
+ new Cesium$2.Cartesian3()
9470
+ );
9471
+ return { center, scale: scaleOffset, rotate: rotateOffset };
9472
+ }
9473
+
9474
+ /**
9475
+ * 为指定军标创建编辑控制点
9476
+ * @private
9477
+ */
9478
+ _createEditHandles(symbolId) {
9479
+ this._removeEditHandles(symbolId);
9480
+ const symbol = this.symbolMap.get(symbolId);
9481
+ if (!symbol) return;
9482
+
9483
+ const positions = this._getHandlePositions(symbol.metadata.position);
9484
+ const commonPoint = {
9485
+ pixelSize: 14,
9486
+ outlineColor: Cesium$2.Color.WHITE,
9487
+ outlineWidth: 2,
9488
+ heightReference: Cesium$2.HeightReference.CLAMP_TO_GROUND,
9489
+ };
9490
+ const commonLabel = {
9491
+ font: '12px Microsoft YaHei',
9492
+ fillColor: Cesium$2.Color.WHITE,
9493
+ outlineColor: Cesium$2.Color.BLACK,
9494
+ outlineWidth: 2,
9495
+ style: Cesium$2.LabelStyle.FILL_AND_OUTLINE,
9496
+ verticalOrigin: Cesium$2.VerticalOrigin.BOTTOM,
9497
+ horizontalOrigin: Cesium$2.HorizontalOrigin.CENTER,
9498
+ pixelOffset: new Cesium$2.Cartesian2(0, -10),
9499
+ heightReference: Cesium$2.HeightReference.CLAMP_TO_GROUND,
9500
+ };
9501
+
9502
+ const handles = {};
9503
+ handles.move = this.viewer.entities.add({
9504
+ id: `${symbolId}_move`,
9505
+ position: positions.center,
9506
+ point: { ...commonPoint, color: Cesium$2.Color.LIME },
9507
+ label: { ...commonLabel, text: '移动' },
9508
+ });
9509
+ handles.scale = this.viewer.entities.add({
9510
+ id: `${symbolId}_scale`,
9511
+ position: positions.scale,
9512
+ point: { ...commonPoint, color: Cesium$2.Color.DEEPSKYBLUE },
9513
+ label: { ...commonLabel, text: '缩放' },
9514
+ });
9515
+ handles.rotate = this.viewer.entities.add({
9516
+ id: `${symbolId}_rotate`,
9517
+ position: positions.rotate,
9518
+ point: { ...commonPoint, color: Cesium$2.Color.RED },
9519
+ label: { ...commonLabel, text: '旋转' },
9520
+ });
9521
+
9522
+ this._editHandles.set(symbolId, handles);
9523
+ }
9524
+
9525
+ /**
9526
+ * 移除指定军标的编辑控制点
9527
+ * @private
9528
+ */
9529
+ _removeEditHandles(symbolId) {
9530
+ const handles = this._editHandles.get(symbolId);
9531
+ if (!handles) return;
9532
+ Object.values(handles).forEach((entity) => {
9533
+ if (entity) this.viewer.entities.remove(entity);
9534
+ });
9535
+ this._editHandles.delete(symbolId);
9536
+ }
9537
+
9538
+ /**
9539
+ * 更新控制点位置以跟随军标
9540
+ * @private
9541
+ */
9542
+ _updateHandlePositions(symbolId) {
9543
+ const symbol = this.symbolMap.get(symbolId);
9544
+ const handles = this._editHandles.get(symbolId);
9545
+ if (!symbol || !handles) return;
9546
+ const positions = this._getHandlePositions(symbol.metadata.position);
9547
+ handles.move.position = positions.center;
9548
+ handles.scale.position = positions.scale;
9549
+ handles.rotate.position = positions.rotate;
9550
+ }
9551
+
9552
+ /**
9553
+ * 将 WGS84 坐标转换为屏幕坐标
9554
+ * @private
9555
+ */
9556
+ _cartesianToScreen(cartesian) {
9557
+ if (!cartesian) return null;
9558
+ try {
9559
+ return Cesium$2.SceneTransforms.wgs84ToWindowCoordinates(this.viewer.scene, cartesian);
9560
+ } catch (e) {
9561
+ return null;
9562
+ }
9563
+ }
9387
9564
  /**
9388
9565
  * 创建军标图元
9389
9566
  * @param {string} id 军标唯一ID
@@ -9422,7 +9599,6 @@ class MilitarySymbolModule {
9422
9599
  verticalOrigin: Cesium$2.VerticalOrigin.BOTTOM,
9423
9600
  horizontalOrigin: Cesium$2.HorizontalOrigin.CENTER,
9424
9601
  heightReference: Cesium$2.HeightReference.CLAMP_TO_GROUND,
9425
- disableDepthTestDistance: 100000,
9426
9602
  },
9427
9603
  label: {
9428
9604
  text: name,
@@ -9435,7 +9611,6 @@ class MilitarySymbolModule {
9435
9611
  horizontalOrigin: Cesium$2.HorizontalOrigin.CENTER,
9436
9612
  pixelOffset: new Cesium$2.Cartesian2(0, 4),
9437
9613
  heightReference: Cesium$2.HeightReference.CLAMP_TO_GROUND,
9438
- disableDepthTestDistance: 100000,
9439
9614
  show: true,
9440
9615
  },
9441
9616
  show: visible,
@@ -9469,6 +9644,10 @@ class MilitarySymbolModule {
9469
9644
  return false;
9470
9645
  }
9471
9646
  this.viewer.entities.remove(symbol.entity);
9647
+ this._removeEditHandles(id);
9648
+ if (this._editSymbolId === id) {
9649
+ this.disableEditMode();
9650
+ }
9472
9651
  this.symbolMap.delete(id);
9473
9652
  if (this._draggingId === id) {
9474
9653
  this._draggingId = null;
@@ -9481,6 +9660,11 @@ class MilitarySymbolModule {
9481
9660
  * 删除所有军标
9482
9661
  */
9483
9662
  removeAll() {
9663
+ this._editHandles.forEach((_, id) => this._removeEditHandles(id));
9664
+ this._editHandles.clear();
9665
+ if (this._editMode) {
9666
+ this.disableEditMode();
9667
+ }
9484
9668
  this.symbolMap.forEach((symbol) => {
9485
9669
  this.viewer.entities.remove(symbol.entity);
9486
9670
  });
@@ -9604,13 +9788,14 @@ class MilitarySymbolModule {
9604
9788
  const pitch = options.pitch ?? -Cesium$2.Math.PI_OVER_TWO;
9605
9789
  const range = options.range ?? 5000;
9606
9790
 
9607
- return this.viewer.camera.flyTo({
9608
- destination: Cesium$2.Cartesian3.fromDegrees(position[0], position[1], (position[2] || 0) + range),
9609
- orientation: { heading: 0, pitch, roll: 0 },
9610
- duration,
9611
- }).then(() => true).catch((err) => {
9612
- console.error('军标定位失败', err);
9613
- return false;
9791
+ return new Promise((resolve) => {
9792
+ this.viewer.camera.flyTo({
9793
+ destination: Cesium$2.Cartesian3.fromDegrees(position[0], position[1], (position[2] || 0) + range),
9794
+ orientation: { heading: 0, pitch, roll: 0 },
9795
+ duration,
9796
+ complete: () => resolve(true),
9797
+ cancel: () => resolve(false),
9798
+ });
9614
9799
  });
9615
9800
  }
9616
9801
 
@@ -9636,49 +9821,115 @@ class MilitarySymbolModule {
9636
9821
  }
9637
9822
 
9638
9823
  /**
9639
- * 启用拖拽编辑模式
9640
- * 鼠标在军标上按下即可拖拽,拖拽结束后触发 onPositionChanged 回调。
9824
+ * 启用军标编辑模式(生成移动/缩放/旋转控制点)
9825
+ * @param {string} id 要编辑的军标ID
9641
9826
  */
9642
- enableEditMode() {
9643
- if (this._editMode) return;
9827
+ enableEditMode(id) {
9828
+ if (this._editMode) {
9829
+ if (this._editSymbolId === id) return;
9830
+ this.disableEditMode();
9831
+ }
9832
+ if (!this.symbolMap.has(id)) {
9833
+ console.warn(`军标 ${id} 不存在,无法启用编辑`);
9834
+ return;
9835
+ }
9644
9836
  if (!this.eventModule) {
9645
- console.warn('MilitarySymbolModule 未注入 EventModule,无法启用拖拽编辑');
9837
+ console.warn('MilitarySymbolModule 未注入 EventModule,无法启用编辑');
9646
9838
  return;
9647
9839
  }
9648
9840
 
9649
9841
  this._editMode = true;
9842
+ this._editSymbolId = id;
9843
+ this._createEditHandles(id);
9844
+
9845
+ const downId = this.eventModule.registerObserver('mouse:down', (lngLat, pickedEntity, movement) => {
9846
+ const handleType = this._getHandleType(pickedEntity);
9847
+ let symbolId = null;
9848
+ if (handleType) {
9849
+ symbolId = this._editSymbolId;
9850
+ } else {
9851
+ symbolId = this._getSymbolIdFromPick(pickedEntity);
9852
+ }
9853
+ if (!symbolId || symbolId !== this._editSymbolId) return;
9854
+
9855
+ const symbol = this.symbolMap.get(symbolId);
9856
+ const centerScreen = this._cartesianToScreen(symbol.entity.position.getValue(Cesium$2.JulianDate.now()));
9857
+ const mouseScreen = movement.position;
9858
+ if (centerScreen && mouseScreen) {
9859
+ const dx = mouseScreen.x - centerScreen.x;
9860
+ const dy = mouseScreen.y - centerScreen.y;
9861
+ this._dragStartDist = Math.hypot(dx, dy);
9862
+ this._dragStartAngle = Math.atan2(dx, -dy);
9863
+ } else {
9864
+ this._dragStartDist = 0;
9865
+ this._dragStartAngle = 0;
9866
+ }
9650
9867
 
9651
- const downId = this.eventModule.registerObserver('mouse:down', (lngLat, pickedEntity) => {
9652
- const symbolId = this._getSymbolIdFromPick(pickedEntity);
9653
- if (!symbolId) return;
9654
9868
  this._draggingId = symbolId;
9869
+ this._draggingHandle = handleType || 'move';
9870
+ this._dragStartScale = symbol.metadata.scale;
9871
+ this._dragStartRotation = symbol.metadata.rotation || 0;
9655
9872
  this.viewer.scene.screenSpaceCameraController.enableInputs = false;
9656
9873
  });
9657
9874
 
9658
9875
  const moveId = this.eventModule.registerObserver('mouse:move', (lngLat, pickedEntity, movement) => {
9659
9876
  if (!this._draggingId || !movement?.endPosition) return;
9660
9877
 
9661
- const cartesian = this.viewer.scene.pickPosition(movement.endPosition)
9662
- || this.viewer.scene.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
9663
- if (!cartesian) return;
9664
-
9665
- const carto = Cesium$2.Cartographic.fromCartesian(cartesian);
9666
- const position = [
9667
- Cesium$2.Math.toDegrees(carto.longitude),
9668
- Cesium$2.Math.toDegrees(carto.latitude),
9669
- carto.height,
9670
- ];
9671
- this.update(this._draggingId, { position });
9878
+ const symbol = this.symbolMap.get(this._draggingId);
9879
+ if (!symbol) return;
9880
+
9881
+ if (this._draggingHandle === 'move') {
9882
+ const cartesian = this.viewer.scene.pickPosition(movement.endPosition)
9883
+ || this.viewer.scene.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
9884
+ if (!cartesian) return;
9885
+
9886
+ const carto = Cesium$2.Cartographic.fromCartesian(cartesian);
9887
+ const position = [
9888
+ Cesium$2.Math.toDegrees(carto.longitude),
9889
+ Cesium$2.Math.toDegrees(carto.latitude),
9890
+ carto.height,
9891
+ ];
9892
+ this.update(this._draggingId, { position });
9893
+ this._updateHandlePositions(this._draggingId);
9894
+ } else if (this._draggingHandle === 'scale') {
9895
+ const centerScreen = this._cartesianToScreen(symbol.entity.position.getValue(Cesium$2.JulianDate.now()));
9896
+ if (!centerScreen) return;
9897
+ const dx = movement.endPosition.x - centerScreen.x;
9898
+ const dy = movement.endPosition.y - centerScreen.y;
9899
+ const dist = Math.hypot(dx, dy);
9900
+ const ratio = this._dragStartDist > 0 ? dist / this._dragStartDist : 1;
9901
+ const newScale = Math.max(0.1, this._dragStartScale * ratio);
9902
+ this.update(this._draggingId, { scale: newScale });
9903
+ } else if (this._draggingHandle === 'rotate') {
9904
+ const centerScreen = this._cartesianToScreen(symbol.entity.position.getValue(Cesium$2.JulianDate.now()));
9905
+ if (!centerScreen) return;
9906
+ const dx = movement.endPosition.x - centerScreen.x;
9907
+ const dy = movement.endPosition.y - centerScreen.y;
9908
+ const angle = Math.atan2(dx, -dy);
9909
+ const deltaDeg = Cesium$2.Math.toDegrees(angle - this._dragStartAngle);
9910
+ let newRotation = this._dragStartRotation + deltaDeg;
9911
+ newRotation = ((newRotation % 360) + 360) % 360;
9912
+ this.update(this._draggingId, { rotation: newRotation });
9913
+ }
9672
9914
  });
9673
9915
 
9674
9916
  const upId = this.eventModule.registerObserver('mouse:up', () => {
9675
9917
  if (!this._draggingId) return;
9676
9918
  const symbol = this.symbolMap.get(this._draggingId);
9677
- const payload = symbol ? { id: this._draggingId, position: [...symbol.metadata.position] } : null;
9919
+ const payload = symbol
9920
+ ? {
9921
+ id: this._draggingId,
9922
+ position: [...symbol.metadata.position],
9923
+ scale: symbol.metadata.scale,
9924
+ rotation: symbol.metadata.rotation,
9925
+ }
9926
+ : null;
9678
9927
  this._draggingId = null;
9928
+ this._draggingHandle = null;
9679
9929
  this.viewer.scene.screenSpaceCameraController.enableInputs = true;
9680
9930
  if (payload) {
9681
- this._positionChangedCallbacks.forEach((callback) => callback(payload));
9931
+ this._positionChangedCallbacks.forEach((callback) => callback({ id: payload.id, position: payload.position }));
9932
+ this._transformChangedCallbacks.forEach((callback) => callback(payload));
9682
9933
  }
9683
9934
  });
9684
9935
 
@@ -9686,13 +9937,12 @@ class MilitarySymbolModule {
9686
9937
  }
9687
9938
 
9688
9939
  /**
9689
- * 禁用拖拽编辑模式
9940
+ * 禁用军标编辑模式
9690
9941
  */
9691
9942
  disableEditMode() {
9692
9943
  if (!this._editMode) return;
9693
9944
  if (this.eventModule) {
9694
9945
  this._dragObserverIds.forEach((observerId) => {
9695
- // 每个观察者注册时的事件类型固定,分别注销
9696
9946
  ['mouse:down', 'mouse:move', 'mouse:up'].forEach((eventType) => {
9697
9947
  try {
9698
9948
  this.eventModule.unregisterObserver(eventType, observerId);
@@ -9703,13 +9953,18 @@ class MilitarySymbolModule {
9703
9953
  });
9704
9954
  }
9705
9955
  this._dragObserverIds = [];
9956
+ if (this._editSymbolId) {
9957
+ this._removeEditHandles(this._editSymbolId);
9958
+ }
9959
+ this._editSymbolId = null;
9706
9960
  this._draggingId = null;
9961
+ this._draggingHandle = null;
9707
9962
  this.viewer.scene.screenSpaceCameraController.enableInputs = true;
9708
9963
  this._editMode = false;
9709
9964
  }
9710
9965
 
9711
9966
  /**
9712
- * 注册位置变化回调
9967
+ * 注册位置变化回调(仅位置)
9713
9968
  * @param {Function} callback 回调参数 { id, position }
9714
9969
  * @returns {Function} 注销函数
9715
9970
  */
@@ -9717,6 +9972,16 @@ class MilitarySymbolModule {
9717
9972
  this._positionChangedCallbacks.add(callback);
9718
9973
  return () => this._positionChangedCallbacks.delete(callback);
9719
9974
  }
9975
+
9976
+ /**
9977
+ * 注册变换变化回调(位置、缩放、旋转)
9978
+ * @param {Function} callback 回调参数 { id, position, scale, rotation }
9979
+ * @returns {Function} 注销函数
9980
+ */
9981
+ onTransformChanged(callback) {
9982
+ this._transformChangedCallbacks.add(callback);
9983
+ return () => this._transformChangedCallbacks.delete(callback);
9984
+ }
9720
9985
  }
9721
9986
 
9722
9987
  /**