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