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