@jorgmoritz/gis-manager 0.1.21 → 0.1.27

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/dist/vue/index.js CHANGED
@@ -60,6 +60,8 @@ var CameraEventBus = class {
60
60
  __publicField(this, "onPoseChange", new Emitter());
61
61
  /** 视锥体形状变化事件 */
62
62
  __publicField(this, "onFrustumShapeChange", new Emitter());
63
+ /** 飞机游标姿态变化事件 */
64
+ __publicField(this, "onCursorPoseChange", new Emitter());
63
65
  }
64
66
  /**
65
67
  * 清理所有监听器
@@ -68,9 +70,20 @@ var CameraEventBus = class {
68
70
  this.onFOVChange.clear();
69
71
  this.onPoseChange.clear();
70
72
  this.onFrustumShapeChange.clear();
73
+ this.onCursorPoseChange.clear();
71
74
  }
72
75
  };
73
- var globalCameraEventBus = new CameraEventBus();
76
+ var GLOBAL_KEY = "__gisManager_globalCameraEventBus__";
77
+ function getGlobalEventBus() {
78
+ if (typeof window !== "undefined") {
79
+ if (!window[GLOBAL_KEY]) {
80
+ window[GLOBAL_KEY] = new CameraEventBus();
81
+ }
82
+ return window[GLOBAL_KEY];
83
+ }
84
+ return new CameraEventBus();
85
+ }
86
+ var globalCameraEventBus = getGlobalEventBus();
74
87
 
75
88
  // src/core/LayerManager.ts
76
89
  var layerIdSeq = 0;
@@ -1078,7 +1091,9 @@ var SceneManager = class {
1078
1091
  depth: true,
1079
1092
  stencil: true,
1080
1093
  antialias: true,
1081
- powerPreference: "high-performance"
1094
+ powerPreference: "high-performance",
1095
+ preserveDrawingBuffer: true
1096
+ // 必需:允许 canvas.toDataURL() 截图
1082
1097
  }
1083
1098
  },
1084
1099
  ...viewerOptions
@@ -1516,8 +1531,8 @@ var CameraFOVController = class {
1516
1531
  if (!this.sliderEl) return;
1517
1532
  const sliderValue = parseFloat(this.sliderEl.value);
1518
1533
  const sliderIndex = this.focalLengthPresets.length - 1 - sliderValue;
1519
- const zoomMultiplier = this.interpolateZoomMultiplier(sliderIndex);
1520
- const fov = this.zoomMultiplierToFOV(zoomMultiplier);
1534
+ const focalLength = this.interpolateFocalLength(sliderIndex);
1535
+ const fov = this.focalLengthToFOV(focalLength);
1521
1536
  this.currentFOV = fov;
1522
1537
  this.updateDisplay();
1523
1538
  this.emitChange();
@@ -1525,24 +1540,35 @@ var CameraFOVController = class {
1525
1540
  this.sensorWidth = opts.sensorWidth ?? 36;
1526
1541
  this.focalLengthPresets = opts.focalLengthPresets ?? [112, 56, 14, 7, 3, 1];
1527
1542
  if (opts.minFOV === void 0 || opts.maxFOV === void 0) {
1528
- const minZoom = Math.min(...this.focalLengthPresets);
1529
- const maxZoom = Math.max(...this.focalLengthPresets);
1530
- this.maxFOV = this.zoomMultiplierToFOVDirect(minZoom);
1531
- this.minFOV = this.zoomMultiplierToFOVDirect(maxZoom);
1543
+ const minFocalLength = Math.min(...this.focalLengthPresets);
1544
+ const maxFocalLength = Math.max(...this.focalLengthPresets);
1545
+ this.maxFOV = this.focalLengthToFOVDirect(minFocalLength);
1546
+ this.minFOV = this.focalLengthToFOVDirect(maxFocalLength);
1532
1547
  } else {
1533
1548
  this.minFOV = opts.minFOV;
1534
1549
  this.maxFOV = opts.maxFOV;
1535
1550
  }
1536
- this.currentFOV = opts.initialFOV ?? this.zoomMultiplierToFOVDirect(3);
1551
+ this.currentFOV = opts.initialFOV ?? this.focalLengthToFOVDirect(56);
1537
1552
  this.useGlobalEventBus = opts.useGlobalEventBus ?? true;
1538
1553
  this.createUI();
1554
+ this.setupExternalFOVListener();
1539
1555
  }
1540
1556
  /**
1541
- * 变焦倍数转换为 FOV(度)- 直接计算版本
1557
+ * 🆕 监听外部 FOV 变化事件(来自 UI 面板等),同步更新滑块位置
1542
1558
  */
1543
- zoomMultiplierToFOVDirect(zoomMultiplier) {
1544
- const focalLength = zoomMultiplier * this.sensorWidth;
1545
- const fovRad = 2 * Math.atan(this.sensorWidth / (2 * focalLength));
1559
+ setupExternalFOVListener() {
1560
+ if (!this.useGlobalEventBus) return;
1561
+ globalCameraEventBus.onFOVChange.on((event) => {
1562
+ if (event.source !== "controller") {
1563
+ this.setFOVSilent(event.fov);
1564
+ }
1565
+ });
1566
+ }
1567
+ /**
1568
+ * 焦距转换为 FOV(度)- 直接计算版本
1569
+ */
1570
+ focalLengthToFOVDirect(focalLengthMm) {
1571
+ const fovRad = 2 * Math.atan(this.sensorWidth / (2 * focalLengthMm));
1546
1572
  return fovRad * 180 / Math.PI;
1547
1573
  }
1548
1574
  /**
@@ -1666,7 +1692,7 @@ var CameraFOVController = class {
1666
1692
  `;
1667
1693
  this.focalLengthPresets.forEach((fl) => {
1668
1694
  const marker = document.createElement("div");
1669
- marker.textContent = `${fl}x`;
1695
+ marker.textContent = `${fl}`;
1670
1696
  marker.style.cssText = `
1671
1697
  cursor: pointer;
1672
1698
  padding: 2px 4px;
@@ -1681,7 +1707,7 @@ var CameraFOVController = class {
1681
1707
  marker.style.background = "transparent";
1682
1708
  });
1683
1709
  marker.addEventListener("click", () => {
1684
- this.setZoomMultiplier(fl);
1710
+ this.setFocalLength(fl);
1685
1711
  });
1686
1712
  markersContainer.appendChild(marker);
1687
1713
  });
@@ -1694,38 +1720,38 @@ var CameraFOVController = class {
1694
1720
  this.emitChange();
1695
1721
  }
1696
1722
  /**
1697
- * 根据索引插值计算变焦倍数
1723
+ * 根据索引插值计算焦距值
1698
1724
  */
1699
- interpolateZoomMultiplier(index) {
1725
+ interpolateFocalLength(index) {
1700
1726
  const clampedIndex = Math.max(0, Math.min(this.focalLengthPresets.length - 1, index));
1701
1727
  if (Number.isInteger(clampedIndex)) {
1702
1728
  return this.focalLengthPresets[clampedIndex];
1703
1729
  }
1704
1730
  const lowerIndex = Math.floor(clampedIndex);
1705
1731
  const upperIndex = Math.ceil(clampedIndex);
1706
- const lowerZoom = this.focalLengthPresets[lowerIndex];
1707
- const upperZoom = this.focalLengthPresets[upperIndex];
1732
+ const lowerFL = this.focalLengthPresets[lowerIndex];
1733
+ const upperFL = this.focalLengthPresets[upperIndex];
1708
1734
  const fraction = clampedIndex - lowerIndex;
1709
- return lowerZoom + (upperZoom - lowerZoom) * fraction;
1735
+ return lowerFL + (upperFL - lowerFL) * fraction;
1710
1736
  }
1711
1737
  /**
1712
- * 根据变焦倍数计算对应的索引(可以是小数)
1738
+ * 根据焦距值计算对应的索引(可以是小数)
1713
1739
  */
1714
- getZoomMultiplierIndex(zoomMultiplier) {
1715
- const minZoom = Math.min(...this.focalLengthPresets);
1716
- const maxZoom = Math.max(...this.focalLengthPresets);
1717
- const clampedZoom = Math.max(minZoom, Math.min(maxZoom, zoomMultiplier));
1740
+ getFocalLengthIndex(focalLength) {
1741
+ const minFL = Math.min(...this.focalLengthPresets);
1742
+ const maxFL = Math.max(...this.focalLengthPresets);
1743
+ const clampedFL = Math.max(minFL, Math.min(maxFL, focalLength));
1718
1744
  for (let i = 0; i < this.focalLengthPresets.length - 1; i++) {
1719
1745
  const current = this.focalLengthPresets[i];
1720
1746
  const next = this.focalLengthPresets[i + 1];
1721
- if (current <= clampedZoom && clampedZoom <= next || current >= clampedZoom && clampedZoom >= next) {
1722
- const fraction = (clampedZoom - current) / (next - current);
1747
+ if (current <= clampedFL && clampedFL <= next || current >= clampedFL && clampedFL >= next) {
1748
+ const fraction = (clampedFL - current) / (next - current);
1723
1749
  return i + fraction;
1724
1750
  }
1725
1751
  }
1726
1752
  return this.focalLengthPresets.indexOf(
1727
1753
  this.focalLengthPresets.reduce(
1728
- (prev, curr) => Math.abs(curr - clampedZoom) < Math.abs(prev - clampedZoom) ? curr : prev
1754
+ (prev, curr) => Math.abs(curr - clampedFL) < Math.abs(prev - clampedFL) ? curr : prev
1729
1755
  )
1730
1756
  );
1731
1757
  }
@@ -1734,8 +1760,7 @@ var CameraFOVController = class {
1734
1760
  */
1735
1761
  getClosestPresetIndex(fov) {
1736
1762
  const focalLength = this.fovToFocalLength(fov);
1737
- const zoomMultiplier = focalLength / this.sensorWidth;
1738
- const index = this.getZoomMultiplierIndex(zoomMultiplier);
1763
+ const index = this.getFocalLengthIndex(focalLength);
1739
1764
  return this.focalLengthPresets.length - 1 - index;
1740
1765
  }
1741
1766
  /**
@@ -1743,9 +1768,8 @@ var CameraFOVController = class {
1743
1768
  */
1744
1769
  updateDisplay() {
1745
1770
  const focalLength = this.fovToFocalLength(this.currentFOV);
1746
- const zoomMultiplier = focalLength / this.sensorWidth;
1747
1771
  if (this.labelEl) {
1748
- this.labelEl.textContent = `\u53D8\u7126 ${zoomMultiplier.toFixed(1)}X`;
1772
+ this.labelEl.textContent = `\u7126\u8DDD ${Math.round(focalLength)}`;
1749
1773
  }
1750
1774
  }
1751
1775
  /**
@@ -1816,6 +1840,18 @@ var CameraFOVController = class {
1816
1840
  this.updateDisplay();
1817
1841
  this.emitChange();
1818
1842
  }
1843
+ /**
1844
+ * 🆕 静默设置 FOV(度)- 只更新滑块位置,不广播事件
1845
+ * 用于响应外部 FOV 变化事件,避免循环广播
1846
+ */
1847
+ setFOVSilent(fovDeg) {
1848
+ this.currentFOV = Math.max(this.minFOV, Math.min(this.maxFOV, fovDeg));
1849
+ if (this.sliderEl) {
1850
+ const index = this.getClosestPresetIndex(this.currentFOV);
1851
+ this.sliderEl.value = String(index);
1852
+ }
1853
+ this.updateDisplay();
1854
+ }
1819
1855
  /**
1820
1856
  * 获取当前 FOV
1821
1857
  */
@@ -2854,11 +2890,11 @@ var AirplaneCursor = class {
2854
2890
  setPos(addVec(pose.position, C.Cartesian3.multiplyByScalar(e3, step, new C.Cartesian3())));
2855
2891
  moved = true;
2856
2892
  }
2857
- if (this.keysPressed.has("z")) {
2893
+ if (this.keysPressed.has("c")) {
2858
2894
  setPos(addVec(pose.position, C.Cartesian3.multiplyByScalar(u3, step, new C.Cartesian3())));
2859
2895
  moved = true;
2860
2896
  }
2861
- if (this.keysPressed.has("c")) {
2897
+ if (this.keysPressed.has("z")) {
2862
2898
  setPos(addVec(pose.position, C.Cartesian3.multiplyByScalar(u3, -step, new C.Cartesian3())));
2863
2899
  moved = true;
2864
2900
  }
@@ -2882,6 +2918,7 @@ var AirplaneCursor = class {
2882
2918
  this.opts?.onPose?.({ ...pose });
2883
2919
  this.viewer.scene?.requestRender?.();
2884
2920
  this.updateFrustum();
2921
+ this.broadcastPoseChange();
2885
2922
  }
2886
2923
  requestAnimationFrame(update);
2887
2924
  };
@@ -2939,9 +2976,15 @@ var AirplaneCursor = class {
2939
2976
  } catch {
2940
2977
  }
2941
2978
  }
2942
- /** 获取当前姿态(注意:返回的是引用对象,不要在外部直接修改其字段) */
2979
+ /** 获取当前姿态(包含高度信息) */
2943
2980
  getPose() {
2944
- return this.pose;
2981
+ const C = this.CesiumNS;
2982
+ const cartographic = C.Cartographic.fromCartesian(this.pose.position);
2983
+ const altitude = cartographic ? cartographic.height : 0;
2984
+ return {
2985
+ ...this.pose,
2986
+ altitude
2987
+ };
2945
2988
  }
2946
2989
  /** 获取内部实体(用于拾取识别) */
2947
2990
  getEntity() {
@@ -3001,8 +3044,58 @@ var AirplaneCursor = class {
3001
3044
  } catch {
3002
3045
  }
3003
3046
  this.updateFrustum();
3047
+ this.broadcastPoseChange();
3004
3048
  this.viewer.scene?.requestRender?.();
3005
3049
  }
3050
+ /**
3051
+ * 广播游标姿态变化事件到全局事件总线
3052
+ */
3053
+ broadcastPoseChange() {
3054
+ try {
3055
+ const C = this.CesiumNS;
3056
+ const cartographic = C.Cartographic.fromCartesian(this.pose.position);
3057
+ const altitude = cartographic ? cartographic.height : 0;
3058
+ console.log("[AirplaneCursor] \u{1F4E1} \u5E7F\u64AD\u59FF\u6001\u53D8\u5316:", {
3059
+ heading: this.pose.heading,
3060
+ pitch: this.pose.pitch,
3061
+ altitude
3062
+ });
3063
+ globalCameraEventBus.onCursorPoseChange.emit({
3064
+ position: this.pose.position,
3065
+ heading: this.pose.heading,
3066
+ pitch: this.pose.pitch,
3067
+ roll: this.pose.roll,
3068
+ altitude,
3069
+ source: "cursor"
3070
+ });
3071
+ } catch (e) {
3072
+ }
3073
+ }
3074
+ /**
3075
+ * 模拟按键按下(用于虚拟控制器)
3076
+ * @param key 按键名称 (w/a/s/d/q/e/c/z)
3077
+ * @param duration 按键持续时间(毫秒),默认 100ms
3078
+ */
3079
+ simulateKeyPress(key, duration = 100) {
3080
+ const lowerKey = key.toLowerCase();
3081
+ const keyMap = {
3082
+ "c": "c",
3083
+ // 上升
3084
+ "z": "z"
3085
+ // 下降
3086
+ };
3087
+ const mappedKey = keyMap[lowerKey] || lowerKey;
3088
+ console.log("[AirplaneCursor] \u{1F3AE} simulateKeyPress:", mappedKey, "duration:", duration);
3089
+ console.log("[AirplaneCursor] \u{1F3AE} updateLoopRunning:", this.updateLoopRunning);
3090
+ this.keysPressed.add(mappedKey);
3091
+ if (!this.updateLoopRunning) {
3092
+ console.log("[AirplaneCursor] \u{1F3AE} \u542F\u52A8 updateLoop");
3093
+ this.startUpdateLoop();
3094
+ }
3095
+ setTimeout(() => {
3096
+ this.keysPressed.delete(mappedKey);
3097
+ }, duration);
3098
+ }
3006
3099
  destroy() {
3007
3100
  if (this.destroyed) return;
3008
3101
  this.destroyed = true;
@@ -4832,6 +4925,10 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4832
4925
  } catch {
4833
4926
  }
4834
4927
  };
4928
+ const getTotalDistance = () => {
4929
+ if (positions.length < 2) return 0;
4930
+ return calculatePathDistance(CesiumNS, positions, positions.length - 1, hiddenClimbIndex);
4931
+ };
4835
4932
  const vertexInsertionHandler = new VertexInsertionHandler({
4836
4933
  CesiumNS,
4837
4934
  layer,
@@ -4842,6 +4939,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4842
4939
  if (!cursorStart) cursorStart = positions[hiddenClimbIndex === 1 ? 1 : 0];
4843
4940
  let airplaneCursor;
4844
4941
  const insertVertex = (insertAt, p3, poseOrient) => {
4942
+ console.log("[PathEditing] \u63D2\u5165\u9876\u70B9\u524D positions \u957F\u5EA6:", positions.length, "insertAt:", insertAt);
4845
4943
  const actualIndex = vertexInsertionHandler.insertVertex(
4846
4944
  insertAt,
4847
4945
  p3,
@@ -4857,6 +4955,8 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4857
4955
  airplaneCursor,
4858
4956
  poseOrient
4859
4957
  );
4958
+ console.log("[PathEditing] \u63D2\u5165\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length, "actualIndex:", actualIndex);
4959
+ entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
4860
4960
  setActiveIndex(actualIndex);
4861
4961
  createOrUpdateMarkerForIndex(actualIndex);
4862
4962
  };
@@ -4887,6 +4987,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4887
4987
  } catch {
4888
4988
  }
4889
4989
  }
4990
+ console.log("[PathEditing] \u5220\u9664\u9876\u70B9\u524D positions \u957F\u5EA6:", positions.length, "deleteAt:", deleteAt);
4890
4991
  positions.splice(deleteAt, 1);
4891
4992
  handles.splice(deleteAt, 1);
4892
4993
  headings.splice(deleteAt, 1);
@@ -4894,6 +4995,8 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4894
4995
  rolls.splice(deleteAt, 1);
4895
4996
  fovs.splice(deleteAt, 1);
4896
4997
  heightMarkers.splice(deleteAt, 1);
4998
+ console.log("[PathEditing] \u5220\u9664\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length);
4999
+ entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
4897
5000
  const newEditedIndices = /* @__PURE__ */ new Set();
4898
5001
  editedIndices.forEach((idx) => {
4899
5002
  if (idx < deleteAt) {
@@ -5016,7 +5119,9 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5016
5119
  totalVerticesBefore: totalBefore,
5017
5120
  totalVerticesAfter: totalAfter,
5018
5121
  newVertex,
5019
- timestamp: /* @__PURE__ */ new Date()
5122
+ timestamp: /* @__PURE__ */ new Date(),
5123
+ totalDistance: getTotalDistance()
5124
+ // 🆕 航线总里程
5020
5125
  };
5021
5126
  options.onVertexInsertDetail(operationInfo);
5022
5127
  } catch (error) {
@@ -5037,7 +5142,9 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5037
5142
  displayNumber,
5038
5143
  totalVerticesBefore: totalBefore,
5039
5144
  totalVerticesAfter: totalAfter,
5040
- timestamp: /* @__PURE__ */ new Date()
5145
+ timestamp: /* @__PURE__ */ new Date(),
5146
+ totalDistance: getTotalDistance()
5147
+ // 🆕 航线总里程
5041
5148
  };
5042
5149
  options.onVertexDeleteDetail(operationInfo);
5043
5150
  } catch (error) {
@@ -5065,6 +5172,31 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5065
5172
  vertexLabelManager.updateLabelPosition(index, newPosition);
5066
5173
  } catch {
5067
5174
  }
5175
+ if (options?.onVertexDragMoveDetail) {
5176
+ try {
5177
+ let coordinates = { longitude: 0, latitude: 0, height: 0 };
5178
+ try {
5179
+ const cartographic = C.Cartographic.fromCartesian(newPosition);
5180
+ coordinates = {
5181
+ longitude: C.Math.toDegrees(cartographic.longitude),
5182
+ latitude: C.Math.toDegrees(cartographic.latitude),
5183
+ height: cartographic.height
5184
+ };
5185
+ } catch {
5186
+ }
5187
+ const displayNumber = hiddenClimbIndex === 1 && index > 1 ? index - 1 : index;
5188
+ const dragMoveInfo = {
5189
+ index,
5190
+ displayNumber,
5191
+ position: newPosition,
5192
+ coordinates,
5193
+ timestamp: /* @__PURE__ */ new Date()
5194
+ };
5195
+ options.onVertexDragMoveDetail(dragMoveInfo);
5196
+ } catch (error) {
5197
+ console.error("Error in onVertexDragMoveDetail:", error);
5198
+ }
5199
+ }
5068
5200
  },
5069
5201
  onVertexDragEnd: (index, finalPosition) => {
5070
5202
  editedIndices.add(index);
@@ -5092,7 +5224,9 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5092
5224
  totalVerticesAfter: positions.length,
5093
5225
  newVertex: vertexInfo,
5094
5226
  newPosition: finalPosition,
5095
- timestamp: /* @__PURE__ */ new Date()
5227
+ timestamp: /* @__PURE__ */ new Date(),
5228
+ totalDistance: getTotalDistance()
5229
+ // 🆕 航线总里程
5096
5230
  };
5097
5231
  options.onVertexDragCompleteDetail(operationInfo);
5098
5232
  } catch (error) {
@@ -5105,6 +5239,12 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5105
5239
  getAirplaneCursor: () => airplaneCursor,
5106
5240
  getPoseData: () => ({ headings, pitches, rolls, fovs }),
5107
5241
  getEntity: () => entity,
5242
+ /** 模拟按键控制飞机游标(用于虚拟控制器) */
5243
+ simulateKeyPress: (key, duration) => {
5244
+ airplaneCursor?.simulateKeyPress(key, duration);
5245
+ },
5246
+ /** 获取游标当前姿态(包含高度) */
5247
+ getCursorPose: () => airplaneCursor?.getPose(),
5108
5248
  // 🆕 快速编辑回调
5109
5249
  getQuickEditEnabled: () => quickEditEnabled,
5110
5250
  getQuickEditOptions: () => ({
@@ -5115,6 +5255,18 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5115
5255
  );
5116
5256
  const cleanupSession = () => {
5117
5257
  setActiveIndex(void 0);
5258
+ try {
5259
+ handles.forEach((handle) => {
5260
+ if (handle) {
5261
+ try {
5262
+ layer.entities.remove(handle);
5263
+ } catch {
5264
+ }
5265
+ }
5266
+ });
5267
+ handles.length = 0;
5268
+ } catch {
5269
+ }
5118
5270
  try {
5119
5271
  heightMarkers.forEach((m) => m?.destroy());
5120
5272
  } catch {
@@ -5287,9 +5439,214 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5287
5439
  /** 🆕 检查快速编辑模式是否启用 */
5288
5440
  isQuickEditEnabled: () => {
5289
5441
  return quickEditEnabled;
5442
+ },
5443
+ // ========================
5444
+ // 🆕 程序化更新方法
5445
+ // ========================
5446
+ /**
5447
+ * 🆕 程序化更新指定航点
5448
+ * @param index 航点索引
5449
+ * @param updates 需要更新的字段
5450
+ * @returns 是否更新成功
5451
+ */
5452
+ updateWaypoint: (index, updates) => {
5453
+ if (index < 0 || index >= positions.length) {
5454
+ console.warn(`[updateWaypoint] Invalid index: ${index}, valid range: 0-${positions.length - 1}`);
5455
+ return false;
5456
+ }
5457
+ try {
5458
+ let positionUpdated = false;
5459
+ if (updates.position) {
5460
+ positions[index] = updates.position;
5461
+ positionUpdated = true;
5462
+ }
5463
+ if (updates.altitude !== void 0 && !updates.position) {
5464
+ const currentPos = positions[index];
5465
+ const cartographic = C.Cartographic.fromCartesian(currentPos);
5466
+ const newPos = C.Cartesian3.fromRadians(
5467
+ cartographic.longitude,
5468
+ cartographic.latitude,
5469
+ updates.altitude
5470
+ );
5471
+ positions[index] = newPos;
5472
+ positionUpdated = true;
5473
+ }
5474
+ if (positionUpdated) {
5475
+ const newPos = positions[index];
5476
+ if (handles[index]) {
5477
+ try {
5478
+ handles[index].position = newPos;
5479
+ } catch {
5480
+ }
5481
+ }
5482
+ try {
5483
+ vertexLabelManager.updateLabelPosition(index, newPos);
5484
+ } catch {
5485
+ }
5486
+ try {
5487
+ createOrUpdateMarkerForIndex(index);
5488
+ } catch {
5489
+ }
5490
+ }
5491
+ if (updates.heading !== void 0) headings[index] = updates.heading;
5492
+ if (updates.pitch !== void 0) pitches[index] = updates.pitch;
5493
+ if (updates.roll !== void 0) rolls[index] = updates.roll;
5494
+ if (updates.fov !== void 0) {
5495
+ fovs[index] = updates.fov;
5496
+ if (index === activeIndex) {
5497
+ globalCameraEventBus.onFOVChange.emit({
5498
+ fov: updates.fov,
5499
+ focalLength: fovToFocalLength(updates.fov),
5500
+ source: "user"
5501
+ });
5502
+ }
5503
+ }
5504
+ if (index === activeIndex && airplaneCursor) {
5505
+ try {
5506
+ airplaneCursor.setPose(
5507
+ positions[index],
5508
+ headings[index] ?? 0,
5509
+ pitches[index] ?? -10,
5510
+ rolls[index] ?? 0
5511
+ );
5512
+ } catch {
5513
+ }
5514
+ }
5515
+ stateManager.persistToEntity(positions, headings, pitches, rolls, fovs);
5516
+ return true;
5517
+ } catch (error) {
5518
+ console.error("[updateWaypoint] Error:", error);
5519
+ return false;
5520
+ }
5521
+ },
5522
+ /**
5523
+ * 🆕 更新航点位置
5524
+ */
5525
+ updateWaypointPosition: function(index, position) {
5526
+ return this.updateWaypoint(index, { position });
5527
+ },
5528
+ /**
5529
+ * 🆕 更新航点高度(仅垂直方向,保持经纬度不变)
5530
+ */
5531
+ updateWaypointAltitude: function(index, altitude) {
5532
+ return this.updateWaypoint(index, { altitude });
5533
+ },
5534
+ /**
5535
+ * 🆕 更新航点偏航角
5536
+ */
5537
+ updateWaypointHeading: function(index, heading) {
5538
+ return this.updateWaypoint(index, { heading });
5539
+ },
5540
+ /**
5541
+ * 🆕 更新航点俯仰角
5542
+ */
5543
+ updateWaypointPitch: function(index, pitch) {
5544
+ return this.updateWaypoint(index, { pitch });
5545
+ },
5546
+ /**
5547
+ * 🆕 更新航点视野角
5548
+ */
5549
+ updateWaypointFov: function(index, fov) {
5550
+ return this.updateWaypoint(index, { fov });
5551
+ },
5552
+ /**
5553
+ * 🆕 批量更新多个航点
5554
+ */
5555
+ batchUpdateWaypoints: function(updates) {
5556
+ let allSuccess = true;
5557
+ for (const { index, data } of updates) {
5558
+ if (!this.updateWaypoint(index, data)) {
5559
+ allSuccess = false;
5560
+ }
5561
+ }
5562
+ return allSuccess;
5563
+ },
5564
+ /**
5565
+ * 🆕 程序化选中指定航点
5566
+ * 将飞机游标移动到指定航点位置,更新选中状态
5567
+ * @param index 航点索引
5568
+ * @returns 是否选中成功
5569
+ */
5570
+ selectVertex: (index) => {
5571
+ if (index < 0 || index >= positions.length) {
5572
+ console.warn(`[selectVertex] Invalid index: ${index}, valid range: 0-${positions.length - 1}`);
5573
+ return false;
5574
+ }
5575
+ try {
5576
+ setActiveIndex(index);
5577
+ return true;
5578
+ } catch (e) {
5579
+ console.error("[selectVertex] Failed to select vertex:", e);
5580
+ return false;
5581
+ }
5582
+ },
5583
+ /**
5584
+ * 🆕 模拟按键控制飞机游标(用于虚拟控制器)
5585
+ * @param key 按键名称 (w/a/s/d/q/e/c/z)
5586
+ * @param duration 按键持续时间(毫秒)
5587
+ */
5588
+ simulateKeyPress: (key, duration) => {
5589
+ airplaneCursor?.simulateKeyPress(key, duration);
5590
+ },
5591
+ /**
5592
+ * 🆕 获取游标当前姿态(包含高度)
5593
+ */
5594
+ getCursorPose: () => airplaneCursor?.getPose(),
5595
+ /**
5596
+ * 🆕 动态更新爬升高度(climbHeight)
5597
+ * 更新隐藏爬升点(index 1)的高度,实现安全飞行高度的动态调节
5598
+ * @param climbHeight 新的爬升高度(米)
5599
+ * @returns 是否更新成功
5600
+ */
5601
+ updateClimbHeight: function(climbHeight) {
5602
+ if (hiddenClimbIndex !== 1 || positions.length < 2) {
5603
+ console.warn("[updateClimbHeight] No hidden climb point exists");
5604
+ return false;
5605
+ }
5606
+ try {
5607
+ const startPos = positions[0];
5608
+ const startCarto = C.Cartographic.fromCartesian(startPos);
5609
+ const newAltitude = startCarto.height + climbHeight;
5610
+ const success = this.updateWaypointAltitude(1, newAltitude);
5611
+ if (success) {
5612
+ try {
5613
+ entity.properties._climbHeight = climbHeight;
5614
+ } catch {
5615
+ }
5616
+ console.log("[updateClimbHeight] \u2705 \u722C\u5347\u9AD8\u5EA6\u5DF2\u66F4\u65B0:", climbHeight, "\u7C73");
5617
+ }
5618
+ return success;
5619
+ } catch (error) {
5620
+ console.error("[updateClimbHeight] Error:", error);
5621
+ return false;
5622
+ }
5623
+ },
5624
+ /**
5625
+ * 🆕 获取起飞点信息
5626
+ * @returns 起飞点的经纬度和高度
5627
+ */
5628
+ getStartPoint: () => {
5629
+ if (positions.length < 1) return null;
5630
+ try {
5631
+ const startPos = positions[0];
5632
+ const carto = C.Cartographic.fromCartesian(startPos);
5633
+ return {
5634
+ position: startPos,
5635
+ latitude: C.Math.toDegrees(carto.latitude),
5636
+ longitude: C.Math.toDegrees(carto.longitude),
5637
+ altitude: carto.height
5638
+ };
5639
+ } catch {
5640
+ return null;
5641
+ }
5290
5642
  }
5291
5643
  };
5292
5644
  }
5645
+ function fovToFocalLength(fovDeg) {
5646
+ const sensorWidth = 36;
5647
+ const fovRad = fovDeg * Math.PI / 180;
5648
+ return sensorWidth / (2 * Math.tan(fovRad / 2));
5649
+ }
5293
5650
 
5294
5651
  // src/core/path-manager/ArrowShape.ts
5295
5652
  var ArrowShape = class {
@@ -5458,6 +5815,7 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
5458
5815
  const DEFAULT_MAIN_WIDTH = options?.width ?? 6;
5459
5816
  let startCartographic;
5460
5817
  let hasStart = false;
5818
+ let createdEntity = void 0;
5461
5819
  const getPositionFromMouse = (movement) => {
5462
5820
  const scene = viewer.scene;
5463
5821
  const winPos = movement?.position ?? movement?.endPosition ?? movement;
@@ -5558,6 +5916,7 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
5558
5916
  _hasHiddenClimb: climbHeight > 0
5559
5917
  }
5560
5918
  });
5919
+ createdEntity = created;
5561
5920
  try {
5562
5921
  tempHandles.forEach((hh, idx) => {
5563
5922
  try {
@@ -5594,8 +5953,21 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
5594
5953
  } else {
5595
5954
  editOptions.preview = { enabled: true };
5596
5955
  }
5597
- if (options?.onVertexSelectDetail) {
5598
- editOptions.onVertexSelectDetail = options.onVertexSelectDetail;
5956
+ const autoOpts = typeof auto === "object" ? auto : {};
5957
+ if (autoOpts.onVertexSelectDetail) {
5958
+ editOptions.onVertexSelectDetail = autoOpts.onVertexSelectDetail;
5959
+ }
5960
+ if (autoOpts.onVertexDragMoveDetail) {
5961
+ editOptions.onVertexDragMoveDetail = autoOpts.onVertexDragMoveDetail;
5962
+ }
5963
+ if (autoOpts.onVertexDragCompleteDetail) {
5964
+ editOptions.onVertexDragCompleteDetail = autoOpts.onVertexDragCompleteDetail;
5965
+ }
5966
+ if (autoOpts.onVertexInsertDetail) {
5967
+ editOptions.onVertexInsertDetail = autoOpts.onVertexInsertDetail;
5968
+ }
5969
+ if (autoOpts.onVertexDeleteDetail) {
5970
+ editOptions.onVertexDeleteDetail = autoOpts.onVertexDeleteDetail;
5599
5971
  }
5600
5972
  const editSession = startPathEditing(CesiumNS, viewer, created, editOptions);
5601
5973
  if (editSession && options?.onEditingStarted) {
@@ -5621,6 +5993,12 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
5621
5993
  handler?.destroy();
5622
5994
  } catch {
5623
5995
  }
5996
+ if (createdEntity) {
5997
+ try {
5998
+ layer.entities.remove(createdEntity);
5999
+ } catch {
6000
+ }
6001
+ }
5624
6002
  }
5625
6003
  };
5626
6004
  }
@@ -5686,7 +6064,6 @@ function parsePoseFromAction(action) {
5686
6064
  fov: parseFloat(numbers[3])
5687
6065
  };
5688
6066
  }
5689
- console.warn("[sinoflyAdapter] \u65E0\u6CD5\u89E3\u6790 action \u5B57\u6BB5:", action, error);
5690
6067
  return {};
5691
6068
  }
5692
6069
  }
@@ -5704,15 +6081,40 @@ function parseTakeOffRefPoint(takeOffRefPoint, takeOffSecurityHeight, CesiumNS)
5704
6081
  if (!takeOffRefPoint || typeof takeOffRefPoint !== "string" || takeOffRefPoint.trim() === "") {
5705
6082
  return null;
5706
6083
  }
6084
+ const C = CesiumNS;
6085
+ let longitude;
6086
+ let latitude;
6087
+ let height;
6088
+ const trimmed = takeOffRefPoint.trim();
6089
+ if (trimmed.includes(",")) {
6090
+ try {
6091
+ const parts = trimmed.split(",").map((p) => p.trim());
6092
+ if (parts.length >= 2) {
6093
+ latitude = parseFloat(parts[0]);
6094
+ longitude = parseFloat(parts[1]);
6095
+ height = parts.length >= 3 ? parseFloat(parts[2]) : takeOffSecurityHeight;
6096
+ if (isNaN(latitude) || isNaN(longitude) || isNaN(height)) {
6097
+ throw new Error("\u65E0\u6CD5\u89E3\u6790\u9017\u53F7\u5206\u9694\u7684\u5750\u6807\u503C");
6098
+ }
6099
+ const startPosition = C.Cartesian3.fromDegrees(longitude, latitude, height);
6100
+ const climbPosition = C.Cartesian3.fromDegrees(
6101
+ longitude,
6102
+ latitude,
6103
+ height + takeOffSecurityHeight
6104
+ );
6105
+ return {
6106
+ startPosition,
6107
+ climbPosition
6108
+ };
6109
+ }
6110
+ } catch (error) {
6111
+ }
6112
+ }
5707
6113
  try {
5708
6114
  const parsed = JSON.parse(takeOffRefPoint);
5709
6115
  if (typeof parsed !== "object" || parsed === null) {
5710
6116
  return null;
5711
6117
  }
5712
- const C = CesiumNS;
5713
- let longitude;
5714
- let latitude;
5715
- let height;
5716
6118
  if (typeof parsed.longitude === "number") {
5717
6119
  longitude = parsed.longitude;
5718
6120
  } else if (typeof parsed.lon === "number") {
@@ -5743,7 +6145,6 @@ function parseTakeOffRefPoint(takeOffRefPoint, takeOffSecurityHeight, CesiumNS)
5743
6145
  climbPosition
5744
6146
  };
5745
6147
  } catch (error) {
5746
- console.warn("[sinoflyAdapter] \u65E0\u6CD5\u89E3\u6790 takeOffRefPoint:", takeOffRefPoint, error);
5747
6148
  return null;
5748
6149
  }
5749
6150
  }
@@ -5765,16 +6166,10 @@ function convertSinoflyWayline(data, options) {
5765
6166
  if (!data.waypointInfo || !Array.isArray(data.waypointInfo)) {
5766
6167
  throw new Error("[sinoflyAdapter] waypointInfo \u5FC5\u987B\u662F\u6570\u7EC4");
5767
6168
  }
5768
- if (data.waypointInfo.length === 0) {
5769
- console.warn("[sinoflyAdapter] waypointInfo \u6570\u7EC4\u4E3A\u7A7A\uFF0C\u5C06\u8FD4\u56DE\u7A7A\u7684\u822A\u70B9\u6570\u636E");
5770
- }
6169
+ if (data.waypointInfo.length === 0) ;
5771
6170
  if (data.waylineId === void 0 || data.waylineId === null) {
5772
6171
  throw new Error("[sinoflyAdapter] waylineId \u4E0D\u80FD\u4E3A\u7A7A");
5773
6172
  }
5774
- console.log(`[sinoflyAdapter] \u5F00\u59CB\u8F6C\u6362\u822A\u7EBF ${data.waylineId}\uFF0C\u5305\u542B ${data.waypointInfo.length} \u4E2A\u822A\u70B9`);
5775
- if (data.takeOffRefPoint) {
5776
- console.log(`[sinoflyAdapter] \u68C0\u6D4B\u5230 takeOffRefPoint: ${data.takeOffRefPoint}`);
5777
- }
5778
6173
  const takeOffInfo = parseTakeOffRefPoint(
5779
6174
  data.takeOffRefPoint,
5780
6175
  data.takeOffSecurityHeight,
@@ -5839,7 +6234,6 @@ function convertSinoflyWayline(data, options) {
5839
6234
  if (poseFromAction.roll !== void 0) roll = poseFromAction.roll;
5840
6235
  if (poseFromAction.fov !== void 0) fov = poseFromAction.fov;
5841
6236
  } catch (actionError) {
5842
- console.warn(`[sinoflyAdapter] \u822A\u70B9 ${idx} action \u89E3\u6790\u5931\u8D25:`, actionError);
5843
6237
  }
5844
6238
  }
5845
6239
  return {
@@ -5852,7 +6246,6 @@ function convertSinoflyWayline(data, options) {
5852
6246
  // 重新分配 index,从 waypointStartIndex 开始
5853
6247
  };
5854
6248
  } catch (error) {
5855
- console.error(`[sinoflyAdapter] \u822A\u70B9 ${idx} \u8F6C\u6362\u5931\u8D25:`, error);
5856
6249
  throw new Error(`\u822A\u70B9 ${idx} \u8F6C\u6362\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
5857
6250
  }
5858
6251
  });
@@ -5860,7 +6253,6 @@ function convertSinoflyWayline(data, options) {
5860
6253
  if (!Array.isArray(waypointData)) {
5861
6254
  throw new Error("[sinoflyAdapter] waypointData \u4E0D\u662F\u6570\u7EC4");
5862
6255
  }
5863
- console.log(`[sinoflyAdapter] \u8F6C\u6362\u5B8C\u6210\uFF0C\u5171\u751F\u6210 ${waypointData.length} \u4E2A\u822A\u70B9`);
5864
6256
  const metadata = {
5865
6257
  waylineId: data.waylineId,
5866
6258
  waylineType: data.waylineType,