@jorgmoritz/gis-manager 0.1.43 → 0.1.45

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.42"};
16
+ version: "0.1.44"};
17
17
 
18
18
  // src/utils/version.ts
19
19
  var version = package_default.version;
@@ -2004,14 +2004,15 @@ var HeightMarker = class {
2004
2004
  polyline: {
2005
2005
  positions: [C.Cartesian3.ZERO, C.Cartesian3.ZERO],
2006
2006
  width: this.opts.width ?? 2,
2007
- material: color,
2007
+ material: C.Color.YELLOW.withAlpha(0.4),
2008
+ // 黄色半透明垂直线
2008
2009
  clampToGround: false
2009
2010
  // vertical line in 3D space
2010
2011
  },
2011
2012
  ellipse: {
2012
- semiMajorAxis: this.opts.circleRadius ?? 5,
2013
- semiMinorAxis: this.opts.circleRadius ?? 5,
2014
- material: color.withAlpha ? color.withAlpha(0.65) : color,
2013
+ semiMajorAxis: this.opts.circleRadius ?? 3,
2014
+ semiMinorAxis: this.opts.circleRadius ?? 3,
2015
+ material: color.withAlpha ? color.withAlpha(0.45) : color,
2015
2016
  outline: true,
2016
2017
  outlineColor: color,
2017
2018
  heightReference: C.HeightReference.CLAMP_TO_GROUND
@@ -2022,12 +2023,13 @@ var HeightMarker = class {
2022
2023
  position: C.Cartesian3.ZERO,
2023
2024
  label: {
2024
2025
  text: "",
2025
- font: "14px sans-serif",
2026
+ font: "10px sans-serif",
2027
+ // 字体改小
2026
2028
  style: C.LabelStyle.FILL_AND_OUTLINE,
2027
2029
  fillColor: C.Color.BLACK,
2028
2030
  outlineColor: C.Color.WHITE,
2029
- outlineWidth: 3,
2030
- pixelOffset: new C.Cartesian2(0, -6),
2031
+ outlineWidth: 2,
2032
+ pixelOffset: new C.Cartesian2(0, -4),
2031
2033
  showBackground: false,
2032
2034
  horizontalOrigin: C.HorizontalOrigin.CENTER,
2033
2035
  verticalOrigin: C.VerticalOrigin.BOTTOM,
@@ -3553,6 +3555,10 @@ var AirplaneCursor = class {
3553
3555
  __publicField(this, "cachedScaleFactor", 1);
3554
3556
  // 相机变化事件监听器
3555
3557
  __publicField(this, "cameraChangedListener");
3558
+ /** 飞机游标模型可见性,默认 false */
3559
+ __publicField(this, "visible", false);
3560
+ /** 是否显示视锥体,默认 true */
3561
+ __publicField(this, "showFrustum", true);
3556
3562
  const C = this.CesiumNS;
3557
3563
  this.opts = opts;
3558
3564
  this.pose = { position: startPosition, heading: 0, pitch: -10, roll: 0 };
@@ -3560,6 +3566,8 @@ var AirplaneCursor = class {
3560
3566
  this.angleStep = opts.angleStepDeg ?? 1;
3561
3567
  this.fastFactor = opts.fastFactor ?? 5;
3562
3568
  this.currentFOV = opts.fovDeg ?? DEFAULT_FOV;
3569
+ this.visible = opts.visible ?? false;
3570
+ this.showFrustum = opts.showFrustum ?? true;
3563
3571
  this.ensureEntity(opts.color ?? C.Color.CYAN.withAlpha(0.9));
3564
3572
  this.attachKeyboard(opts);
3565
3573
  this.setupFOVListener();
@@ -3614,6 +3622,8 @@ var AirplaneCursor = class {
3614
3622
  runAnimations: true,
3615
3623
  clampAnimations: false
3616
3624
  },
3625
+ show: this.visible,
3626
+ // 根据 visible 设置初始显示状态
3617
3627
  properties: { _type: "airplane-cursor" }
3618
3628
  });
3619
3629
  this.entity = this.modelEntity;
@@ -3791,6 +3801,7 @@ var AirplaneCursor = class {
3791
3801
  * - 后续只依赖回调读取 pose 与 currentFOV 即可自动更新
3792
3802
  */
3793
3803
  updateFrustum() {
3804
+ if (!this.showFrustum) return;
3794
3805
  try {
3795
3806
  if (!this.frustum) {
3796
3807
  let layer = this.viewer.dataSources?._dataSources?.[0];
@@ -3841,6 +3852,18 @@ var AirplaneCursor = class {
3841
3852
  altitude
3842
3853
  };
3843
3854
  }
3855
+ /** 设置飞机游标可见性 */
3856
+ setVisible(visible) {
3857
+ this.visible = visible;
3858
+ if (this.modelEntity) {
3859
+ this.modelEntity.show = visible;
3860
+ }
3861
+ this.viewer.scene?.requestRender?.();
3862
+ }
3863
+ /** 获取当前可见性状态 */
3864
+ isVisible() {
3865
+ return this.visible;
3866
+ }
3844
3867
  /** 获取内部实体(用于拾取识别) */
3845
3868
  getEntity() {
3846
3869
  return this.entity;
@@ -5070,6 +5093,44 @@ function calculateRelativeHeight(altitudeMode, absoluteHeight, startPointAltitud
5070
5093
  }
5071
5094
  }
5072
5095
 
5096
+ // src/utils/geometryUtils.ts
5097
+ function calculateDistanceAndMidpoint(CesiumNS, point1, point2) {
5098
+ const C = CesiumNS;
5099
+ const distance = C.Cartesian3.distance(point1, point2);
5100
+ const midpoint = new C.Cartesian3();
5101
+ C.Cartesian3.midpoint(point1, point2, midpoint);
5102
+ return { distance, midpoint };
5103
+ }
5104
+ function calculateDistance(CesiumNS, point1, point2) {
5105
+ const C = CesiumNS;
5106
+ return C.Cartesian3.distance(point1, point2);
5107
+ }
5108
+ function calculateMidpoint(CesiumNS, point1, point2) {
5109
+ const C = CesiumNS;
5110
+ const midpoint = new C.Cartesian3();
5111
+ C.Cartesian3.midpoint(point1, point2, midpoint);
5112
+ return midpoint;
5113
+ }
5114
+ function calculateHeadingBetweenPoints(CesiumNS, point1, point2) {
5115
+ const C = CesiumNS;
5116
+ try {
5117
+ const fromCarto = C.Cartographic.fromCartesian(point1);
5118
+ const toCarto = C.Cartographic.fromCartesian(point2);
5119
+ const deltaLon = toCarto.longitude - fromCarto.longitude;
5120
+ let heading = Math.atan2(
5121
+ Math.sin(deltaLon) * Math.cos(toCarto.latitude),
5122
+ Math.cos(fromCarto.latitude) * Math.sin(toCarto.latitude) - Math.sin(fromCarto.latitude) * Math.cos(toCarto.latitude) * Math.cos(deltaLon)
5123
+ );
5124
+ heading = C.Math.toDegrees(heading);
5125
+ if (heading > 180) heading -= 360;
5126
+ if (heading < -180) heading += 360;
5127
+ return heading;
5128
+ } catch (error) {
5129
+ console.error("[calculateHeadingBetweenPoints] Error:", error);
5130
+ return 0;
5131
+ }
5132
+ }
5133
+
5073
5134
  // src/core/path-manager/editing/PathEditingEventHandler.ts
5074
5135
  var PathEditingEventHandler = class {
5075
5136
  constructor(options, callbacks) {
@@ -5130,36 +5191,14 @@ var PathEditingEventHandler = class {
5130
5191
  if (typeof index === "number") {
5131
5192
  if (currentActiveIndex === index) {
5132
5193
  this.vertexDragHandler.startDrag(index, movement.position);
5133
- } else {
5134
- if (this.callbacks.onVertexSelect) {
5135
- this.callbacks.onVertexSelect(index);
5136
- }
5137
- console.log("[PathEditingEventHandler] \u9876\u70B9\u88AB\u70B9\u51FB\uFF0C\u7D22\u5F15:", index, "\u56DE\u8C03\u5B58\u5728:", !!this.callbacks.onVertexSelectDetail);
5138
- if (this.callbacks.onVertexSelectDetail) {
5139
- try {
5140
- const detailInfo = this.buildVertexDetailInfo(index);
5141
- console.log("[PathEditingEventHandler] \u8C03\u7528 onVertexSelectDetail \u56DE\u8C03");
5142
- this.callbacks.onVertexSelectDetail(detailInfo);
5143
- } catch (error) {
5144
- console.error("[PathEditingEventHandler] Error building vertex detail info:", error);
5145
- }
5146
- }
5147
5194
  }
5148
5195
  return;
5149
5196
  }
5150
5197
  }
5151
5198
  const idx = pickVertexIndex(this.viewer, movement.position, this.hiddenClimbIndex);
5152
5199
  if (typeof idx === "number") {
5153
- if (this.callbacks.onVertexSelect) {
5154
- this.callbacks.onVertexSelect(idx);
5155
- }
5156
- if (this.callbacks.onVertexSelectDetail) {
5157
- try {
5158
- const detailInfo = this.buildVertexDetailInfo(idx);
5159
- this.callbacks.onVertexSelectDetail(detailInfo);
5160
- } catch (error) {
5161
- console.error("Error building vertex detail info:", error);
5162
- }
5200
+ if (currentActiveIndex === idx) {
5201
+ this.vertexDragHandler.startDrag(idx, movement.position);
5163
5202
  }
5164
5203
  }
5165
5204
  }, C.ScreenSpaceEventType.LEFT_DOWN);
@@ -5178,6 +5217,26 @@ var PathEditingEventHandler = class {
5178
5217
  if (this.vertexDragHandler.isDragging()) {
5179
5218
  return;
5180
5219
  }
5220
+ const scene = this.viewer.scene;
5221
+ const picked = scene.pick?.(movement.position);
5222
+ const entity = picked?.id;
5223
+ if (entity?.properties?._type?.getValue?.() === "vertex-label") {
5224
+ const labelIndex = entity.properties._vertexIndex?.getValue?.();
5225
+ if (typeof labelIndex === "number") {
5226
+ if (this.callbacks.onVertexSelect) {
5227
+ this.callbacks.onVertexSelect(labelIndex);
5228
+ }
5229
+ if (this.callbacks.onVertexSelectDetail) {
5230
+ try {
5231
+ const detailInfo = this.buildVertexDetailInfo(labelIndex);
5232
+ this.callbacks.onVertexSelectDetail(detailInfo);
5233
+ } catch (error) {
5234
+ console.error("Error building vertex detail info:", error);
5235
+ }
5236
+ }
5237
+ return;
5238
+ }
5239
+ }
5181
5240
  const idx = pickVertexIndex(this.viewer, movement.position, this.hiddenClimbIndex);
5182
5241
  if (typeof idx === "number") {
5183
5242
  if (this.callbacks.onVertexSelect) {
@@ -5193,6 +5252,45 @@ var PathEditingEventHandler = class {
5193
5252
  }
5194
5253
  return;
5195
5254
  }
5255
+ const CLICK_THRESHOLD_PIXELS = 25;
5256
+ const positions = this.callbacks.getPositions?.() || [];
5257
+ if (positions.length > 0) {
5258
+ const clickPos = movement.position;
5259
+ let minDistance = Infinity;
5260
+ let nearestIndex = -1;
5261
+ for (let i = 0; i < positions.length; i++) {
5262
+ try {
5263
+ const screenPos = C.SceneTransforms.wgs84ToWindowCoordinates(
5264
+ scene,
5265
+ positions[i]
5266
+ );
5267
+ if (screenPos) {
5268
+ const dx = screenPos.x - clickPos.x;
5269
+ const dy = screenPos.y - clickPos.y;
5270
+ const distance = Math.sqrt(dx * dx + dy * dy);
5271
+ if (distance < minDistance) {
5272
+ minDistance = distance;
5273
+ nearestIndex = i;
5274
+ }
5275
+ }
5276
+ } catch {
5277
+ }
5278
+ }
5279
+ if (minDistance < CLICK_THRESHOLD_PIXELS && nearestIndex >= 0) {
5280
+ if (this.callbacks.onVertexSelect) {
5281
+ this.callbacks.onVertexSelect(nearestIndex);
5282
+ }
5283
+ if (this.callbacks.onVertexSelectDetail) {
5284
+ try {
5285
+ const detailInfo = this.buildVertexDetailInfo(nearestIndex);
5286
+ this.callbacks.onVertexSelectDetail(detailInfo);
5287
+ } catch (error) {
5288
+ console.error("Error building vertex detail info:", error);
5289
+ }
5290
+ }
5291
+ return;
5292
+ }
5293
+ }
5196
5294
  const quickEditEnabled = this.callbacks.getQuickEditEnabled?.();
5197
5295
  if (quickEditEnabled) {
5198
5296
  this.handleQuickEditClick(movement);
@@ -5296,6 +5394,14 @@ var PathEditingEventHandler = class {
5296
5394
  this.handleInsertVertex(activeIndex + 1, pose, "after");
5297
5395
  }
5298
5396
  });
5397
+ if (activeIndex < positions.length - 1) {
5398
+ menuItems.push({
5399
+ label: "\u5728\u4E2D\u95F4\u63D2\u5165\u822A\u70B9",
5400
+ action: () => {
5401
+ this.handleInsertMidpoint(activeIndex);
5402
+ }
5403
+ });
5404
+ }
5299
5405
  } else if (pose && visibleVertexCount <= 1) {
5300
5406
  menuItems.push({
5301
5407
  label: "\u6DFB\u52A0\u7B2C\u4E00\u4E2A\u822A\u70B9",
@@ -5442,6 +5548,27 @@ var PathEditingEventHandler = class {
5442
5548
  });
5443
5549
  }
5444
5550
  }
5551
+ /**
5552
+ * 🆕 在选中航点和下一个航点中间插入新航点
5553
+ */
5554
+ handleInsertMidpoint(activeIndex) {
5555
+ const positions = this.callbacks.getPositions?.() || [];
5556
+ const poseData = this.callbacks.getPoseData?.();
5557
+ if (activeIndex >= positions.length - 1) {
5558
+ console.warn("[handleInsertMidpoint] \u9009\u4E2D\u822A\u70B9\u662F\u6700\u540E\u4E00\u4E2A\uFF0C\u65E0\u6CD5\u63D2\u5165\u4E2D\u70B9");
5559
+ return;
5560
+ }
5561
+ const point1 = positions[activeIndex];
5562
+ const point2 = positions[activeIndex + 1];
5563
+ const { midpoint } = calculateDistanceAndMidpoint(this.CesiumNS, point1, point2);
5564
+ const heading = calculateHeadingBetweenPoints(this.CesiumNS, point1, point2);
5565
+ const pitch = poseData?.pitches[activeIndex] ?? -10;
5566
+ const roll = poseData?.rolls[activeIndex] ?? 0;
5567
+ console.log("[handleInsertMidpoint] \u5728\u7D22\u5F15", activeIndex, "\u548C", activeIndex + 1, "\u4E4B\u95F4\u63D2\u5165\u4E2D\u70B9");
5568
+ if (this.callbacks.onInsertVertex) {
5569
+ this.callbacks.onInsertVertex(activeIndex + 1, midpoint, { heading, pitch, roll });
5570
+ }
5571
+ }
5445
5572
  /**
5446
5573
  * 处理删除顶点
5447
5574
  */
@@ -5890,23 +6017,37 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5890
6017
  while (useGlobalHeightFlags.length < positions.length) {
5891
6018
  useGlobalHeightFlags.push(false);
5892
6019
  }
5893
- let quickEditEnabled = false;
6020
+ const waypointIds = [];
6021
+ if (options?.waypointIds && Array.isArray(options.waypointIds)) {
6022
+ const offset = 0;
6023
+ for (let i = 0; i < offset; i++) {
6024
+ waypointIds[i] = void 0;
6025
+ }
6026
+ options.waypointIds.forEach((id, idx) => {
6027
+ waypointIds[offset + idx] = id;
6028
+ });
6029
+ }
6030
+ while (waypointIds.length < positions.length) {
6031
+ waypointIds.push(void 0);
6032
+ }
6033
+ let quickEditEnabled = true;
5894
6034
  let quickEditOptions = {
5895
- enabled: false,
6035
+ enabled: true,
5896
6036
  climbHeight: currentDefaultAltitude,
5897
6037
  // 使用当前默认高度
5898
6038
  altitudeMode: currentAltitudeMode
5899
6039
  // 使用当前高度模式
5900
6040
  };
5901
- if (options?.quickEdit) {
6041
+ if (options?.quickEdit !== void 0) {
5902
6042
  if (typeof options.quickEdit === "boolean") {
5903
6043
  quickEditEnabled = options.quickEdit;
5904
6044
  quickEditOptions.enabled = options.quickEdit;
5905
6045
  } else {
5906
- quickEditEnabled = options.quickEdit.enabled ?? false;
6046
+ quickEditEnabled = options.quickEdit.enabled ?? true;
5907
6047
  quickEditOptions = {
5908
6048
  ...quickEditOptions,
5909
- ...options.quickEdit
6049
+ ...options.quickEdit,
6050
+ enabled: options.quickEdit.enabled ?? true
5910
6051
  };
5911
6052
  }
5912
6053
  }
@@ -6001,6 +6142,8 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
6001
6142
  const stateManager = new PathStateManager(entity, positions, headings, pitches, rolls, fovs);
6002
6143
  let activeIndex;
6003
6144
  let editedIndices = /* @__PURE__ */ new Set();
6145
+ let isUpdatingFromCursor = false;
6146
+ let isUpdatingFromDrag = false;
6004
6147
  const handleStyleManager = new VertexHandleStyleManager(CesiumNS);
6005
6148
  const setActiveIndex = (idx) => {
6006
6149
  if (activeIndex !== void 0 && handles[activeIndex]) {
@@ -6106,6 +6249,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
6106
6249
  useGlobalHeightFlags
6107
6250
  // 🆕 传递 useGlobalHeightFlags
6108
6251
  );
6252
+ waypointIds.splice(actualIndex, 0, void 0);
6109
6253
  console.log("[PathEditing] \u63D2\u5165\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length, "actualIndex:", actualIndex);
6110
6254
  entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
6111
6255
  setActiveIndex(actualIndex);
@@ -6138,6 +6282,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
6138
6282
  fovs.splice(deleteAt, 1);
6139
6283
  heightMarkers.splice(deleteAt, 1);
6140
6284
  useGlobalHeightFlags.splice(deleteAt, 1);
6285
+ waypointIds.splice(deleteAt, 1);
6141
6286
  console.log("[PathEditing] \u5220\u9664\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length);
6142
6287
  entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
6143
6288
  const newEditedIndices = /* @__PURE__ */ new Set();
@@ -6224,12 +6369,36 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
6224
6369
  }
6225
6370
  airplaneCursor.updateOptions({
6226
6371
  onPose: (pose) => {
6372
+ if (isUpdatingFromDrag) return;
6227
6373
  try {
6228
6374
  if (preview) {
6229
6375
  preview.setPose(pose.position, pose.heading, pose.pitch, pose.roll, 50);
6230
6376
  }
6231
6377
  } catch {
6232
6378
  }
6379
+ if (activeIndex !== void 0 && !isUpdatingFromCursor) {
6380
+ isUpdatingFromCursor = true;
6381
+ try {
6382
+ positions[activeIndex] = pose.position;
6383
+ headings[activeIndex] = pose.heading;
6384
+ pitches[activeIndex] = pose.pitch;
6385
+ rolls[activeIndex] = pose.roll;
6386
+ if (handles[activeIndex]) {
6387
+ handles[activeIndex].position = pose.position;
6388
+ }
6389
+ vertexLabelManager.updateLabelPosition(activeIndex, pose.position);
6390
+ createOrUpdateMarkerForIndex(activeIndex);
6391
+ editedIndices.add(activeIndex);
6392
+ useGlobalHeightFlags[activeIndex] = false;
6393
+ try {
6394
+ entity.polyline.positions = positions.slice();
6395
+ } catch {
6396
+ }
6397
+ viewer.scene?.requestRender?.();
6398
+ } finally {
6399
+ isUpdatingFromCursor = false;
6400
+ }
6401
+ }
6233
6402
  }
6234
6403
  });
6235
6404
  }
@@ -6305,24 +6474,38 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
6305
6474
  },
6306
6475
  onVertexDragMove: (index, newPosition) => {
6307
6476
  console.log("[PathEditing] onVertexDragMove \u88AB\u8C03\u7528, index:", index);
6308
- positions[index] = newPosition;
6309
- if (handles[index]) {
6477
+ if (isUpdatingFromCursor) return;
6478
+ isUpdatingFromDrag = true;
6479
+ try {
6480
+ positions[index] = newPosition;
6481
+ if (handles[index]) {
6482
+ try {
6483
+ handles[index].position = newPosition;
6484
+ } catch {
6485
+ }
6486
+ }
6310
6487
  try {
6311
- handles[index].position = newPosition;
6488
+ vertexLabelManager.updateLabelPosition(index, newPosition);
6312
6489
  } catch {
6313
6490
  }
6314
- }
6315
- try {
6316
- vertexLabelManager.updateLabelPosition(index, newPosition);
6317
- } catch {
6318
- }
6319
- try {
6320
- entity.polyline.positions = positions.slice();
6321
- } catch {
6322
- }
6323
- try {
6324
- viewer.scene.requestRender();
6325
- } catch {
6491
+ try {
6492
+ entity.polyline.positions = positions.slice();
6493
+ } catch {
6494
+ }
6495
+ try {
6496
+ viewer.scene.requestRender();
6497
+ } catch {
6498
+ }
6499
+ if (airplaneCursor && index === activeIndex) {
6500
+ airplaneCursor.setPose(
6501
+ newPosition,
6502
+ headings[index] ?? 0,
6503
+ pitches[index] ?? -10,
6504
+ rolls[index] ?? 0
6505
+ );
6506
+ }
6507
+ } finally {
6508
+ isUpdatingFromDrag = false;
6326
6509
  }
6327
6510
  if (options?.onVertexDragMoveDetail) {
6328
6511
  try {
@@ -6526,6 +6709,55 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
6526
6709
  // 🆕 添加相对高度
6527
6710
  };
6528
6711
  };
6712
+ if (options?.initialClickPosition && positions.length === 0) {
6713
+ console.log("[PathEditing] \u5904\u7406\u521D\u59CB\u70B9\u51FB\u4F4D\u7F6E\uFF0C\u7ACB\u5373\u6DFB\u52A0\u7B2C\u4E00\u4E2A\u822A\u70B9");
6714
+ let finalPosition = options.initialClickPosition;
6715
+ try {
6716
+ const cartographic = C.Cartographic.fromCartesian(options.initialClickPosition);
6717
+ const lon = C.Math.toDegrees(cartographic.longitude);
6718
+ const lat = C.Math.toDegrees(cartographic.latitude);
6719
+ let finalHeight;
6720
+ if (currentAltitudeMode === "absolute") {
6721
+ finalHeight = currentDefaultAltitude;
6722
+ } else if (currentAltitudeMode === "relativeToGround") {
6723
+ const terrainHeight = queryTerrainHeightSync(CesiumNS, viewer, options.initialClickPosition);
6724
+ finalHeight = terrainHeight + currentDefaultAltitude;
6725
+ } else if (currentAltitudeMode === "relativeToStart") {
6726
+ finalHeight = currentDefaultAltitude;
6727
+ } else {
6728
+ finalHeight = currentDefaultAltitude;
6729
+ }
6730
+ finalPosition = C.Cartesian3.fromDegrees(lon, lat, finalHeight);
6731
+ console.log("[PathEditing] \u521D\u59CB\u822A\u70B9\u9AD8\u5EA6\u8BA1\u7B97:", {
6732
+ altitudeMode: currentAltitudeMode,
6733
+ defaultAltitude: currentDefaultAltitude,
6734
+ finalHeight
6735
+ });
6736
+ } catch (error) {
6737
+ console.warn("[PathEditing] \u8BA1\u7B97\u521D\u59CB\u822A\u70B9\u9AD8\u5EA6\u5931\u8D25:", error);
6738
+ }
6739
+ const totalBefore = positions.length;
6740
+ insertVertex(0, finalPosition, { heading: 0, pitch: -10, roll: 0 });
6741
+ if (options?.onVertexInsertDetail) {
6742
+ try {
6743
+ const totalAfter = positions.length;
6744
+ const newVertex = buildVertexDetailInfo(0);
6745
+ const operationInfo = {
6746
+ type: "insert",
6747
+ index: 0,
6748
+ displayNumber: newVertex.displayNumber,
6749
+ totalVerticesBefore: totalBefore,
6750
+ totalVerticesAfter: totalAfter,
6751
+ newVertex,
6752
+ timestamp: /* @__PURE__ */ new Date(),
6753
+ totalDistance: getTotalDistance()
6754
+ };
6755
+ options.onVertexInsertDetail(operationInfo);
6756
+ } catch (error) {
6757
+ console.error("[PathEditing] \u89E6\u53D1 onVertexInsertDetail \u56DE\u8C03\u5931\u8D25:", error);
6758
+ }
6759
+ }
6760
+ }
6529
6761
  return {
6530
6762
  /** 保存并退出编辑模式 */
6531
6763
  saveAndStop: () => {
@@ -6567,8 +6799,10 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
6567
6799
  distance: calculatePathDistance(CesiumNS, positions, index),
6568
6800
  ellipsoidHeight,
6569
6801
  relativeHeight,
6570
- useGlobalHeight: useGlobalHeightFlags[index] ?? false
6802
+ useGlobalHeight: useGlobalHeightFlags[index] ?? false,
6571
6803
  // 🆕 添加 useGlobalHeight
6804
+ waypointId: waypointIds[index]
6805
+ // 🆕 添加 waypointId(原有航点保留ID,新插入航点为 undefined)
6572
6806
  };
6573
6807
  });
6574
6808
  cleanupSession();
@@ -6951,6 +7185,67 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
6951
7185
  options2?.onProgress?.("error");
6952
7186
  return { success: false, error: String(error) };
6953
7187
  }
7188
+ },
7189
+ /**
7190
+ * 🆕 获取当前选中航点索引
7191
+ * @returns 当前选中的航点索引,如果没有选中则返回 undefined
7192
+ */
7193
+ getActiveIndex: () => {
7194
+ return activeIndex;
7195
+ },
7196
+ /**
7197
+ * 🆕 在选中航点和下一个航点中间插入新航点
7198
+ * @param index 选中航点的索引(如果不传则使用当前选中的航点)
7199
+ * @returns 是否插入成功
7200
+ */
7201
+ insertMidpoint: (index) => {
7202
+ const targetIndex = index ?? activeIndex;
7203
+ if (targetIndex === void 0) {
7204
+ console.warn("[insertMidpoint] \u6CA1\u6709\u9009\u4E2D\u822A\u70B9");
7205
+ return false;
7206
+ }
7207
+ if (targetIndex >= positions.length - 1) {
7208
+ console.warn("[insertMidpoint] \u9009\u4E2D\u822A\u70B9\u662F\u6700\u540E\u4E00\u4E2A\uFF0C\u65E0\u6CD5\u63D2\u5165\u4E2D\u70B9");
7209
+ return false;
7210
+ }
7211
+ const point1 = positions[targetIndex];
7212
+ const point2 = positions[targetIndex + 1];
7213
+ const midpoint = C.Cartesian3.midpoint(point1, point2, new C.Cartesian3());
7214
+ const carto1 = C.Cartographic.fromCartesian(point1);
7215
+ const carto2 = C.Cartographic.fromCartesian(point2);
7216
+ const deltaLon = carto2.longitude - carto1.longitude;
7217
+ let heading = Math.atan2(
7218
+ Math.sin(deltaLon) * Math.cos(carto2.latitude),
7219
+ Math.cos(carto1.latitude) * Math.sin(carto2.latitude) - Math.sin(carto1.latitude) * Math.cos(carto2.latitude) * Math.cos(deltaLon)
7220
+ );
7221
+ heading = C.Math.toDegrees(heading);
7222
+ if (heading > 180) heading -= 360;
7223
+ if (heading < -180) heading += 360;
7224
+ const pitch = pitches[targetIndex] ?? -10;
7225
+ const roll = rolls[targetIndex] ?? 0;
7226
+ console.log("[insertMidpoint] \u5728\u7D22\u5F15", targetIndex, "\u548C", targetIndex + 1, "\u4E4B\u95F4\u63D2\u5165\u4E2D\u70B9");
7227
+ const totalBefore = positions.length;
7228
+ insertVertex(targetIndex + 1, midpoint, { heading, pitch, roll });
7229
+ if (options?.onVertexInsertDetail) {
7230
+ try {
7231
+ const totalAfter = positions.length;
7232
+ const newVertex = buildVertexDetailInfo(targetIndex + 1);
7233
+ const operationInfo = {
7234
+ type: "insert",
7235
+ index: targetIndex + 1,
7236
+ displayNumber: newVertex.displayNumber,
7237
+ totalVerticesBefore: totalBefore,
7238
+ totalVerticesAfter: totalAfter,
7239
+ newVertex,
7240
+ timestamp: /* @__PURE__ */ new Date(),
7241
+ totalDistance: getTotalDistance()
7242
+ };
7243
+ options.onVertexInsertDetail(operationInfo);
7244
+ } catch (error) {
7245
+ console.error("Error in onVertexInsertDetail:", error);
7246
+ }
7247
+ }
7248
+ return true;
6954
7249
  }
6955
7250
  };
6956
7251
  }
@@ -7032,8 +7327,8 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
7032
7327
  try {
7033
7328
  const auto = options?.autoStartEditing;
7034
7329
  const editOptions = {
7035
- // 默认使用自由编辑模式(快速编辑和自由编辑互斥)
7036
- quickEdit: false
7330
+ // 🔧 修复:默认使用快速编辑模式,用户点击地图即可添加航点
7331
+ quickEdit: true
7037
7332
  };
7038
7333
  if (typeof auto === "object" && auto.preview) {
7039
7334
  editOptions.preview = auto.preview;
@@ -7059,8 +7354,9 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
7059
7354
  editOptions.altitudeMode = altitudeMode;
7060
7355
  editOptions.defaultAltitude = defaultAltitude;
7061
7356
  editOptions.climbHeight = climbHeight;
7357
+ editOptions.initialClickPosition = picked;
7062
7358
  editSession = startPathEditing(CesiumNS, viewer, created, editOptions);
7063
- console.log("[startPathDrawing] \u7F16\u8F91\u6A21\u5F0F\u5DF2\u542F\u52A8\uFF0C\u81EA\u7531\u7F16\u8F91\u6A21\u5F0F\uFF0C\u4F7F\u7528\u98DE\u673A\u6E38\u6807\u6DFB\u52A0\u822A\u70B9");
7359
+ console.log("[startPathDrawing] \u7F16\u8F91\u6A21\u5F0F\u5DF2\u542F\u52A8\uFF0C\u5FEB\u901F\u7F16\u8F91\u6A21\u5F0F\uFF0C\u70B9\u51FB\u5730\u56FE\u6DFB\u52A0\u822A\u70B9");
7064
7360
  if (editSession && options?.onEditingStarted) {
7065
7361
  try {
7066
7362
  options.onEditingStarted(editSession);
@@ -11934,6 +12230,171 @@ var PointCloudPicker = class {
11934
12230
  }
11935
12231
  };
11936
12232
 
12233
+ // src/core/path-manager/RealtimeFlightTracker.ts
12234
+ var RealtimeFlightTracker = class {
12235
+ constructor(CesiumNS, viewer, options) {
12236
+ __publicField(this, "CesiumNS");
12237
+ __publicField(this, "viewer");
12238
+ __publicField(this, "options");
12239
+ __publicField(this, "airplaneCursor");
12240
+ __publicField(this, "trackEntity");
12241
+ __publicField(this, "trackPositions", []);
12242
+ __publicField(this, "isInitialized", false);
12243
+ this.CesiumNS = CesiumNS;
12244
+ this.viewer = viewer;
12245
+ const C = CesiumNS;
12246
+ this.options = {
12247
+ showTrack: options?.showTrack ?? true,
12248
+ fovDeg: options?.fovDeg ?? 50,
12249
+ layer: options?.layer,
12250
+ trackStyle: {
12251
+ color: options?.trackStyle?.color ?? C.Color.CYAN,
12252
+ width: options?.trackStyle?.width ?? 3,
12253
+ clampToGround: options?.trackStyle?.clampToGround ?? false,
12254
+ maxPoints: options?.trackStyle?.maxPoints ?? 1e3
12255
+ }
12256
+ };
12257
+ }
12258
+ /**
12259
+ * 更新飞机位置(接收新航点)
12260
+ * @param waypoint 实时航点数据
12261
+ */
12262
+ updatePosition(waypoint) {
12263
+ const C = this.CesiumNS;
12264
+ const position = C.Cartesian3.fromDegrees(
12265
+ waypoint.longitude,
12266
+ waypoint.latitude,
12267
+ waypoint.altitude
12268
+ );
12269
+ if (!this.isInitialized) {
12270
+ this.initialize(position);
12271
+ this.isInitialized = true;
12272
+ }
12273
+ if (this.airplaneCursor) {
12274
+ this.airplaneCursor.setPose(
12275
+ position,
12276
+ waypoint.heading ?? 0,
12277
+ waypoint.pitch ?? 0,
12278
+ waypoint.roll ?? 0
12279
+ );
12280
+ }
12281
+ if (this.options.showTrack) {
12282
+ this.addTrackPoint(position);
12283
+ }
12284
+ this.viewer.scene.requestRender();
12285
+ }
12286
+ /**
12287
+ * 初始化飞机和轨迹实体
12288
+ */
12289
+ initialize(initialPosition) {
12290
+ this.airplaneCursor = new AirplaneCursor(
12291
+ this.CesiumNS,
12292
+ this.viewer,
12293
+ initialPosition,
12294
+ {
12295
+ stepMeters: 0,
12296
+ // 禁用键盘控制
12297
+ angleStepDeg: 0,
12298
+ fovDeg: this.options.fovDeg,
12299
+ visible: true,
12300
+ showFrustum: false
12301
+ // 实时飞行追踪不显示视锥体
12302
+ }
12303
+ );
12304
+ if (this.options.showTrack) {
12305
+ this.createTrackEntity();
12306
+ }
12307
+ }
12308
+ /**
12309
+ * 创建轨迹实体
12310
+ */
12311
+ createTrackEntity() {
12312
+ const C = this.CesiumNS;
12313
+ const entities = this.options.layer?.entities ?? this.viewer.entities;
12314
+ const style = this.options.trackStyle;
12315
+ this.trackEntity = entities.add({
12316
+ id: `realtime-flight-track-${Date.now()}`,
12317
+ polyline: {
12318
+ // 使用 CallbackProperty 实现动态更新
12319
+ positions: new C.CallbackProperty(() => {
12320
+ return this.trackPositions.slice();
12321
+ }, false),
12322
+ width: style.width,
12323
+ material: style.color,
12324
+ clampToGround: style.clampToGround
12325
+ },
12326
+ properties: {
12327
+ _type: "realtime-flight-track"
12328
+ }
12329
+ });
12330
+ }
12331
+ /**
12332
+ * 添加轨迹点
12333
+ */
12334
+ addTrackPoint(position) {
12335
+ this.trackPositions.push(position);
12336
+ const maxPoints = this.options.trackStyle.maxPoints;
12337
+ if (this.trackPositions.length > maxPoints) {
12338
+ this.trackPositions = this.trackPositions.slice(-maxPoints);
12339
+ }
12340
+ }
12341
+ /**
12342
+ * 清除轨迹
12343
+ */
12344
+ clearTrack() {
12345
+ this.trackPositions = [];
12346
+ this.viewer.scene.requestRender();
12347
+ }
12348
+ /**
12349
+ * 获取当前轨迹点数
12350
+ */
12351
+ getTrackPointCount() {
12352
+ return this.trackPositions.length;
12353
+ }
12354
+ /**
12355
+ * 设置轨迹可见性
12356
+ */
12357
+ setTrackVisible(visible) {
12358
+ if (this.trackEntity) {
12359
+ this.trackEntity.show = visible;
12360
+ this.viewer.scene.requestRender();
12361
+ }
12362
+ }
12363
+ /**
12364
+ * 飞到当前飞机位置
12365
+ */
12366
+ flyToAirplane(options) {
12367
+ if (this.trackPositions.length === 0) return;
12368
+ const C = this.CesiumNS;
12369
+ const lastPosition = this.trackPositions[this.trackPositions.length - 1];
12370
+ const carto = C.Cartographic.fromCartesian(lastPosition);
12371
+ this.viewer.camera.flyTo({
12372
+ destination: C.Cartesian3.fromRadians(
12373
+ carto.longitude,
12374
+ carto.latitude,
12375
+ (options?.height ?? 500) + carto.height
12376
+ ),
12377
+ duration: options?.duration ?? 1.5
12378
+ });
12379
+ }
12380
+ /**
12381
+ * 销毁并清理资源
12382
+ */
12383
+ destroy() {
12384
+ const entities = this.options.layer?.entities ?? this.viewer.entities;
12385
+ if (this.airplaneCursor) {
12386
+ this.airplaneCursor.destroy();
12387
+ this.airplaneCursor = void 0;
12388
+ }
12389
+ if (this.trackEntity) {
12390
+ entities.remove(this.trackEntity);
12391
+ this.trackEntity = void 0;
12392
+ }
12393
+ this.trackPositions = [];
12394
+ this.isInitialized = false;
12395
+ }
12396
+ };
12397
+
11937
12398
  // src/utils/pathToSinoflyAdapter.ts
11938
12399
  function convertCartesian3ToLatLonHeight(CesiumNS, position) {
11939
12400
  const C = CesiumNS;
@@ -12063,8 +12524,8 @@ function convertPathToSinofly(data, options) {
12063
12524
  );
12064
12525
  const action = convertPoseToActionGroup(wp.heading, wp.pitch, wp.roll, wp.fov, newIndex);
12065
12526
  const waypoint = {
12066
- waypointId: "",
12067
- // 由服务器自动生成
12527
+ waypointId: wp.waypointId ?? "",
12528
+ // 🆕 使用传入的 waypointId,新航点为空字符串
12068
12529
  waylineId,
12069
12530
  index: newIndex,
12070
12531
  latitude,
@@ -12513,13 +12974,18 @@ exports.LayerManager = LayerManager;
12513
12974
  exports.PathSafetyChecker = PathSafetyChecker;
12514
12975
  exports.PointCloudPicker = PointCloudPicker;
12515
12976
  exports.PolygonEditor = PolygonEditor;
12977
+ exports.RealtimeFlightTracker = RealtimeFlightTracker;
12516
12978
  exports.SceneManager = SceneManager;
12517
12979
  exports.Selector = Selector;
12518
12980
  exports.StateManager = StateManager;
12519
12981
  exports.assertCesiumAssetsConfigured = assertCesiumAssetsConfigured;
12520
12982
  exports.calculateAbsoluteHeight = calculateAbsoluteHeight;
12521
12983
  exports.calculateBoundsDiagonal = calculateBoundsDiagonal;
12984
+ exports.calculateDistance = calculateDistance;
12985
+ exports.calculateDistanceAndMidpoint = calculateDistanceAndMidpoint;
12522
12986
  exports.calculateGeoBounds = calculateGeoBounds;
12987
+ exports.calculateHeadingBetweenPoints = calculateHeadingBetweenPoints;
12988
+ exports.calculateMidpoint = calculateMidpoint;
12523
12989
  exports.calculateRelativeHeight = calculateRelativeHeight;
12524
12990
  exports.configureCesiumAssets = configureCesiumAssets;
12525
12991
  exports.configureCesiumIonToken = configureCesiumIonToken;