@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/index.js CHANGED
@@ -11,7 +11,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@jorgmoritz/gis-manager",
14
- version: "0.1.21"};
14
+ version: "0.1.26"};
15
15
 
16
16
  // src/utils/version.ts
17
17
  var version = package_default.version;
@@ -104,6 +104,8 @@ var CameraEventBus = class {
104
104
  __publicField(this, "onPoseChange", new Emitter());
105
105
  /** 视锥体形状变化事件 */
106
106
  __publicField(this, "onFrustumShapeChange", new Emitter());
107
+ /** 飞机游标姿态变化事件 */
108
+ __publicField(this, "onCursorPoseChange", new Emitter());
107
109
  }
108
110
  /**
109
111
  * 清理所有监听器
@@ -112,9 +114,20 @@ var CameraEventBus = class {
112
114
  this.onFOVChange.clear();
113
115
  this.onPoseChange.clear();
114
116
  this.onFrustumShapeChange.clear();
117
+ this.onCursorPoseChange.clear();
115
118
  }
116
119
  };
117
- var globalCameraEventBus = new CameraEventBus();
120
+ var GLOBAL_KEY = "__gisManager_globalCameraEventBus__";
121
+ function getGlobalEventBus() {
122
+ if (typeof window !== "undefined") {
123
+ if (!window[GLOBAL_KEY]) {
124
+ window[GLOBAL_KEY] = new CameraEventBus();
125
+ }
126
+ return window[GLOBAL_KEY];
127
+ }
128
+ return new CameraEventBus();
129
+ }
130
+ var globalCameraEventBus = getGlobalEventBus();
118
131
 
119
132
  // src/core/LayerManager.ts
120
133
  var layerIdSeq = 0;
@@ -1122,7 +1135,9 @@ var SceneManager = class {
1122
1135
  depth: true,
1123
1136
  stencil: true,
1124
1137
  antialias: true,
1125
- powerPreference: "high-performance"
1138
+ powerPreference: "high-performance",
1139
+ preserveDrawingBuffer: true
1140
+ // 必需:允许 canvas.toDataURL() 截图
1126
1141
  }
1127
1142
  },
1128
1143
  ...viewerOptions
@@ -1560,8 +1575,8 @@ var CameraFOVController = class {
1560
1575
  if (!this.sliderEl) return;
1561
1576
  const sliderValue = parseFloat(this.sliderEl.value);
1562
1577
  const sliderIndex = this.focalLengthPresets.length - 1 - sliderValue;
1563
- const zoomMultiplier = this.interpolateZoomMultiplier(sliderIndex);
1564
- const fov = this.zoomMultiplierToFOV(zoomMultiplier);
1578
+ const focalLength = this.interpolateFocalLength(sliderIndex);
1579
+ const fov = this.focalLengthToFOV(focalLength);
1565
1580
  this.currentFOV = fov;
1566
1581
  this.updateDisplay();
1567
1582
  this.emitChange();
@@ -1569,24 +1584,35 @@ var CameraFOVController = class {
1569
1584
  this.sensorWidth = opts.sensorWidth ?? 36;
1570
1585
  this.focalLengthPresets = opts.focalLengthPresets ?? [112, 56, 14, 7, 3, 1];
1571
1586
  if (opts.minFOV === void 0 || opts.maxFOV === void 0) {
1572
- const minZoom = Math.min(...this.focalLengthPresets);
1573
- const maxZoom = Math.max(...this.focalLengthPresets);
1574
- this.maxFOV = this.zoomMultiplierToFOVDirect(minZoom);
1575
- this.minFOV = this.zoomMultiplierToFOVDirect(maxZoom);
1587
+ const minFocalLength = Math.min(...this.focalLengthPresets);
1588
+ const maxFocalLength = Math.max(...this.focalLengthPresets);
1589
+ this.maxFOV = this.focalLengthToFOVDirect(minFocalLength);
1590
+ this.minFOV = this.focalLengthToFOVDirect(maxFocalLength);
1576
1591
  } else {
1577
1592
  this.minFOV = opts.minFOV;
1578
1593
  this.maxFOV = opts.maxFOV;
1579
1594
  }
1580
- this.currentFOV = opts.initialFOV ?? this.zoomMultiplierToFOVDirect(3);
1595
+ this.currentFOV = opts.initialFOV ?? this.focalLengthToFOVDirect(56);
1581
1596
  this.useGlobalEventBus = opts.useGlobalEventBus ?? true;
1582
1597
  this.createUI();
1598
+ this.setupExternalFOVListener();
1583
1599
  }
1584
1600
  /**
1585
- * 变焦倍数转换为 FOV(度)- 直接计算版本
1601
+ * 🆕 监听外部 FOV 变化事件(来自 UI 面板等),同步更新滑块位置
1586
1602
  */
1587
- zoomMultiplierToFOVDirect(zoomMultiplier) {
1588
- const focalLength = zoomMultiplier * this.sensorWidth;
1589
- const fovRad = 2 * Math.atan(this.sensorWidth / (2 * focalLength));
1603
+ setupExternalFOVListener() {
1604
+ if (!this.useGlobalEventBus) return;
1605
+ globalCameraEventBus.onFOVChange.on((event) => {
1606
+ if (event.source !== "controller") {
1607
+ this.setFOVSilent(event.fov);
1608
+ }
1609
+ });
1610
+ }
1611
+ /**
1612
+ * 焦距转换为 FOV(度)- 直接计算版本
1613
+ */
1614
+ focalLengthToFOVDirect(focalLengthMm) {
1615
+ const fovRad = 2 * Math.atan(this.sensorWidth / (2 * focalLengthMm));
1590
1616
  return fovRad * 180 / Math.PI;
1591
1617
  }
1592
1618
  /**
@@ -1710,7 +1736,7 @@ var CameraFOVController = class {
1710
1736
  `;
1711
1737
  this.focalLengthPresets.forEach((fl) => {
1712
1738
  const marker = document.createElement("div");
1713
- marker.textContent = `${fl}x`;
1739
+ marker.textContent = `${fl}`;
1714
1740
  marker.style.cssText = `
1715
1741
  cursor: pointer;
1716
1742
  padding: 2px 4px;
@@ -1725,7 +1751,7 @@ var CameraFOVController = class {
1725
1751
  marker.style.background = "transparent";
1726
1752
  });
1727
1753
  marker.addEventListener("click", () => {
1728
- this.setZoomMultiplier(fl);
1754
+ this.setFocalLength(fl);
1729
1755
  });
1730
1756
  markersContainer.appendChild(marker);
1731
1757
  });
@@ -1738,38 +1764,38 @@ var CameraFOVController = class {
1738
1764
  this.emitChange();
1739
1765
  }
1740
1766
  /**
1741
- * 根据索引插值计算变焦倍数
1767
+ * 根据索引插值计算焦距值
1742
1768
  */
1743
- interpolateZoomMultiplier(index) {
1769
+ interpolateFocalLength(index) {
1744
1770
  const clampedIndex = Math.max(0, Math.min(this.focalLengthPresets.length - 1, index));
1745
1771
  if (Number.isInteger(clampedIndex)) {
1746
1772
  return this.focalLengthPresets[clampedIndex];
1747
1773
  }
1748
1774
  const lowerIndex = Math.floor(clampedIndex);
1749
1775
  const upperIndex = Math.ceil(clampedIndex);
1750
- const lowerZoom = this.focalLengthPresets[lowerIndex];
1751
- const upperZoom = this.focalLengthPresets[upperIndex];
1776
+ const lowerFL = this.focalLengthPresets[lowerIndex];
1777
+ const upperFL = this.focalLengthPresets[upperIndex];
1752
1778
  const fraction = clampedIndex - lowerIndex;
1753
- return lowerZoom + (upperZoom - lowerZoom) * fraction;
1779
+ return lowerFL + (upperFL - lowerFL) * fraction;
1754
1780
  }
1755
1781
  /**
1756
- * 根据变焦倍数计算对应的索引(可以是小数)
1782
+ * 根据焦距值计算对应的索引(可以是小数)
1757
1783
  */
1758
- getZoomMultiplierIndex(zoomMultiplier) {
1759
- const minZoom = Math.min(...this.focalLengthPresets);
1760
- const maxZoom = Math.max(...this.focalLengthPresets);
1761
- const clampedZoom = Math.max(minZoom, Math.min(maxZoom, zoomMultiplier));
1784
+ getFocalLengthIndex(focalLength) {
1785
+ const minFL = Math.min(...this.focalLengthPresets);
1786
+ const maxFL = Math.max(...this.focalLengthPresets);
1787
+ const clampedFL = Math.max(minFL, Math.min(maxFL, focalLength));
1762
1788
  for (let i = 0; i < this.focalLengthPresets.length - 1; i++) {
1763
1789
  const current = this.focalLengthPresets[i];
1764
1790
  const next = this.focalLengthPresets[i + 1];
1765
- if (current <= clampedZoom && clampedZoom <= next || current >= clampedZoom && clampedZoom >= next) {
1766
- const fraction = (clampedZoom - current) / (next - current);
1791
+ if (current <= clampedFL && clampedFL <= next || current >= clampedFL && clampedFL >= next) {
1792
+ const fraction = (clampedFL - current) / (next - current);
1767
1793
  return i + fraction;
1768
1794
  }
1769
1795
  }
1770
1796
  return this.focalLengthPresets.indexOf(
1771
1797
  this.focalLengthPresets.reduce(
1772
- (prev, curr) => Math.abs(curr - clampedZoom) < Math.abs(prev - clampedZoom) ? curr : prev
1798
+ (prev, curr) => Math.abs(curr - clampedFL) < Math.abs(prev - clampedFL) ? curr : prev
1773
1799
  )
1774
1800
  );
1775
1801
  }
@@ -1778,8 +1804,7 @@ var CameraFOVController = class {
1778
1804
  */
1779
1805
  getClosestPresetIndex(fov) {
1780
1806
  const focalLength = this.fovToFocalLength(fov);
1781
- const zoomMultiplier = focalLength / this.sensorWidth;
1782
- const index = this.getZoomMultiplierIndex(zoomMultiplier);
1807
+ const index = this.getFocalLengthIndex(focalLength);
1783
1808
  return this.focalLengthPresets.length - 1 - index;
1784
1809
  }
1785
1810
  /**
@@ -1787,9 +1812,8 @@ var CameraFOVController = class {
1787
1812
  */
1788
1813
  updateDisplay() {
1789
1814
  const focalLength = this.fovToFocalLength(this.currentFOV);
1790
- const zoomMultiplier = focalLength / this.sensorWidth;
1791
1815
  if (this.labelEl) {
1792
- this.labelEl.textContent = `\u53D8\u7126 ${zoomMultiplier.toFixed(1)}X`;
1816
+ this.labelEl.textContent = `\u7126\u8DDD ${Math.round(focalLength)}`;
1793
1817
  }
1794
1818
  }
1795
1819
  /**
@@ -1860,6 +1884,18 @@ var CameraFOVController = class {
1860
1884
  this.updateDisplay();
1861
1885
  this.emitChange();
1862
1886
  }
1887
+ /**
1888
+ * 🆕 静默设置 FOV(度)- 只更新滑块位置,不广播事件
1889
+ * 用于响应外部 FOV 变化事件,避免循环广播
1890
+ */
1891
+ setFOVSilent(fovDeg) {
1892
+ this.currentFOV = Math.max(this.minFOV, Math.min(this.maxFOV, fovDeg));
1893
+ if (this.sliderEl) {
1894
+ const index = this.getClosestPresetIndex(this.currentFOV);
1895
+ this.sliderEl.value = String(index);
1896
+ }
1897
+ this.updateDisplay();
1898
+ }
1863
1899
  /**
1864
1900
  * 获取当前 FOV
1865
1901
  */
@@ -2898,11 +2934,11 @@ var AirplaneCursor = class {
2898
2934
  setPos(addVec(pose.position, C.Cartesian3.multiplyByScalar(e3, step, new C.Cartesian3())));
2899
2935
  moved = true;
2900
2936
  }
2901
- if (this.keysPressed.has("z")) {
2937
+ if (this.keysPressed.has("c")) {
2902
2938
  setPos(addVec(pose.position, C.Cartesian3.multiplyByScalar(u3, step, new C.Cartesian3())));
2903
2939
  moved = true;
2904
2940
  }
2905
- if (this.keysPressed.has("c")) {
2941
+ if (this.keysPressed.has("z")) {
2906
2942
  setPos(addVec(pose.position, C.Cartesian3.multiplyByScalar(u3, -step, new C.Cartesian3())));
2907
2943
  moved = true;
2908
2944
  }
@@ -2926,6 +2962,7 @@ var AirplaneCursor = class {
2926
2962
  this.opts?.onPose?.({ ...pose });
2927
2963
  this.viewer.scene?.requestRender?.();
2928
2964
  this.updateFrustum();
2965
+ this.broadcastPoseChange();
2929
2966
  }
2930
2967
  requestAnimationFrame(update);
2931
2968
  };
@@ -2983,9 +3020,15 @@ var AirplaneCursor = class {
2983
3020
  } catch {
2984
3021
  }
2985
3022
  }
2986
- /** 获取当前姿态(注意:返回的是引用对象,不要在外部直接修改其字段) */
3023
+ /** 获取当前姿态(包含高度信息) */
2987
3024
  getPose() {
2988
- return this.pose;
3025
+ const C = this.CesiumNS;
3026
+ const cartographic = C.Cartographic.fromCartesian(this.pose.position);
3027
+ const altitude = cartographic ? cartographic.height : 0;
3028
+ return {
3029
+ ...this.pose,
3030
+ altitude
3031
+ };
2989
3032
  }
2990
3033
  /** 获取内部实体(用于拾取识别) */
2991
3034
  getEntity() {
@@ -3045,8 +3088,58 @@ var AirplaneCursor = class {
3045
3088
  } catch {
3046
3089
  }
3047
3090
  this.updateFrustum();
3091
+ this.broadcastPoseChange();
3048
3092
  this.viewer.scene?.requestRender?.();
3049
3093
  }
3094
+ /**
3095
+ * 广播游标姿态变化事件到全局事件总线
3096
+ */
3097
+ broadcastPoseChange() {
3098
+ try {
3099
+ const C = this.CesiumNS;
3100
+ const cartographic = C.Cartographic.fromCartesian(this.pose.position);
3101
+ const altitude = cartographic ? cartographic.height : 0;
3102
+ console.log("[AirplaneCursor] \u{1F4E1} \u5E7F\u64AD\u59FF\u6001\u53D8\u5316:", {
3103
+ heading: this.pose.heading,
3104
+ pitch: this.pose.pitch,
3105
+ altitude
3106
+ });
3107
+ globalCameraEventBus.onCursorPoseChange.emit({
3108
+ position: this.pose.position,
3109
+ heading: this.pose.heading,
3110
+ pitch: this.pose.pitch,
3111
+ roll: this.pose.roll,
3112
+ altitude,
3113
+ source: "cursor"
3114
+ });
3115
+ } catch (e) {
3116
+ }
3117
+ }
3118
+ /**
3119
+ * 模拟按键按下(用于虚拟控制器)
3120
+ * @param key 按键名称 (w/a/s/d/q/e/c/z)
3121
+ * @param duration 按键持续时间(毫秒),默认 100ms
3122
+ */
3123
+ simulateKeyPress(key, duration = 100) {
3124
+ const lowerKey = key.toLowerCase();
3125
+ const keyMap = {
3126
+ "c": "c",
3127
+ // 上升
3128
+ "z": "z"
3129
+ // 下降
3130
+ };
3131
+ const mappedKey = keyMap[lowerKey] || lowerKey;
3132
+ console.log("[AirplaneCursor] \u{1F3AE} simulateKeyPress:", mappedKey, "duration:", duration);
3133
+ console.log("[AirplaneCursor] \u{1F3AE} updateLoopRunning:", this.updateLoopRunning);
3134
+ this.keysPressed.add(mappedKey);
3135
+ if (!this.updateLoopRunning) {
3136
+ console.log("[AirplaneCursor] \u{1F3AE} \u542F\u52A8 updateLoop");
3137
+ this.startUpdateLoop();
3138
+ }
3139
+ setTimeout(() => {
3140
+ this.keysPressed.delete(mappedKey);
3141
+ }, duration);
3142
+ }
3050
3143
  destroy() {
3051
3144
  if (this.destroyed) return;
3052
3145
  this.destroyed = true;
@@ -4876,6 +4969,10 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4876
4969
  } catch {
4877
4970
  }
4878
4971
  };
4972
+ const getTotalDistance = () => {
4973
+ if (positions.length < 2) return 0;
4974
+ return calculatePathDistance(CesiumNS, positions, positions.length - 1, hiddenClimbIndex);
4975
+ };
4879
4976
  const vertexInsertionHandler = new VertexInsertionHandler({
4880
4977
  CesiumNS,
4881
4978
  layer,
@@ -4886,6 +4983,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4886
4983
  if (!cursorStart) cursorStart = positions[hiddenClimbIndex === 1 ? 1 : 0];
4887
4984
  let airplaneCursor;
4888
4985
  const insertVertex = (insertAt, p3, poseOrient) => {
4986
+ console.log("[PathEditing] \u63D2\u5165\u9876\u70B9\u524D positions \u957F\u5EA6:", positions.length, "insertAt:", insertAt);
4889
4987
  const actualIndex = vertexInsertionHandler.insertVertex(
4890
4988
  insertAt,
4891
4989
  p3,
@@ -4901,6 +4999,8 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4901
4999
  airplaneCursor,
4902
5000
  poseOrient
4903
5001
  );
5002
+ console.log("[PathEditing] \u63D2\u5165\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length, "actualIndex:", actualIndex);
5003
+ entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
4904
5004
  setActiveIndex(actualIndex);
4905
5005
  createOrUpdateMarkerForIndex(actualIndex);
4906
5006
  };
@@ -4931,6 +5031,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4931
5031
  } catch {
4932
5032
  }
4933
5033
  }
5034
+ console.log("[PathEditing] \u5220\u9664\u9876\u70B9\u524D positions \u957F\u5EA6:", positions.length, "deleteAt:", deleteAt);
4934
5035
  positions.splice(deleteAt, 1);
4935
5036
  handles.splice(deleteAt, 1);
4936
5037
  headings.splice(deleteAt, 1);
@@ -4938,6 +5039,8 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4938
5039
  rolls.splice(deleteAt, 1);
4939
5040
  fovs.splice(deleteAt, 1);
4940
5041
  heightMarkers.splice(deleteAt, 1);
5042
+ console.log("[PathEditing] \u5220\u9664\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length);
5043
+ entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
4941
5044
  const newEditedIndices = /* @__PURE__ */ new Set();
4942
5045
  editedIndices.forEach((idx) => {
4943
5046
  if (idx < deleteAt) {
@@ -5060,7 +5163,9 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5060
5163
  totalVerticesBefore: totalBefore,
5061
5164
  totalVerticesAfter: totalAfter,
5062
5165
  newVertex,
5063
- timestamp: /* @__PURE__ */ new Date()
5166
+ timestamp: /* @__PURE__ */ new Date(),
5167
+ totalDistance: getTotalDistance()
5168
+ // 🆕 航线总里程
5064
5169
  };
5065
5170
  options.onVertexInsertDetail(operationInfo);
5066
5171
  } catch (error) {
@@ -5081,7 +5186,9 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5081
5186
  displayNumber,
5082
5187
  totalVerticesBefore: totalBefore,
5083
5188
  totalVerticesAfter: totalAfter,
5084
- timestamp: /* @__PURE__ */ new Date()
5189
+ timestamp: /* @__PURE__ */ new Date(),
5190
+ totalDistance: getTotalDistance()
5191
+ // 🆕 航线总里程
5085
5192
  };
5086
5193
  options.onVertexDeleteDetail(operationInfo);
5087
5194
  } catch (error) {
@@ -5109,6 +5216,31 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5109
5216
  vertexLabelManager.updateLabelPosition(index, newPosition);
5110
5217
  } catch {
5111
5218
  }
5219
+ if (options?.onVertexDragMoveDetail) {
5220
+ try {
5221
+ let coordinates = { longitude: 0, latitude: 0, height: 0 };
5222
+ try {
5223
+ const cartographic = C.Cartographic.fromCartesian(newPosition);
5224
+ coordinates = {
5225
+ longitude: C.Math.toDegrees(cartographic.longitude),
5226
+ latitude: C.Math.toDegrees(cartographic.latitude),
5227
+ height: cartographic.height
5228
+ };
5229
+ } catch {
5230
+ }
5231
+ const displayNumber = hiddenClimbIndex === 1 && index > 1 ? index - 1 : index;
5232
+ const dragMoveInfo = {
5233
+ index,
5234
+ displayNumber,
5235
+ position: newPosition,
5236
+ coordinates,
5237
+ timestamp: /* @__PURE__ */ new Date()
5238
+ };
5239
+ options.onVertexDragMoveDetail(dragMoveInfo);
5240
+ } catch (error) {
5241
+ console.error("Error in onVertexDragMoveDetail:", error);
5242
+ }
5243
+ }
5112
5244
  },
5113
5245
  onVertexDragEnd: (index, finalPosition) => {
5114
5246
  editedIndices.add(index);
@@ -5136,7 +5268,9 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5136
5268
  totalVerticesAfter: positions.length,
5137
5269
  newVertex: vertexInfo,
5138
5270
  newPosition: finalPosition,
5139
- timestamp: /* @__PURE__ */ new Date()
5271
+ timestamp: /* @__PURE__ */ new Date(),
5272
+ totalDistance: getTotalDistance()
5273
+ // 🆕 航线总里程
5140
5274
  };
5141
5275
  options.onVertexDragCompleteDetail(operationInfo);
5142
5276
  } catch (error) {
@@ -5149,6 +5283,12 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5149
5283
  getAirplaneCursor: () => airplaneCursor,
5150
5284
  getPoseData: () => ({ headings, pitches, rolls, fovs }),
5151
5285
  getEntity: () => entity,
5286
+ /** 模拟按键控制飞机游标(用于虚拟控制器) */
5287
+ simulateKeyPress: (key, duration) => {
5288
+ airplaneCursor?.simulateKeyPress(key, duration);
5289
+ },
5290
+ /** 获取游标当前姿态(包含高度) */
5291
+ getCursorPose: () => airplaneCursor?.getPose(),
5152
5292
  // 🆕 快速编辑回调
5153
5293
  getQuickEditEnabled: () => quickEditEnabled,
5154
5294
  getQuickEditOptions: () => ({
@@ -5159,6 +5299,18 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5159
5299
  );
5160
5300
  const cleanupSession = () => {
5161
5301
  setActiveIndex(void 0);
5302
+ try {
5303
+ handles.forEach((handle) => {
5304
+ if (handle) {
5305
+ try {
5306
+ layer.entities.remove(handle);
5307
+ } catch {
5308
+ }
5309
+ }
5310
+ });
5311
+ handles.length = 0;
5312
+ } catch {
5313
+ }
5162
5314
  try {
5163
5315
  heightMarkers.forEach((m) => m?.destroy());
5164
5316
  } catch {
@@ -5331,9 +5483,214 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5331
5483
  /** 🆕 检查快速编辑模式是否启用 */
5332
5484
  isQuickEditEnabled: () => {
5333
5485
  return quickEditEnabled;
5486
+ },
5487
+ // ========================
5488
+ // 🆕 程序化更新方法
5489
+ // ========================
5490
+ /**
5491
+ * 🆕 程序化更新指定航点
5492
+ * @param index 航点索引
5493
+ * @param updates 需要更新的字段
5494
+ * @returns 是否更新成功
5495
+ */
5496
+ updateWaypoint: (index, updates) => {
5497
+ if (index < 0 || index >= positions.length) {
5498
+ console.warn(`[updateWaypoint] Invalid index: ${index}, valid range: 0-${positions.length - 1}`);
5499
+ return false;
5500
+ }
5501
+ try {
5502
+ let positionUpdated = false;
5503
+ if (updates.position) {
5504
+ positions[index] = updates.position;
5505
+ positionUpdated = true;
5506
+ }
5507
+ if (updates.altitude !== void 0 && !updates.position) {
5508
+ const currentPos = positions[index];
5509
+ const cartographic = C.Cartographic.fromCartesian(currentPos);
5510
+ const newPos = C.Cartesian3.fromRadians(
5511
+ cartographic.longitude,
5512
+ cartographic.latitude,
5513
+ updates.altitude
5514
+ );
5515
+ positions[index] = newPos;
5516
+ positionUpdated = true;
5517
+ }
5518
+ if (positionUpdated) {
5519
+ const newPos = positions[index];
5520
+ if (handles[index]) {
5521
+ try {
5522
+ handles[index].position = newPos;
5523
+ } catch {
5524
+ }
5525
+ }
5526
+ try {
5527
+ vertexLabelManager.updateLabelPosition(index, newPos);
5528
+ } catch {
5529
+ }
5530
+ try {
5531
+ createOrUpdateMarkerForIndex(index);
5532
+ } catch {
5533
+ }
5534
+ }
5535
+ if (updates.heading !== void 0) headings[index] = updates.heading;
5536
+ if (updates.pitch !== void 0) pitches[index] = updates.pitch;
5537
+ if (updates.roll !== void 0) rolls[index] = updates.roll;
5538
+ if (updates.fov !== void 0) {
5539
+ fovs[index] = updates.fov;
5540
+ if (index === activeIndex) {
5541
+ globalCameraEventBus.onFOVChange.emit({
5542
+ fov: updates.fov,
5543
+ focalLength: fovToFocalLength(updates.fov),
5544
+ source: "user"
5545
+ });
5546
+ }
5547
+ }
5548
+ if (index === activeIndex && airplaneCursor) {
5549
+ try {
5550
+ airplaneCursor.setPose(
5551
+ positions[index],
5552
+ headings[index] ?? 0,
5553
+ pitches[index] ?? -10,
5554
+ rolls[index] ?? 0
5555
+ );
5556
+ } catch {
5557
+ }
5558
+ }
5559
+ stateManager.persistToEntity(positions, headings, pitches, rolls, fovs);
5560
+ return true;
5561
+ } catch (error) {
5562
+ console.error("[updateWaypoint] Error:", error);
5563
+ return false;
5564
+ }
5565
+ },
5566
+ /**
5567
+ * 🆕 更新航点位置
5568
+ */
5569
+ updateWaypointPosition: function(index, position) {
5570
+ return this.updateWaypoint(index, { position });
5571
+ },
5572
+ /**
5573
+ * 🆕 更新航点高度(仅垂直方向,保持经纬度不变)
5574
+ */
5575
+ updateWaypointAltitude: function(index, altitude) {
5576
+ return this.updateWaypoint(index, { altitude });
5577
+ },
5578
+ /**
5579
+ * 🆕 更新航点偏航角
5580
+ */
5581
+ updateWaypointHeading: function(index, heading) {
5582
+ return this.updateWaypoint(index, { heading });
5583
+ },
5584
+ /**
5585
+ * 🆕 更新航点俯仰角
5586
+ */
5587
+ updateWaypointPitch: function(index, pitch) {
5588
+ return this.updateWaypoint(index, { pitch });
5589
+ },
5590
+ /**
5591
+ * 🆕 更新航点视野角
5592
+ */
5593
+ updateWaypointFov: function(index, fov) {
5594
+ return this.updateWaypoint(index, { fov });
5595
+ },
5596
+ /**
5597
+ * 🆕 批量更新多个航点
5598
+ */
5599
+ batchUpdateWaypoints: function(updates) {
5600
+ let allSuccess = true;
5601
+ for (const { index, data } of updates) {
5602
+ if (!this.updateWaypoint(index, data)) {
5603
+ allSuccess = false;
5604
+ }
5605
+ }
5606
+ return allSuccess;
5607
+ },
5608
+ /**
5609
+ * 🆕 程序化选中指定航点
5610
+ * 将飞机游标移动到指定航点位置,更新选中状态
5611
+ * @param index 航点索引
5612
+ * @returns 是否选中成功
5613
+ */
5614
+ selectVertex: (index) => {
5615
+ if (index < 0 || index >= positions.length) {
5616
+ console.warn(`[selectVertex] Invalid index: ${index}, valid range: 0-${positions.length - 1}`);
5617
+ return false;
5618
+ }
5619
+ try {
5620
+ setActiveIndex(index);
5621
+ return true;
5622
+ } catch (e) {
5623
+ console.error("[selectVertex] Failed to select vertex:", e);
5624
+ return false;
5625
+ }
5626
+ },
5627
+ /**
5628
+ * 🆕 模拟按键控制飞机游标(用于虚拟控制器)
5629
+ * @param key 按键名称 (w/a/s/d/q/e/c/z)
5630
+ * @param duration 按键持续时间(毫秒)
5631
+ */
5632
+ simulateKeyPress: (key, duration) => {
5633
+ airplaneCursor?.simulateKeyPress(key, duration);
5634
+ },
5635
+ /**
5636
+ * 🆕 获取游标当前姿态(包含高度)
5637
+ */
5638
+ getCursorPose: () => airplaneCursor?.getPose(),
5639
+ /**
5640
+ * 🆕 动态更新爬升高度(climbHeight)
5641
+ * 更新隐藏爬升点(index 1)的高度,实现安全飞行高度的动态调节
5642
+ * @param climbHeight 新的爬升高度(米)
5643
+ * @returns 是否更新成功
5644
+ */
5645
+ updateClimbHeight: function(climbHeight) {
5646
+ if (hiddenClimbIndex !== 1 || positions.length < 2) {
5647
+ console.warn("[updateClimbHeight] No hidden climb point exists");
5648
+ return false;
5649
+ }
5650
+ try {
5651
+ const startPos = positions[0];
5652
+ const startCarto = C.Cartographic.fromCartesian(startPos);
5653
+ const newAltitude = startCarto.height + climbHeight;
5654
+ const success = this.updateWaypointAltitude(1, newAltitude);
5655
+ if (success) {
5656
+ try {
5657
+ entity.properties._climbHeight = climbHeight;
5658
+ } catch {
5659
+ }
5660
+ console.log("[updateClimbHeight] \u2705 \u722C\u5347\u9AD8\u5EA6\u5DF2\u66F4\u65B0:", climbHeight, "\u7C73");
5661
+ }
5662
+ return success;
5663
+ } catch (error) {
5664
+ console.error("[updateClimbHeight] Error:", error);
5665
+ return false;
5666
+ }
5667
+ },
5668
+ /**
5669
+ * 🆕 获取起飞点信息
5670
+ * @returns 起飞点的经纬度和高度
5671
+ */
5672
+ getStartPoint: () => {
5673
+ if (positions.length < 1) return null;
5674
+ try {
5675
+ const startPos = positions[0];
5676
+ const carto = C.Cartographic.fromCartesian(startPos);
5677
+ return {
5678
+ position: startPos,
5679
+ latitude: C.Math.toDegrees(carto.latitude),
5680
+ longitude: C.Math.toDegrees(carto.longitude),
5681
+ altitude: carto.height
5682
+ };
5683
+ } catch {
5684
+ return null;
5685
+ }
5334
5686
  }
5335
5687
  };
5336
5688
  }
5689
+ function fovToFocalLength(fovDeg) {
5690
+ const sensorWidth = 36;
5691
+ const fovRad = fovDeg * Math.PI / 180;
5692
+ return sensorWidth / (2 * Math.tan(fovRad / 2));
5693
+ }
5337
5694
 
5338
5695
  // src/core/path-manager/ArrowShape.ts
5339
5696
  var ArrowShape = class {
@@ -5502,6 +5859,7 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
5502
5859
  const DEFAULT_MAIN_WIDTH = options?.width ?? 6;
5503
5860
  let startCartographic;
5504
5861
  let hasStart = false;
5862
+ let createdEntity = void 0;
5505
5863
  const getPositionFromMouse = (movement) => {
5506
5864
  const scene = viewer.scene;
5507
5865
  const winPos = movement?.position ?? movement?.endPosition ?? movement;
@@ -5602,6 +5960,7 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
5602
5960
  _hasHiddenClimb: climbHeight > 0
5603
5961
  }
5604
5962
  });
5963
+ createdEntity = created;
5605
5964
  try {
5606
5965
  tempHandles.forEach((hh, idx) => {
5607
5966
  try {
@@ -5638,8 +5997,21 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
5638
5997
  } else {
5639
5998
  editOptions.preview = { enabled: true };
5640
5999
  }
5641
- if (options?.onVertexSelectDetail) {
5642
- editOptions.onVertexSelectDetail = options.onVertexSelectDetail;
6000
+ const autoOpts = typeof auto === "object" ? auto : {};
6001
+ if (autoOpts.onVertexSelectDetail) {
6002
+ editOptions.onVertexSelectDetail = autoOpts.onVertexSelectDetail;
6003
+ }
6004
+ if (autoOpts.onVertexDragMoveDetail) {
6005
+ editOptions.onVertexDragMoveDetail = autoOpts.onVertexDragMoveDetail;
6006
+ }
6007
+ if (autoOpts.onVertexDragCompleteDetail) {
6008
+ editOptions.onVertexDragCompleteDetail = autoOpts.onVertexDragCompleteDetail;
6009
+ }
6010
+ if (autoOpts.onVertexInsertDetail) {
6011
+ editOptions.onVertexInsertDetail = autoOpts.onVertexInsertDetail;
6012
+ }
6013
+ if (autoOpts.onVertexDeleteDetail) {
6014
+ editOptions.onVertexDeleteDetail = autoOpts.onVertexDeleteDetail;
5643
6015
  }
5644
6016
  const editSession = startPathEditing(CesiumNS, viewer, created, editOptions);
5645
6017
  if (editSession && options?.onEditingStarted) {
@@ -5665,6 +6037,12 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
5665
6037
  handler?.destroy();
5666
6038
  } catch {
5667
6039
  }
6040
+ if (createdEntity) {
6041
+ try {
6042
+ layer.entities.remove(createdEntity);
6043
+ } catch {
6044
+ }
6045
+ }
5668
6046
  }
5669
6047
  };
5670
6048
  }
@@ -5730,7 +6108,6 @@ function parsePoseFromAction(action) {
5730
6108
  fov: parseFloat(numbers[3])
5731
6109
  };
5732
6110
  }
5733
- console.warn("[sinoflyAdapter] \u65E0\u6CD5\u89E3\u6790 action \u5B57\u6BB5:", action, error);
5734
6111
  return {};
5735
6112
  }
5736
6113
  }
@@ -5748,15 +6125,40 @@ function parseTakeOffRefPoint(takeOffRefPoint, takeOffSecurityHeight, CesiumNS)
5748
6125
  if (!takeOffRefPoint || typeof takeOffRefPoint !== "string" || takeOffRefPoint.trim() === "") {
5749
6126
  return null;
5750
6127
  }
6128
+ const C = CesiumNS;
6129
+ let longitude;
6130
+ let latitude;
6131
+ let height;
6132
+ const trimmed = takeOffRefPoint.trim();
6133
+ if (trimmed.includes(",")) {
6134
+ try {
6135
+ const parts = trimmed.split(",").map((p) => p.trim());
6136
+ if (parts.length >= 2) {
6137
+ latitude = parseFloat(parts[0]);
6138
+ longitude = parseFloat(parts[1]);
6139
+ height = parts.length >= 3 ? parseFloat(parts[2]) : takeOffSecurityHeight;
6140
+ if (isNaN(latitude) || isNaN(longitude) || isNaN(height)) {
6141
+ throw new Error("\u65E0\u6CD5\u89E3\u6790\u9017\u53F7\u5206\u9694\u7684\u5750\u6807\u503C");
6142
+ }
6143
+ const startPosition = C.Cartesian3.fromDegrees(longitude, latitude, height);
6144
+ const climbPosition = C.Cartesian3.fromDegrees(
6145
+ longitude,
6146
+ latitude,
6147
+ height + takeOffSecurityHeight
6148
+ );
6149
+ return {
6150
+ startPosition,
6151
+ climbPosition
6152
+ };
6153
+ }
6154
+ } catch (error) {
6155
+ }
6156
+ }
5751
6157
  try {
5752
6158
  const parsed = JSON.parse(takeOffRefPoint);
5753
6159
  if (typeof parsed !== "object" || parsed === null) {
5754
6160
  return null;
5755
6161
  }
5756
- const C = CesiumNS;
5757
- let longitude;
5758
- let latitude;
5759
- let height;
5760
6162
  if (typeof parsed.longitude === "number") {
5761
6163
  longitude = parsed.longitude;
5762
6164
  } else if (typeof parsed.lon === "number") {
@@ -5787,7 +6189,6 @@ function parseTakeOffRefPoint(takeOffRefPoint, takeOffSecurityHeight, CesiumNS)
5787
6189
  climbPosition
5788
6190
  };
5789
6191
  } catch (error) {
5790
- console.warn("[sinoflyAdapter] \u65E0\u6CD5\u89E3\u6790 takeOffRefPoint:", takeOffRefPoint, error);
5791
6192
  return null;
5792
6193
  }
5793
6194
  }
@@ -5809,16 +6210,10 @@ function convertSinoflyWayline(data, options) {
5809
6210
  if (!data.waypointInfo || !Array.isArray(data.waypointInfo)) {
5810
6211
  throw new Error("[sinoflyAdapter] waypointInfo \u5FC5\u987B\u662F\u6570\u7EC4");
5811
6212
  }
5812
- if (data.waypointInfo.length === 0) {
5813
- console.warn("[sinoflyAdapter] waypointInfo \u6570\u7EC4\u4E3A\u7A7A\uFF0C\u5C06\u8FD4\u56DE\u7A7A\u7684\u822A\u70B9\u6570\u636E");
5814
- }
6213
+ if (data.waypointInfo.length === 0) ;
5815
6214
  if (data.waylineId === void 0 || data.waylineId === null) {
5816
6215
  throw new Error("[sinoflyAdapter] waylineId \u4E0D\u80FD\u4E3A\u7A7A");
5817
6216
  }
5818
- console.log(`[sinoflyAdapter] \u5F00\u59CB\u8F6C\u6362\u822A\u7EBF ${data.waylineId}\uFF0C\u5305\u542B ${data.waypointInfo.length} \u4E2A\u822A\u70B9`);
5819
- if (data.takeOffRefPoint) {
5820
- console.log(`[sinoflyAdapter] \u68C0\u6D4B\u5230 takeOffRefPoint: ${data.takeOffRefPoint}`);
5821
- }
5822
6217
  const takeOffInfo = parseTakeOffRefPoint(
5823
6218
  data.takeOffRefPoint,
5824
6219
  data.takeOffSecurityHeight,
@@ -5883,7 +6278,6 @@ function convertSinoflyWayline(data, options) {
5883
6278
  if (poseFromAction.roll !== void 0) roll = poseFromAction.roll;
5884
6279
  if (poseFromAction.fov !== void 0) fov = poseFromAction.fov;
5885
6280
  } catch (actionError) {
5886
- console.warn(`[sinoflyAdapter] \u822A\u70B9 ${idx} action \u89E3\u6790\u5931\u8D25:`, actionError);
5887
6281
  }
5888
6282
  }
5889
6283
  return {
@@ -5896,7 +6290,6 @@ function convertSinoflyWayline(data, options) {
5896
6290
  // 重新分配 index,从 waypointStartIndex 开始
5897
6291
  };
5898
6292
  } catch (error) {
5899
- console.error(`[sinoflyAdapter] \u822A\u70B9 ${idx} \u8F6C\u6362\u5931\u8D25:`, error);
5900
6293
  throw new Error(`\u822A\u70B9 ${idx} \u8F6C\u6362\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
5901
6294
  }
5902
6295
  });
@@ -5904,7 +6297,6 @@ function convertSinoflyWayline(data, options) {
5904
6297
  if (!Array.isArray(waypointData)) {
5905
6298
  throw new Error("[sinoflyAdapter] waypointData \u4E0D\u662F\u6570\u7EC4");
5906
6299
  }
5907
- console.log(`[sinoflyAdapter] \u8F6C\u6362\u5B8C\u6210\uFF0C\u5171\u751F\u6210 ${waypointData.length} \u4E2A\u822A\u70B9`);
5908
6300
  const metadata = {
5909
6301
  waylineId: data.waylineId,
5910
6302
  waylineType: data.waylineType,