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