@jorgmoritz/gis-manager 0.1.36 → 0.1.38

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/vue/index.js CHANGED
@@ -2587,7 +2587,7 @@ var FrustumPyramid = class {
2587
2587
  showAt(apex, headingDeg, pitchDeg = 0, rollDeg = 0, fovDeg, lengthOverride) {
2588
2588
  if (this.destroyed) return;
2589
2589
  const C = this.CesiumNS;
2590
- const length = Math.max(1, lengthOverride ?? this.opts.length ?? 1e3);
2590
+ const length = Math.max(1, lengthOverride ?? this.opts.length ?? 500);
2591
2591
  const lakeEdge = this.opts.color ?? C.Color.fromCssColorString("#1e90ff");
2592
2592
  const alpha = this.opts.fillAlpha ?? 0.25;
2593
2593
  const lakeFill = (this.opts.fillColor ?? C.Color.fromCssColorString("#1e90ff")).withAlpha(alpha);
@@ -2770,8 +2770,6 @@ var AirplaneCursor = class {
2770
2770
  __publicField(this, "angleStep");
2771
2771
  /** 加速倍率(按住 Shift 生效) */
2772
2772
  __publicField(this, "fastFactor");
2773
- /** 最小高度(米) */
2774
- __publicField(this, "minHeight");
2775
2773
  /** 外部传入的行为配置与回调 */
2776
2774
  __publicField(this, "opts");
2777
2775
  // 使用内联的 _FrustumPyramid
@@ -2799,7 +2797,6 @@ var AirplaneCursor = class {
2799
2797
  this.step = opts.stepMeters ?? 2;
2800
2798
  this.angleStep = opts.angleStepDeg ?? 1;
2801
2799
  this.fastFactor = opts.fastFactor ?? 5;
2802
- this.minHeight = opts.minHeight ?? 0;
2803
2800
  this.currentFOV = opts.fovDeg ?? 50;
2804
2801
  this.ensureEntity(opts.color ?? C.Color.CYAN.withAlpha(0.9));
2805
2802
  this.attachKeyboard(opts);
@@ -3024,11 +3021,8 @@ var AirplaneCursor = class {
3024
3021
  }
3025
3022
  if (this.keysPressed.has("z")) {
3026
3023
  const newPos = addVec(pose.position, C.Cartesian3.multiplyByScalar(u3, -step, new C.Cartesian3()));
3027
- const newCarto = C.Cartographic.fromCartesian(newPos);
3028
- if (newCarto && newCarto.height >= this.minHeight) {
3029
- setPos(newPos);
3030
- moved = true;
3031
- }
3024
+ setPos(newPos);
3025
+ moved = true;
3032
3026
  }
3033
3027
  if (this.keysPressed.has("q")) {
3034
3028
  pose.heading = clampDeg(pose.heading - ang);
@@ -3081,8 +3075,8 @@ var AirplaneCursor = class {
3081
3075
  this.viewer.dataSources.add(layer);
3082
3076
  }
3083
3077
  this.frustum = new FrustumPyramid(this.CesiumNS, layer, {
3084
- fov: this.opts.fovDeg ?? 50,
3085
- length: 500,
3078
+ fov: this.opts.fovDeg ?? 40,
3079
+ length: 80,
3086
3080
  color: this.opts.color,
3087
3081
  fillAlpha: 0.25,
3088
3082
  width: 2,
@@ -3090,14 +3084,17 @@ var AirplaneCursor = class {
3090
3084
  logAngles: false,
3091
3085
  logThrottleMs: 300
3092
3086
  });
3087
+ const frustumFovScale = 0.6;
3088
+ const frustumLengthScale = 0.1;
3093
3089
  this.frustum.showAtDynamic(
3094
3090
  () => this.pose.position,
3095
3091
  () => this.pose.heading,
3096
3092
  () => this.pose.pitch,
3097
3093
  () => this.pose.roll,
3098
- () => this.currentFOV,
3099
- // 使用动态更新的 currentFOV
3100
- () => 1e3
3094
+ () => this.currentFOV * frustumFovScale,
3095
+ // 缩小 FOV
3096
+ () => 1e3 * frustumLengthScale
3097
+ // 缩小深度(1000 -> 100)
3101
3098
  );
3102
3099
  try {
3103
3100
  this.heightMarker = new HeightMarker(this.CesiumNS, this.viewer, layer);
@@ -3613,7 +3610,7 @@ function calculatePathDistance(CesiumNS, positions, targetIndex, hiddenClimbInde
3613
3610
  if (targetIndex < 0 || targetIndex >= positions.length) return 0;
3614
3611
  const C = CesiumNS;
3615
3612
  let totalDistance = 0;
3616
- const startIndex = hiddenClimbIndex === 1 ? 2 : 1;
3613
+ const startIndex = 1;
3617
3614
  if (targetIndex <= startIndex) return 0;
3618
3615
  for (let i = startIndex; i <= targetIndex; i++) {
3619
3616
  try {
@@ -3656,9 +3653,11 @@ var VertexInsertionHandler = class {
3656
3653
  * @param heightMarkers 高度标记数组(引用)
3657
3654
  * @param airplaneCursor 飞机游标(可选)
3658
3655
  * @param poseOrient 顶点姿态(可选)
3656
+ * @param useGlobalHeightFlags 🆕 是否使用全局高度标志数组(引用,可选)
3657
+ * @param altitudeModeOptions 🆕 高度模式选项(可选)
3659
3658
  * @returns 实际插入的索引
3660
3659
  */
3661
- insertVertex(insertAt, position, positions, handles, headings, pitches, rolls, fovs, editedIndices, vertexLabelManager, heightMarkers, airplaneCursor, poseOrient) {
3660
+ insertVertex(insertAt, position, positions, handles, headings, pitches, rolls, fovs, editedIndices, vertexLabelManager, heightMarkers, airplaneCursor, poseOrient, useGlobalHeightFlags) {
3662
3661
  const C = this.CesiumNS;
3663
3662
  if (this.hiddenClimbIndex === 1 && insertAt <= 1) {
3664
3663
  insertAt = 2;
@@ -3693,6 +3692,12 @@ var VertexInsertionHandler = class {
3693
3692
  pitches.splice(insertAt, 0, pose.pitch);
3694
3693
  rolls.splice(insertAt, 0, pose.roll);
3695
3694
  fovs.splice(insertAt, 0, 50);
3695
+ if (useGlobalHeightFlags) {
3696
+ for (let i = useGlobalHeightFlags.length - 1; i >= insertAt; i--) {
3697
+ useGlobalHeightFlags[i + 1] = useGlobalHeightFlags[i];
3698
+ }
3699
+ useGlobalHeightFlags[insertAt] = true;
3700
+ }
3696
3701
  return insertAt;
3697
3702
  }
3698
3703
  /**
@@ -3901,7 +3906,6 @@ var VertexDragHandler = class {
3901
3906
  __publicField(this, "CesiumNS");
3902
3907
  __publicField(this, "viewer");
3903
3908
  __publicField(this, "hiddenClimbIndex");
3904
- __publicField(this, "minHeight");
3905
3909
  __publicField(this, "callbacks");
3906
3910
  __publicField(this, "dragState", null);
3907
3911
  __publicField(this, "canvas");
@@ -3910,7 +3914,6 @@ var VertexDragHandler = class {
3910
3914
  this.CesiumNS = options.CesiumNS;
3911
3915
  this.viewer = options.viewer;
3912
3916
  this.hiddenClimbIndex = options.hiddenClimbIndex;
3913
- this.minHeight = options.minHeight ?? 0;
3914
3917
  this.callbacks = callbacks;
3915
3918
  this.canvas = this.viewer.scene.canvas;
3916
3919
  this.setupKeyboardListeners();
@@ -4086,7 +4089,7 @@ var VertexDragHandler = class {
4086
4089
  const cameraHeight = this.viewer.camera.positionCartographic.height;
4087
4090
  const scaleFactor = Math.max(cameraHeight / 1e3, 0.5);
4088
4091
  const heightDelta = -deltaY * scaleFactor;
4089
- const newHeight = Math.max(this.minHeight, this.dragState.startHeight + heightDelta);
4092
+ const newHeight = this.dragState.startHeight + heightDelta;
4090
4093
  const newPosition = C.Cartesian3.fromDegrees(
4091
4094
  this.dragState.startLonLat.lon,
4092
4095
  this.dragState.startLonLat.lat,
@@ -4171,6 +4174,79 @@ var VertexDragHandler = class {
4171
4174
  }
4172
4175
  };
4173
4176
 
4177
+ // src/core/path-manager/utils/TerrainHeightQuery.ts
4178
+ async function queryTerrainHeights(CesiumNS, viewer, positions) {
4179
+ const C = CesiumNS;
4180
+ if (!positions || positions.length === 0) {
4181
+ return [];
4182
+ }
4183
+ const cartographics = positions.map((pos) => {
4184
+ try {
4185
+ return C.Cartographic.fromCartesian(pos);
4186
+ } catch {
4187
+ return new C.Cartographic(0, 0, 0);
4188
+ }
4189
+ });
4190
+ const terrainProvider = viewer.terrainProvider;
4191
+ if (!terrainProvider || !C.sampleTerrainMostDetailed) {
4192
+ console.warn("[TerrainHeightQuery] \u65E0\u53EF\u7528\u5730\u5F62\u63D0\u4F9B\u8005\uFF0C\u4F7F\u7528\u540C\u6B65\u56DE\u9000\u65B9\u6848");
4193
+ return positions.map((pos) => queryTerrainHeightSync(CesiumNS, viewer, pos));
4194
+ }
4195
+ try {
4196
+ const sampledPositions = await C.sampleTerrainMostDetailed(
4197
+ terrainProvider,
4198
+ cartographics
4199
+ );
4200
+ return sampledPositions.map((carto) => {
4201
+ const height = carto?.height;
4202
+ return typeof height === "number" && !isNaN(height) ? height : 0;
4203
+ });
4204
+ } catch (error) {
4205
+ console.warn("[TerrainHeightQuery] \u5F02\u6B65\u67E5\u8BE2\u5931\u8D25\uFF0C\u4F7F\u7528\u540C\u6B65\u56DE\u9000:", error);
4206
+ return positions.map((pos) => queryTerrainHeightSync(CesiumNS, viewer, pos));
4207
+ }
4208
+ }
4209
+ function queryTerrainHeightSync(CesiumNS, viewer, position) {
4210
+ const C = CesiumNS;
4211
+ try {
4212
+ const cartographic = C.Cartographic.fromCartesian(position);
4213
+ const globe = viewer.scene.globe;
4214
+ if (globe && typeof globe.getHeight === "function") {
4215
+ const height = globe.getHeight(cartographic);
4216
+ if (typeof height === "number" && !isNaN(height)) {
4217
+ return height;
4218
+ }
4219
+ }
4220
+ } catch (error) {
4221
+ console.warn("[TerrainHeightQuery] \u540C\u6B65\u67E5\u8BE2\u5931\u8D25:", error);
4222
+ }
4223
+ return 0;
4224
+ }
4225
+ function calculateAbsoluteHeight(altitudeMode, defaultAltitude, startPointAltitude, terrainHeight) {
4226
+ switch (altitudeMode) {
4227
+ case "absolute":
4228
+ return defaultAltitude;
4229
+ case "relativeToStart":
4230
+ return startPointAltitude + defaultAltitude;
4231
+ case "relativeToGround":
4232
+ return terrainHeight + defaultAltitude;
4233
+ default:
4234
+ return defaultAltitude;
4235
+ }
4236
+ }
4237
+ function calculateRelativeHeight(altitudeMode, absoluteHeight, startPointAltitude, terrainHeight) {
4238
+ switch (altitudeMode) {
4239
+ case "absolute":
4240
+ return absoluteHeight;
4241
+ case "relativeToStart":
4242
+ return absoluteHeight - startPointAltitude;
4243
+ case "relativeToGround":
4244
+ return absoluteHeight - terrainHeight;
4245
+ default:
4246
+ return absoluteHeight;
4247
+ }
4248
+ }
4249
+
4174
4250
  // src/core/path-manager/editing/PathEditingEventHandler.ts
4175
4251
  var PathEditingEventHandler = class {
4176
4252
  constructor(options, callbacks) {
@@ -4193,8 +4269,7 @@ var PathEditingEventHandler = class {
4193
4269
  {
4194
4270
  CesiumNS: this.CesiumNS,
4195
4271
  viewer: this.viewer,
4196
- hiddenClimbIndex: this.hiddenClimbIndex,
4197
- minHeight: options.minHeight
4272
+ hiddenClimbIndex: this.hiddenClimbIndex
4198
4273
  },
4199
4274
  {
4200
4275
  onDragStart: (index) => {
@@ -4429,23 +4504,14 @@ var PathEditingEventHandler = class {
4429
4504
  buildVertexContextMenuItems(vertexIndex) {
4430
4505
  const menuItems = [];
4431
4506
  const positions = this.callbacks.getPositions?.() || [];
4432
- const visibleVertexCount = positions.length - (this.hiddenClimbIndex === 1 ? 1 : 0);
4433
- const canDelete = visibleVertexCount > 1;
4434
- const isStartPoint = vertexIndex === 0 || this.hiddenClimbIndex === 1 && vertexIndex === 1;
4435
- if (canDelete && !isStartPoint) {
4507
+ const canDelete = positions.length > 1;
4508
+ if (canDelete) {
4436
4509
  menuItems.push({
4437
4510
  label: `\u5220\u9664\u822A\u70B9 ${this.getVertexDisplayNumber(vertexIndex)}`,
4438
4511
  action: () => {
4439
4512
  this.handleDeleteVertex(vertexIndex);
4440
4513
  }
4441
4514
  });
4442
- } else if (isStartPoint) {
4443
- menuItems.push({
4444
- label: "\u8D77\u70B9\u4E0D\u53EF\u5220\u9664",
4445
- action: () => {
4446
- },
4447
- disabled: true
4448
- });
4449
4515
  } else {
4450
4516
  menuItems.push({
4451
4517
  label: "\u81F3\u5C11\u4FDD\u7559\u4E00\u4E2A\u822A\u70B9",
@@ -4457,12 +4523,10 @@ var PathEditingEventHandler = class {
4457
4523
  return menuItems;
4458
4524
  }
4459
4525
  /**
4460
- * 获取顶点的显示编号(跳过隐藏爬升点)
4526
+ * 获取顶点的显示编号
4527
+ * 🆕 不再跳过隐藏爬升点,直接返回索引
4461
4528
  */
4462
4529
  getVertexDisplayNumber(index) {
4463
- if (this.hiddenClimbIndex === 1 && index > 1) {
4464
- return index - 1;
4465
- }
4466
4530
  return index;
4467
4531
  }
4468
4532
  /**
@@ -4494,18 +4558,17 @@ var PathEditingEventHandler = class {
4494
4558
  };
4495
4559
  }
4496
4560
  const totalVertices = positions.length;
4497
- const totalVisibleVertices = totalVertices - (this.hiddenClimbIndex === 1 ? 1 : 0);
4498
- const isStartPoint = index === 0 || this.hiddenClimbIndex === 1 && index === 1;
4499
- const isHiddenClimb = this.hiddenClimbIndex === 1 && index === 1;
4500
- const canDelete = totalVisibleVertices > 1 && !isStartPoint;
4561
+ const totalVisibleVertices = totalVertices;
4562
+ const isStartPoint = false;
4563
+ const isHiddenClimb = false;
4564
+ const canDelete = totalVertices > 1;
4501
4565
  let pathProperties;
4502
4566
  if (entity) {
4503
4567
  try {
4504
4568
  const props = entity.properties;
4505
4569
  pathProperties = {
4506
4570
  altitudeMode: props?._altitudeMode?.getValue?.() ?? props?._altitudeMode,
4507
- climbHeight: props?._climbHeight?.getValue?.() ?? props?._climbHeight,
4508
- hasHiddenClimb: props?._hasHiddenClimb?.getValue?.() ?? props?._hasHiddenClimb
4571
+ climbHeight: props?._climbHeight?.getValue?.() ?? props?._climbHeight
4509
4572
  };
4510
4573
  } catch {
4511
4574
  }
@@ -4544,11 +4607,9 @@ var PathEditingEventHandler = class {
4544
4607
  }
4545
4608
  /**
4546
4609
  * 处理插入顶点
4610
+ * 🆕 不再跳过隐藏爬升点
4547
4611
  */
4548
4612
  handleInsertVertex(insertAt, pose, type) {
4549
- if (this.hiddenClimbIndex === 1 && insertAt <= 1) {
4550
- insertAt = 2;
4551
- }
4552
4613
  if (this.callbacks.onInsertVertex) {
4553
4614
  this.callbacks.onInsertVertex(insertAt, pose.position, {
4554
4615
  heading: pose.heading,
@@ -4579,6 +4640,10 @@ var PathEditingEventHandler = class {
4579
4640
  }
4580
4641
  /**
4581
4642
  * 🆕 处理快速编辑模式的点击
4643
+ * 根据 altitudeMode 计算航点高度:
4644
+ * - absolute: 航点高度 = defaultAltitude
4645
+ * - relativeToGround: 航点高度 = 地形高度 + defaultAltitude
4646
+ * - relativeToStart: 航点高度 = 起飞点高度 + defaultAltitude
4582
4647
  */
4583
4648
  handleQuickEditClick(movement) {
4584
4649
  const position = this.getPositionFromMouse(movement);
@@ -4588,11 +4653,9 @@ var PathEditingEventHandler = class {
4588
4653
  }
4589
4654
  const quickEditOptions = this.callbacks.getQuickEditOptions?.();
4590
4655
  if (!quickEditOptions) return;
4591
- const { climbHeight, altitudeMode } = quickEditOptions;
4656
+ const { defaultAltitude, altitudeMode } = quickEditOptions;
4657
+ const newPosition = this.calculatePositionByAltitudeMode(position, defaultAltitude, altitudeMode);
4592
4658
  const C = this.CesiumNS;
4593
- const originalCarto = C.Cartographic.fromCartesian(position);
4594
- const originalHeight = originalCarto.height;
4595
- const newPosition = this.applyClimbHeight(position, climbHeight, altitudeMode);
4596
4659
  const newCarto = C.Cartographic.fromCartesian(newPosition);
4597
4660
  const newHeight = newCarto.height;
4598
4661
  const positions = this.callbacks.getPositions?.() || [];
@@ -4607,8 +4670,7 @@ var PathEditingEventHandler = class {
4607
4670
  insertAt,
4608
4671
  heading: heading.toFixed(2),
4609
4672
  altitudeMode,
4610
- climbHeight,
4611
- originalHeight: originalHeight.toFixed(2),
4673
+ defaultAltitude,
4612
4674
  finalHeight: newHeight.toFixed(2)
4613
4675
  });
4614
4676
  this.callbacks.onInsertVertex(insertAt, newPosition, {
@@ -4620,21 +4682,35 @@ var PathEditingEventHandler = class {
4620
4682
  }
4621
4683
  }
4622
4684
  /**
4623
- * 🆕 应用爬升高度到位置
4685
+ * 🆕 根据高度模式计算航点位置
4686
+ * - absolute: 航点高度 = defaultAltitude
4687
+ * - relativeToGround: 航点高度 = 地形高度 + defaultAltitude
4688
+ * - relativeToStart: 航点高度 = 起飞点高度 + defaultAltitude
4624
4689
  */
4625
- applyClimbHeight(position, climbHeight, altitudeMode) {
4690
+ calculatePositionByAltitudeMode(position, defaultAltitude, altitudeMode) {
4626
4691
  const C = this.CesiumNS;
4627
4692
  try {
4628
4693
  const cartographic = C.Cartographic.fromCartesian(position);
4629
4694
  const lon = C.Math.toDegrees(cartographic.longitude);
4630
4695
  const lat = C.Math.toDegrees(cartographic.latitude);
4631
- let height = cartographic.height;
4632
- if (altitudeMode === "relativeToGround" || altitudeMode === "relativeToStart") {
4633
- height += climbHeight;
4634
- } else if (altitudeMode === "absolute") {
4635
- height = climbHeight;
4696
+ let finalHeight;
4697
+ if (altitudeMode === "absolute") {
4698
+ finalHeight = defaultAltitude;
4699
+ } else if (altitudeMode === "relativeToGround") {
4700
+ const terrainHeight = queryTerrainHeightSync(this.CesiumNS, this.viewer, position);
4701
+ finalHeight = terrainHeight + defaultAltitude;
4702
+ } else if (altitudeMode === "relativeToStart") {
4703
+ const startPointAltitude = this.callbacks.getStartPointAltitude?.() ?? 0;
4704
+ finalHeight = startPointAltitude + defaultAltitude;
4705
+ } else {
4706
+ finalHeight = defaultAltitude;
4636
4707
  }
4637
- return C.Cartesian3.fromDegrees(lon, lat, height);
4708
+ console.log("[QuickEdit] \u9AD8\u5EA6\u8BA1\u7B97:", {
4709
+ altitudeMode,
4710
+ defaultAltitude,
4711
+ finalHeight
4712
+ });
4713
+ return C.Cartesian3.fromDegrees(lon, lat, finalHeight);
4638
4714
  } catch (error) {
4639
4715
  console.error("[QuickEdit] \u9AD8\u5EA6\u8BA1\u7B97\u5931\u8D25:", error);
4640
4716
  return position;
@@ -4726,19 +4802,19 @@ var VertexHandleStyleManager = class {
4726
4802
  __publicField(this, "selectedStyle");
4727
4803
  const C = CesiumNS;
4728
4804
  this.normalStyle = {
4729
- pixelSize: options?.normal?.pixelSize ?? 10,
4805
+ pixelSize: options?.normal?.pixelSize ?? 6,
4730
4806
  color: options?.normal?.color ?? C.Color.YELLOW,
4731
4807
  outlineColor: options?.normal?.outlineColor ?? C.Color.BLACK,
4732
4808
  outlineWidth: options?.normal?.outlineWidth ?? 1
4733
4809
  };
4734
4810
  this.editedStyle = {
4735
- pixelSize: options?.edited?.pixelSize ?? 10,
4811
+ pixelSize: options?.edited?.pixelSize ?? 6,
4736
4812
  color: options?.edited?.color ?? C.Color.CYAN,
4737
4813
  outlineColor: options?.edited?.outlineColor ?? C.Color.BLACK,
4738
4814
  outlineWidth: options?.edited?.outlineWidth ?? 1
4739
4815
  };
4740
4816
  this.selectedStyle = {
4741
- pixelSize: options?.selected?.pixelSize ?? 14,
4817
+ pixelSize: options?.selected?.pixelSize ?? 8,
4742
4818
  color: options?.selected?.color ?? C.Color.ORANGE,
4743
4819
  outlineColor: options?.selected?.outlineColor ?? C.Color.BLACK,
4744
4820
  outlineWidth: options?.selected?.outlineWidth ?? 2
@@ -4946,18 +5022,57 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4946
5022
  } catch {
4947
5023
  positions = [];
4948
5024
  }
4949
- let hiddenClimbIndex;
5025
+ const hiddenClimbIndex = void 0;
5026
+ let currentAltitudeMode = "absolute";
5027
+ let currentDefaultAltitude = 100;
5028
+ try {
5029
+ const props = entity.properties;
5030
+ const storedMode = props?._altitudeMode?.getValue?.() ?? props?._altitudeMode;
5031
+ if (storedMode === "absolute" || storedMode === "relativeToGround" || storedMode === "relativeToStart") {
5032
+ currentAltitudeMode = storedMode;
5033
+ }
5034
+ const storedDefaultAlt = props?._defaultAltitude?.getValue?.() ?? props?._defaultAltitude;
5035
+ if (typeof storedDefaultAlt === "number") {
5036
+ currentDefaultAltitude = storedDefaultAlt;
5037
+ }
5038
+ } catch {
5039
+ }
5040
+ if (options?.altitudeMode) {
5041
+ currentAltitudeMode = options.altitudeMode;
5042
+ }
5043
+ if (options?.defaultAltitude !== void 0) {
5044
+ currentDefaultAltitude = options.defaultAltitude;
5045
+ }
5046
+ let startPointAltitude = 0;
5047
+ try {
5048
+ if (positions.length > 0) {
5049
+ const startCarto = C.Cartographic.fromCartesian(positions[0]);
5050
+ startPointAltitude = startCarto.height ?? 0;
5051
+ }
5052
+ } catch {
5053
+ }
5054
+ const terrainHeightsCache = /* @__PURE__ */ new Map();
5055
+ const useGlobalHeightFlags = [];
4950
5056
  try {
4951
5057
  const props = entity.properties;
4952
- const hFlag = props?._hasHiddenClimb?.getValue?.() ?? props?._hasHiddenClimb;
4953
- if (hFlag && positions.length >= 2) hiddenClimbIndex = 1;
5058
+ const storedFlags = props?._useGlobalHeightFlags?.getValue?.() ?? props?._useGlobalHeightFlags;
5059
+ if (Array.isArray(storedFlags)) {
5060
+ storedFlags.forEach((flag, idx) => {
5061
+ useGlobalHeightFlags[idx] = flag;
5062
+ });
5063
+ }
4954
5064
  } catch {
4955
5065
  }
5066
+ while (useGlobalHeightFlags.length < positions.length) {
5067
+ useGlobalHeightFlags.push(false);
5068
+ }
4956
5069
  let quickEditEnabled = false;
4957
5070
  let quickEditOptions = {
4958
5071
  enabled: false,
4959
- climbHeight: 100,
4960
- altitudeMode: "absolute"
5072
+ climbHeight: currentDefaultAltitude,
5073
+ // 使用当前默认高度
5074
+ altitudeMode: currentAltitudeMode
5075
+ // 使用当前高度模式
4961
5076
  };
4962
5077
  if (options?.quickEdit) {
4963
5078
  if (typeof options.quickEdit === "boolean") {
@@ -4990,33 +5105,23 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4990
5105
  } catch {
4991
5106
  }
4992
5107
  const handles = positions.map((p, i) => {
4993
- if (hiddenClimbIndex === i) return void 0;
4994
- const isStartPoint = i === 0;
4995
5108
  const h2 = existing[i];
4996
5109
  if (h2) {
4997
5110
  try {
4998
5111
  h2.position = p;
4999
- if (!isStartPoint) {
5000
- h2.point = h2.point ?? { pixelSize: 10 };
5001
- h2.point.pixelSize = 10;
5002
- h2.point.color = C.Color.YELLOW;
5003
- h2.point.outlineColor = C.Color.BLACK;
5004
- h2.point.outlineWidth = 1;
5005
- }
5112
+ h2.point = h2.point ?? { pixelSize: 6 };
5113
+ h2.point.pixelSize = 6;
5114
+ h2.point.color = C.Color.YELLOW;
5115
+ h2.point.outlineColor = C.Color.BLACK;
5116
+ h2.point.outlineWidth = 1;
5006
5117
  h2.properties = { _type: "path-vertex", _ownerId: ownerId, _vertexIndex: i };
5007
5118
  } catch {
5008
5119
  }
5009
5120
  return h2;
5010
5121
  }
5011
- if (isStartPoint) {
5012
- return layer.entities.add({
5013
- position: p,
5014
- properties: { _type: "path-vertex", _ownerId: ownerId, _vertexIndex: i }
5015
- });
5016
- }
5017
5122
  return layer.entities.add({
5018
5123
  position: p,
5019
- point: { pixelSize: 10, color: C.Color.YELLOW, outlineColor: C.Color.BLACK, outlineWidth: 1 },
5124
+ point: { pixelSize: 6, color: C.Color.YELLOW, outlineColor: C.Color.BLACK, outlineWidth: 1 },
5020
5125
  properties: { _type: "path-vertex", _ownerId: ownerId, _vertexIndex: i }
5021
5126
  });
5022
5127
  });
@@ -5033,8 +5138,6 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5033
5138
  };
5034
5139
  const createOrUpdateMarkerForIndex = (idx) => {
5035
5140
  try {
5036
- if (hiddenClimbIndex === 1 && idx === 1) return;
5037
- if (idx === 0) return;
5038
5141
  const pos = positions[idx];
5039
5142
  if (!pos) return;
5040
5143
  const markerLayer = ensureMarkerLayer();
@@ -5116,7 +5219,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5116
5219
  };
5117
5220
  const getTotalDistance = () => {
5118
5221
  if (positions.length < 2) return 0;
5119
- return calculatePathDistance(CesiumNS, positions, positions.length - 1, hiddenClimbIndex);
5222
+ return calculatePathDistance(CesiumNS, positions, positions.length - 1);
5120
5223
  };
5121
5224
  const vertexInsertionHandler = new VertexInsertionHandler({
5122
5225
  CesiumNS,
@@ -5125,7 +5228,40 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5125
5228
  hiddenClimbIndex
5126
5229
  });
5127
5230
  let cursorStart = positions[positions.length - 1];
5128
- if (!cursorStart) cursorStart = positions[hiddenClimbIndex === 1 ? 1 : 0];
5231
+ if (!cursorStart) cursorStart = positions[0];
5232
+ if (!cursorStart) {
5233
+ try {
5234
+ const cameraPos = viewer.camera.positionCartographic;
5235
+ if (cameraPos) {
5236
+ let initialHeight = currentDefaultAltitude;
5237
+ if (currentAltitudeMode === "relativeToGround") {
5238
+ const terrainHeight = queryTerrainHeightSync(
5239
+ CesiumNS,
5240
+ viewer,
5241
+ C.Cartesian3.fromRadians(cameraPos.longitude, cameraPos.latitude, 0)
5242
+ );
5243
+ initialHeight = terrainHeight + currentDefaultAltitude;
5244
+ console.log("[PathEditing] relativeToGround \u6A21\u5F0F\uFF0C\u5730\u5F62\u9AD8\u5EA6:", terrainHeight, "\u521D\u59CB\u9AD8\u5EA6:", initialHeight);
5245
+ } else if (currentAltitudeMode === "relativeToStart") {
5246
+ initialHeight = currentDefaultAltitude;
5247
+ console.log("[PathEditing] relativeToStart \u6A21\u5F0F\uFF08\u7A7A\u822A\u7EBF\uFF09\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u9AD8\u5EA6:", initialHeight);
5248
+ }
5249
+ cursorStart = C.Cartesian3.fromRadians(
5250
+ cameraPos.longitude,
5251
+ cameraPos.latitude,
5252
+ initialHeight
5253
+ );
5254
+ console.log("[PathEditing] \u7A7A\u822A\u7EBF\uFF0C\u4F7F\u7528\u76F8\u673A\u4F4D\u7F6E\u521D\u59CB\u5316\u6E38\u6807:", {
5255
+ lon: C.Math.toDegrees(cameraPos.longitude),
5256
+ lat: C.Math.toDegrees(cameraPos.latitude),
5257
+ height: initialHeight,
5258
+ altitudeMode: currentAltitudeMode
5259
+ });
5260
+ }
5261
+ } catch (error) {
5262
+ console.warn("[PathEditing] \u65E0\u6CD5\u83B7\u53D6\u76F8\u673A\u4F4D\u7F6E:", error);
5263
+ }
5264
+ }
5129
5265
  let airplaneCursor;
5130
5266
  const insertVertex = (insertAt, p3, poseOrient) => {
5131
5267
  console.log("[PathEditing] \u63D2\u5165\u9876\u70B9\u524D positions \u957F\u5EA6:", positions.length, "insertAt:", insertAt);
@@ -5142,7 +5278,9 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5142
5278
  vertexLabelManager,
5143
5279
  heightMarkers,
5144
5280
  airplaneCursor,
5145
- poseOrient
5281
+ poseOrient,
5282
+ useGlobalHeightFlags
5283
+ // 🆕 传递 useGlobalHeightFlags
5146
5284
  );
5147
5285
  console.log("[PathEditing] \u63D2\u5165\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length, "actualIndex:", actualIndex);
5148
5286
  entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
@@ -5151,16 +5289,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5151
5289
  };
5152
5290
  const deleteVertex = (deleteAt) => {
5153
5291
  try {
5154
- if (deleteAt === 0 || hiddenClimbIndex === 1 && deleteAt === 1) {
5155
- console.warn("Cannot delete start point");
5156
- return;
5157
- }
5158
- if (hiddenClimbIndex !== void 0 && deleteAt === hiddenClimbIndex) {
5159
- console.warn("Cannot delete hidden climb point");
5160
- return;
5161
- }
5162
- const visibleVertexCount = positions.length - (hiddenClimbIndex === 1 ? 1 : 0);
5163
- if (visibleVertexCount <= 1) {
5292
+ if (positions.length <= 1) {
5164
5293
  console.warn("Cannot delete last vertex");
5165
5294
  return;
5166
5295
  }
@@ -5184,6 +5313,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5184
5313
  rolls.splice(deleteAt, 1);
5185
5314
  fovs.splice(deleteAt, 1);
5186
5315
  heightMarkers.splice(deleteAt, 1);
5316
+ useGlobalHeightFlags.splice(deleteAt, 1);
5187
5317
  console.log("[PathEditing] \u5220\u9664\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length);
5188
5318
  entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
5189
5319
  const newEditedIndices = /* @__PURE__ */ new Set();
@@ -5245,9 +5375,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5245
5375
  }
5246
5376
  },
5247
5377
  // frustumLengthFactor: options?.preview?.lengthFactor ?? 0.3,
5248
- fovDeg: options?.preview?.fov ?? 50,
5249
- minHeight: options?.minHeight
5250
- // 传递最小高度限制
5378
+ fovDeg: options?.preview?.fov ?? 50
5251
5379
  });
5252
5380
  if (options?.preview?.enabled !== false && airplaneCursor) {
5253
5381
  preview = new PathPreview(CesiumNS, viewer, {
@@ -5291,8 +5419,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5291
5419
  {
5292
5420
  CesiumNS,
5293
5421
  viewer,
5294
- hiddenClimbIndex,
5295
- minHeight: options?.minHeight
5422
+ hiddenClimbIndex
5296
5423
  },
5297
5424
  {
5298
5425
  onVertexSelect: (index) => setActiveIndex(index),
@@ -5323,7 +5450,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5323
5450
  },
5324
5451
  onDeleteVertex: (index) => {
5325
5452
  const totalBefore = positions.length;
5326
- const displayNumber = hiddenClimbIndex === 1 && index > 1 ? index - 1 : index;
5453
+ const displayNumber = index;
5327
5454
  deleteVertex(index);
5328
5455
  if (options?.onVertexDeleteDetail) {
5329
5456
  try {
@@ -5376,7 +5503,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5376
5503
  };
5377
5504
  } catch {
5378
5505
  }
5379
- const displayNumber = hiddenClimbIndex === 1 && index > 1 ? index - 1 : index;
5506
+ const displayNumber = index;
5380
5507
  const dragMoveInfo = {
5381
5508
  index,
5382
5509
  displayNumber,
@@ -5393,6 +5520,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5393
5520
  onVertexDragEnd: (index, finalPosition) => {
5394
5521
  editedIndices.add(index);
5395
5522
  positions[index] = finalPosition;
5523
+ useGlobalHeightFlags[index] = false;
5396
5524
  if (handles[index]) {
5397
5525
  handleStyleManager.applyStyle(handles[index], "edited");
5398
5526
  }
@@ -5439,10 +5567,29 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5439
5567
  getCursorPose: () => airplaneCursor?.getPose(),
5440
5568
  // 🆕 快速编辑回调
5441
5569
  getQuickEditEnabled: () => quickEditEnabled,
5442
- getQuickEditOptions: () => ({
5443
- climbHeight: quickEditOptions.climbHeight ?? 100,
5444
- altitudeMode: quickEditOptions.altitudeMode ?? "absolute"
5445
- })
5570
+ getQuickEditOptions: () => {
5571
+ let climbHeight = 0;
5572
+ try {
5573
+ const props = entity.properties;
5574
+ climbHeight = props?._climbHeight?.getValue?.() ?? props?._climbHeight ?? 0;
5575
+ } catch {
5576
+ }
5577
+ return {
5578
+ climbHeight,
5579
+ defaultAltitude: currentDefaultAltitude,
5580
+ altitudeMode: currentAltitudeMode
5581
+ };
5582
+ },
5583
+ // 🆕 高度模式相关回调
5584
+ getStartPointAltitude: () => startPointAltitude,
5585
+ getDefaultAltitude: () => currentDefaultAltitude,
5586
+ // 🆕 useGlobalHeightFlags 相关回调
5587
+ getUseGlobalHeightFlags: () => useGlobalHeightFlags,
5588
+ setUseGlobalHeightFlag: (index, value) => {
5589
+ if (index >= 0 && index < useGlobalHeightFlags.length) {
5590
+ useGlobalHeightFlags[index] = value;
5591
+ }
5592
+ }
5446
5593
  }
5447
5594
  );
5448
5595
  const cleanupSession = () => {
@@ -5482,6 +5629,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5482
5629
  const buildVertexDetailInfo = (index) => {
5483
5630
  const position = positions[index];
5484
5631
  let coordinates = { longitude: 0, latitude: 0, height: 0 };
5632
+ let ellipsoidHeight = 0;
5485
5633
  try {
5486
5634
  const cartographic = C.Cartographic.fromCartesian(position);
5487
5635
  coordinates = {
@@ -5489,8 +5637,16 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5489
5637
  latitude: C.Math.toDegrees(cartographic.latitude),
5490
5638
  height: cartographic.height
5491
5639
  };
5640
+ ellipsoidHeight = cartographic.height ?? 0;
5492
5641
  } catch {
5493
5642
  }
5643
+ const terrainHeight = terrainHeightsCache.get(index) ?? 0;
5644
+ const relativeHeight = calculateRelativeHeight(
5645
+ currentAltitudeMode,
5646
+ ellipsoidHeight,
5647
+ startPointAltitude,
5648
+ terrainHeight
5649
+ );
5494
5650
  const pose = {
5495
5651
  heading: headings[index] ?? 0,
5496
5652
  pitch: pitches[index] ?? 0,
@@ -5498,18 +5654,17 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5498
5654
  fov: fovs[index] ?? 50
5499
5655
  };
5500
5656
  const totalVertices = positions.length;
5501
- const totalVisibleVertices = totalVertices - (hiddenClimbIndex === 1 ? 1 : 0);
5502
- const isStartPoint = index === 0 || hiddenClimbIndex === 1 && index === 1;
5503
- const isHiddenClimb = hiddenClimbIndex === 1 && index === 1;
5504
- const canDelete = totalVisibleVertices > 1 && !isStartPoint;
5505
- const displayNumber = hiddenClimbIndex === 1 && index > 1 ? index - 1 : index;
5657
+ const totalVisibleVertices = totalVertices;
5658
+ const isStartPoint = false;
5659
+ const isHiddenClimb = false;
5660
+ const canDelete = totalVertices > 1;
5661
+ const displayNumber = index;
5506
5662
  let pathProperties;
5507
5663
  try {
5508
5664
  const props = entity.properties;
5509
5665
  pathProperties = {
5510
5666
  altitudeMode: props?._altitudeMode?.getValue?.() ?? props?._altitudeMode,
5511
- climbHeight: props?._climbHeight?.getValue?.() ?? props?._climbHeight,
5512
- hasHiddenClimb: props?._hasHiddenClimb?.getValue?.() ?? props?._hasHiddenClimb
5667
+ climbHeight: props?._climbHeight?.getValue?.() ?? props?._climbHeight
5513
5668
  };
5514
5669
  } catch {
5515
5670
  }
@@ -5525,44 +5680,68 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5525
5680
  totalVisibleVertices,
5526
5681
  totalVertices,
5527
5682
  entity,
5528
- pathProperties
5683
+ pathProperties,
5684
+ useGlobalHeight: useGlobalHeightFlags[index] ?? false,
5685
+ // 🆕 添加 useGlobalHeight
5686
+ ellipsoidHeight,
5687
+ // 🆕 添加椭球高度
5688
+ relativeHeight
5689
+ // 🆕 添加相对高度
5529
5690
  };
5530
5691
  };
5531
5692
  return {
5532
5693
  /** 保存并退出编辑模式 */
5533
5694
  saveAndStop: () => {
5534
5695
  stateManager.persistToEntity(positions, headings, pitches, rolls, fovs);
5535
- const startPoint = {
5536
- position: positions[0]
5537
- };
5538
- let altitudeMode;
5696
+ try {
5697
+ entity.properties._altitudeMode = currentAltitudeMode;
5698
+ entity.properties._defaultAltitude = currentDefaultAltitude;
5699
+ entity.properties._useGlobalHeightFlags = useGlobalHeightFlags.slice();
5700
+ } catch {
5701
+ }
5702
+ let altitudeMode = currentAltitudeMode;
5539
5703
  let climbHeight;
5540
- let hasHiddenClimb;
5541
5704
  try {
5542
5705
  const props = entity.properties;
5543
- altitudeMode = props?._altitudeMode?.getValue?.() ?? props?._altitudeMode;
5544
5706
  climbHeight = props?._climbHeight?.getValue?.() ?? props?._climbHeight;
5545
- hasHiddenClimb = props?._hasHiddenClimb?.getValue?.() ?? props?._hasHiddenClimb;
5546
5707
  } catch {
5547
5708
  }
5548
- const waypointData = positions.map((position, index) => ({
5549
- position,
5550
- heading: headings[index] ?? 0,
5551
- pitch: pitches[index] ?? -10,
5552
- roll: rolls[index] ?? 0,
5553
- fov: fovs[index] ?? 50,
5554
- index,
5555
- distance: calculatePathDistance(CesiumNS, positions, index, hiddenClimbIndex)
5556
- })).filter((wp) => wp.index >= 2);
5709
+ const waypointData = positions.map((position, index) => {
5710
+ let ellipsoidHeight = 0;
5711
+ try {
5712
+ const carto = C.Cartographic.fromCartesian(position);
5713
+ ellipsoidHeight = carto.height ?? 0;
5714
+ } catch {
5715
+ }
5716
+ const terrainHeight = terrainHeightsCache.get(index) ?? 0;
5717
+ const relativeHeight = calculateRelativeHeight(
5718
+ currentAltitudeMode,
5719
+ ellipsoidHeight,
5720
+ startPointAltitude,
5721
+ terrainHeight
5722
+ );
5723
+ return {
5724
+ position,
5725
+ heading: headings[index] ?? 0,
5726
+ pitch: pitches[index] ?? -10,
5727
+ roll: rolls[index] ?? 0,
5728
+ fov: fovs[index] ?? 50,
5729
+ index,
5730
+ distance: calculatePathDistance(CesiumNS, positions, index),
5731
+ ellipsoidHeight,
5732
+ relativeHeight,
5733
+ useGlobalHeight: useGlobalHeightFlags[index] ?? false
5734
+ // 🆕 添加 useGlobalHeight
5735
+ };
5736
+ });
5557
5737
  cleanupSession();
5558
5738
  return {
5559
5739
  entity,
5560
5740
  positions: positions.slice(),
5561
- startPoint,
5562
5741
  climbHeight,
5563
5742
  waypointData,
5564
5743
  altitudeMode,
5565
- hasHiddenClimb
5744
+ defaultAltitude: currentDefaultAltitude
5566
5745
  };
5567
5746
  },
5568
5747
  /** 不保存退出(回滚到进入编辑时的状态) */
@@ -5785,45 +5964,32 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5785
5964
  */
5786
5965
  getCursorPose: () => airplaneCursor?.getPose(),
5787
5966
  /**
5788
- * 🆕 动态更新爬升高度(climbHeight)
5789
- * 更新隐藏爬升点(index 1)的高度,实现安全飞行高度的动态调节
5790
- * @param climbHeight 新的爬升高度(米)
5967
+ * 🆕 更新爬升高度(climbHeight)
5968
+ * 更新实体属性中的 _climbHeight 值
5969
+ * @param newClimbHeight 新的爬升高度(米)
5791
5970
  * @returns 是否更新成功
5792
5971
  */
5793
- updateClimbHeight: function(climbHeight) {
5794
- if (hiddenClimbIndex !== 1 || positions.length < 2) {
5795
- console.warn("[updateClimbHeight] No hidden climb point exists");
5796
- return false;
5797
- }
5972
+ updateClimbHeight: function(newClimbHeight) {
5798
5973
  try {
5799
- const startPos = positions[0];
5800
- const startCarto = C.Cartographic.fromCartesian(startPos);
5801
- const newAltitude = startCarto.height + climbHeight;
5802
- const success = this.updateWaypointAltitude(1, newAltitude);
5803
- if (success) {
5804
- try {
5805
- entity.properties._climbHeight = climbHeight;
5806
- } catch {
5807
- }
5808
- console.log("[updateClimbHeight] \u2705 \u722C\u5347\u9AD8\u5EA6\u5DF2\u66F4\u65B0:", climbHeight, "\u7C73");
5809
- }
5810
- return success;
5974
+ entity.properties._climbHeight = newClimbHeight;
5975
+ console.log("[updateClimbHeight] \u2705 \u722C\u5347\u9AD8\u5EA6\u5DF2\u66F4\u65B0:", newClimbHeight, "\u7C73");
5976
+ return true;
5811
5977
  } catch (error) {
5812
5978
  console.error("[updateClimbHeight] Error:", error);
5813
5979
  return false;
5814
5980
  }
5815
5981
  },
5816
5982
  /**
5817
- * 🆕 获取起飞点信息
5818
- * @returns 起飞点的经纬度和高度
5983
+ * 🆕 获取第一个航点信息
5984
+ * @returns 第一个航点的经纬度和高度
5819
5985
  */
5820
5986
  getStartPoint: () => {
5821
5987
  if (positions.length < 1) return null;
5822
5988
  try {
5823
- const startPos = positions[0];
5824
- const carto = C.Cartographic.fromCartesian(startPos);
5989
+ const firstPos = positions[0];
5990
+ const carto = C.Cartographic.fromCartesian(firstPos);
5825
5991
  return {
5826
- position: startPos,
5992
+ position: firstPos,
5827
5993
  latitude: C.Math.toDegrees(carto.latitude),
5828
5994
  longitude: C.Math.toDegrees(carto.longitude),
5829
5995
  altitude: carto.height
@@ -5831,6 +5997,123 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5831
5997
  } catch {
5832
5998
  return null;
5833
5999
  }
6000
+ },
6001
+ /**
6002
+ * 🆕 获取当前高度模式
6003
+ */
6004
+ getAltitudeMode: () => currentAltitudeMode,
6005
+ /**
6006
+ * 🆕 获取当前默认高度
6007
+ */
6008
+ getDefaultAltitude: () => currentDefaultAltitude,
6009
+ /**
6010
+ * 🆕 更新默认高度
6011
+ */
6012
+ updateDefaultAltitude: (altitude) => {
6013
+ currentDefaultAltitude = altitude;
6014
+ quickEditOptions.climbHeight = altitude;
6015
+ try {
6016
+ entity.properties._defaultAltitude = altitude;
6017
+ } catch {
6018
+ }
6019
+ console.log("[updateDefaultAltitude] \u2705 \u9ED8\u8BA4\u9AD8\u5EA6\u5DF2\u66F4\u65B0:", altitude, "\u7C73");
6020
+ },
6021
+ /**
6022
+ * 🆕 切换高度计算模式(异步,会重算所有航点高度)
6023
+ * @param newMode 新的高度模式
6024
+ * @param newDefaultAltitude 新的默认高度(可选)
6025
+ * @param options 选项(terrainProvider, onProgress)
6026
+ * @returns 切换结果 { success: boolean; error?: string }
6027
+ */
6028
+ changeAltitudeMode: async (newMode, newDefaultAltitude, options2) => {
6029
+ try {
6030
+ const onProgress = options2?.onProgress;
6031
+ if (newMode === "relativeToGround") {
6032
+ const terrainProvider = options2?.terrainProvider ?? viewer.terrainProvider;
6033
+ const C_any = C;
6034
+ if (!terrainProvider || C_any.EllipsoidTerrainProvider && terrainProvider instanceof C_any.EllipsoidTerrainProvider) {
6035
+ console.error("[changeAltitudeMode] \u274C relativeToGround \u6A21\u5F0F\u9700\u8981\u6709\u6548\u7684 terrain \u56FE\u5C42");
6036
+ onProgress?.("error");
6037
+ return {
6038
+ success: false,
6039
+ error: "Terrain provider required for relativeToGround mode"
6040
+ };
6041
+ }
6042
+ }
6043
+ onProgress?.("loading");
6044
+ const oldMode = currentAltitudeMode;
6045
+ if (newDefaultAltitude !== void 0) {
6046
+ currentDefaultAltitude = newDefaultAltitude;
6047
+ quickEditOptions.climbHeight = newDefaultAltitude;
6048
+ }
6049
+ if (newMode === "relativeToGround" || oldMode === "relativeToGround") {
6050
+ const terrainHeights = await queryTerrainHeights(CesiumNS, viewer, positions);
6051
+ terrainHeights.forEach((height, index) => {
6052
+ terrainHeightsCache.set(index, height);
6053
+ });
6054
+ console.log("[changeAltitudeMode] \u5DF2\u67E5\u8BE2\u5730\u5F62\u9AD8\u5EA6:", terrainHeights);
6055
+ }
6056
+ if (newMode !== oldMode) {
6057
+ console.log("[changeAltitudeMode] \u5207\u6362\u9AD8\u5EA6\u6A21\u5F0F:", oldMode, "->", newMode);
6058
+ for (let i = 0; i < positions.length; i++) {
6059
+ const currentPos = positions[i];
6060
+ const carto = C.Cartographic.fromCartesian(currentPos);
6061
+ const currentAbsoluteHeight = carto.height ?? 0;
6062
+ const oldTerrainHeight = terrainHeightsCache.get(i) ?? 0;
6063
+ const relativeHeight = calculateRelativeHeight(
6064
+ oldMode,
6065
+ currentAbsoluteHeight,
6066
+ startPointAltitude,
6067
+ oldTerrainHeight
6068
+ );
6069
+ const newTerrainHeight = terrainHeightsCache.get(i) ?? 0;
6070
+ const newAbsoluteHeight = calculateAbsoluteHeight(
6071
+ newMode,
6072
+ relativeHeight,
6073
+ startPointAltitude,
6074
+ newTerrainHeight
6075
+ );
6076
+ const newPosition = C.Cartesian3.fromRadians(
6077
+ carto.longitude,
6078
+ carto.latitude,
6079
+ newAbsoluteHeight
6080
+ );
6081
+ positions[i] = newPosition;
6082
+ if (handles[i]) {
6083
+ try {
6084
+ handles[i].position = newPosition;
6085
+ } catch {
6086
+ }
6087
+ }
6088
+ try {
6089
+ vertexLabelManager.updateLabelPosition(i, newPosition);
6090
+ } catch {
6091
+ }
6092
+ try {
6093
+ createOrUpdateMarkerForIndex(i);
6094
+ } catch {
6095
+ }
6096
+ }
6097
+ currentAltitudeMode = newMode;
6098
+ quickEditOptions.altitudeMode = newMode;
6099
+ try {
6100
+ entity.properties._altitudeMode = newMode;
6101
+ entity.properties._defaultAltitude = currentDefaultAltitude;
6102
+ } catch {
6103
+ }
6104
+ entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
6105
+ }
6106
+ console.log("[changeAltitudeMode] \u2705 \u9AD8\u5EA6\u6A21\u5F0F\u5207\u6362\u5B8C\u6210:", {
6107
+ mode: newMode,
6108
+ defaultAltitude: currentDefaultAltitude
6109
+ });
6110
+ onProgress?.("done");
6111
+ return { success: true };
6112
+ } catch (error) {
6113
+ console.error("[changeAltitudeMode] Error:", error);
6114
+ options2?.onProgress?.("error");
6115
+ return { success: false, error: String(error) };
6116
+ }
5834
6117
  }
5835
6118
  };
5836
6119
  }
@@ -5840,221 +6123,18 @@ function fovToFocalLength(fovDeg) {
5840
6123
  return sensorWidth / (2 * Math.tan(fovRad / 2));
5841
6124
  }
5842
6125
 
5843
- // src/core/path-manager/ArrowShape.ts
5844
- var ArrowShape = class {
5845
- constructor(CesiumNS, opts = {}) {
5846
- this.CesiumNS = CesiumNS;
5847
- __publicField(this, "height");
5848
- __publicField(this, "shaftHeight");
5849
- __publicField(this, "headHeight");
5850
- __publicField(this, "shaftRadius");
5851
- __publicField(this, "headRadius");
5852
- __publicField(this, "baseRadius");
5853
- __publicField(this, "color");
5854
- __publicField(this, "shaftAlpha");
5855
- __publicField(this, "headAlpha");
5856
- __publicField(this, "baseAlpha");
5857
- __publicField(this, "outline");
5858
- __publicField(this, "outlineColor");
5859
- // 动态缩放相关
5860
- __publicField(this, "dynamicScale");
5861
- __publicField(this, "viewer");
5862
- __publicField(this, "referenceDistance");
5863
- __publicField(this, "minScale");
5864
- __publicField(this, "maxScale");
5865
- __publicField(this, "cachedScaleFactor", 1);
5866
- const C = this.CesiumNS;
5867
- this.shaftHeight = opts.shaftHeight ?? 17;
5868
- this.headHeight = opts.headHeight ?? 8;
5869
- this.height = opts.height ?? this.shaftHeight + this.headHeight;
5870
- this.shaftRadius = opts.shaftRadius ?? 3;
5871
- this.headRadius = opts.headRadius ?? 6;
5872
- this.baseRadius = opts.baseRadius ?? 8;
5873
- this.color = opts.color ? C.Color.fromCssColorString(opts.color) : C.Color.fromCssColorString("#FFD700");
5874
- this.shaftAlpha = opts.shaftAlpha ?? 1;
5875
- this.headAlpha = opts.headAlpha ?? 1;
5876
- this.baseAlpha = opts.baseAlpha ?? 0.65;
5877
- this.outline = opts.outline ?? false;
5878
- this.outlineColor = opts.outlineColor ? C.Color.fromCssColorString(opts.outlineColor) : this.color.darken(0.3, new C.Color());
5879
- this.dynamicScale = opts.dynamicScale ?? false;
5880
- this.viewer = opts.viewer;
5881
- this.referenceDistance = opts.referenceDistance ?? 500;
5882
- this.minScale = opts.minScale ?? 0.3;
5883
- this.maxScale = opts.maxScale ?? 3;
5884
- }
5885
- /**
5886
- * Create arrow entities at given position
5887
- * Returns array of 3 entities: [shaft, head, base]
5888
- *
5889
- * @param layer - Cesium CustomDataSource to add entities to
5890
- * @param position - Ground position (Cartesian3)
5891
- * @param baseHeight - Ground height in meters
5892
- * @param properties - Additional properties for entities
5893
- * @returns Array of created entities [shaft, head, base]
5894
- */
5895
- createEntities(layer, position, baseHeight, properties) {
5896
- const C = this.CesiumNS;
5897
- const entities = [];
5898
- const carto = C.Cartographic.fromCartesian(position);
5899
- const lon = C.Math.toDegrees(carto.longitude);
5900
- const lat = C.Math.toDegrees(carto.latitude);
5901
- const computeScaleFactor = () => {
5902
- if (!this.dynamicScale || !this.viewer) return 1;
5903
- try {
5904
- const cameraPos = this.viewer.camera.positionWC;
5905
- const distance = C.Cartesian3.distance(cameraPos, position);
5906
- const rawScale = distance / this.referenceDistance;
5907
- return Math.max(this.minScale, Math.min(this.maxScale, rawScale));
5908
- } catch {
5909
- return 1;
5910
- }
5911
- };
5912
- if (this.dynamicScale && this.viewer) {
5913
- this.viewer.scene.preRender.addEventListener(() => {
5914
- this.cachedScaleFactor = computeScaleFactor();
5915
- });
5916
- }
5917
- const hpr = new C.HeadingPitchRoll(0, 0, 0);
5918
- const baseOrientation = C.Transforms.headingPitchRollQuaternion(position, hpr);
5919
- const fixedOrientation = new C.ConstantProperty(baseOrientation);
5920
- const getShaftPosition = () => {
5921
- const scale = this.cachedScaleFactor;
5922
- return C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight * scale / 2);
5923
- };
5924
- const getHeadPosition = () => {
5925
- const scale = this.cachedScaleFactor;
5926
- return C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight * scale + this.headHeight * scale / 2);
5927
- };
5928
- const shaftEntity = layer.entities.add({
5929
- position: this.dynamicScale ? new C.CallbackProperty(getShaftPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight / 2)),
5930
- orientation: fixedOrientation,
5931
- cylinder: {
5932
- length: this.dynamicScale ? new C.CallbackProperty(() => this.shaftHeight * this.cachedScaleFactor, false) : this.shaftHeight,
5933
- topRadius: this.dynamicScale ? new C.CallbackProperty(() => this.shaftRadius * this.cachedScaleFactor, false) : this.shaftRadius,
5934
- bottomRadius: this.dynamicScale ? new C.CallbackProperty(() => this.shaftRadius * this.cachedScaleFactor, false) : this.shaftRadius,
5935
- material: this.color.withAlpha(this.shaftAlpha),
5936
- outline: this.outline,
5937
- outlineColor: this.outlineColor,
5938
- outlineWidth: 2
5939
- },
5940
- properties: {
5941
- ...properties,
5942
- _arrowPart: "shaft"
5943
- }
5944
- });
5945
- entities.push(shaftEntity);
5946
- const headEntity = layer.entities.add({
5947
- position: this.dynamicScale ? new C.CallbackProperty(getHeadPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight + this.headHeight / 2)),
5948
- orientation: fixedOrientation,
5949
- cylinder: {
5950
- length: this.dynamicScale ? new C.CallbackProperty(() => this.headHeight * this.cachedScaleFactor, false) : this.headHeight,
5951
- topRadius: 0,
5952
- // Creates cone shape
5953
- bottomRadius: this.dynamicScale ? new C.CallbackProperty(() => this.headRadius * this.cachedScaleFactor, false) : this.headRadius,
5954
- material: this.color.withAlpha(this.headAlpha),
5955
- outline: this.outline,
5956
- outlineColor: this.outlineColor,
5957
- outlineWidth: 2
5958
- },
5959
- properties: {
5960
- ...properties,
5961
- _arrowPart: "head"
5962
- }
5963
- });
5964
- entities.push(headEntity);
5965
- const baseEntity = layer.entities.add({
5966
- position,
5967
- ellipse: {
5968
- semiMajorAxis: this.dynamicScale ? new C.CallbackProperty(() => this.baseRadius * this.cachedScaleFactor, false) : this.baseRadius,
5969
- semiMinorAxis: this.dynamicScale ? new C.CallbackProperty(() => this.baseRadius * this.cachedScaleFactor, false) : this.baseRadius,
5970
- height: baseHeight,
5971
- material: this.color.withAlpha(this.baseAlpha),
5972
- outline: this.outline,
5973
- outlineColor: this.outlineColor,
5974
- outlineWidth: 2
5975
- },
5976
- properties: {
5977
- ...properties,
5978
- _arrowPart: "base"
5979
- }
5980
- });
5981
- entities.push(baseEntity);
5982
- return entities;
5983
- }
5984
- /**
5985
- * Get the color
5986
- */
5987
- getColor() {
5988
- return this.color;
5989
- }
5990
- /**
5991
- * Update arrow color
5992
- */
5993
- setColor(color) {
5994
- const C = this.CesiumNS;
5995
- this.color = C.Color.fromCssColorString(color);
5996
- }
5997
- /**
5998
- * Update dimensions
5999
- */
6000
- setDimensions(opts) {
6001
- if (opts.shaftHeight !== void 0) this.shaftHeight = opts.shaftHeight;
6002
- if (opts.headHeight !== void 0) this.headHeight = opts.headHeight;
6003
- if (opts.shaftRadius !== void 0) this.shaftRadius = opts.shaftRadius;
6004
- if (opts.headRadius !== void 0) this.headRadius = opts.headRadius;
6005
- if (opts.baseRadius !== void 0) this.baseRadius = opts.baseRadius;
6006
- this.height = this.shaftHeight + this.headHeight;
6007
- }
6008
- /**
6009
- * Update transparency
6010
- */
6011
- setAlpha(opts) {
6012
- if (opts.shaftAlpha !== void 0) this.shaftAlpha = opts.shaftAlpha;
6013
- if (opts.headAlpha !== void 0) this.headAlpha = opts.headAlpha;
6014
- if (opts.baseAlpha !== void 0) this.baseAlpha = opts.baseAlpha;
6015
- }
6016
- /**
6017
- * Get current dimensions
6018
- */
6019
- getDimensions() {
6020
- return {
6021
- height: this.height,
6022
- shaftHeight: this.shaftHeight,
6023
- headHeight: this.headHeight,
6024
- shaftRadius: this.shaftRadius,
6025
- headRadius: this.headRadius,
6026
- baseRadius: this.baseRadius
6027
- };
6028
- }
6029
- /**
6030
- * Get current alpha values
6031
- */
6032
- getAlpha() {
6033
- return {
6034
- shaftAlpha: this.shaftAlpha,
6035
- headAlpha: this.headAlpha,
6036
- baseAlpha: this.baseAlpha
6037
- };
6038
- }
6039
- /**
6040
- * Get total height
6041
- */
6042
- getHeight() {
6043
- return this.height;
6044
- }
6045
- };
6046
-
6047
6126
  // src/core/path-manager/startPathDrawing.ts
6048
6127
  function startPathDrawing(CesiumNS, viewer, options, onComplete) {
6049
6128
  const C = CesiumNS;
6050
6129
  const layer = options?.layer ?? new C.CustomDataSource("Paths");
6051
6130
  if (!options?.layer) viewer.dataSources.add(layer);
6052
6131
  const altitudeMode = options?.altitudeMode ?? "absolute";
6053
- const climbHeight = Number(options?.climbHeight) || 100;
6054
- const DEFAULT_MAIN_WIDTH = options?.width ?? 6;
6055
- let startCartographic;
6056
- let hasStart = false;
6132
+ const climbHeight = Number(options?.climbHeight) || 0;
6133
+ const defaultAltitude = options?.defaultAltitude ?? options?.defaultHeight ?? 100;
6134
+ const DEFAULT_MAIN_WIDTH = options?.width ?? 3;
6057
6135
  let createdEntity = void 0;
6136
+ let entityCreated = false;
6137
+ let editSession = void 0;
6058
6138
  const getPositionFromMouse = (movement) => {
6059
6139
  const scene = viewer.scene;
6060
6140
  const winPos = movement?.position ?? movement?.endPosition ?? movement;
@@ -6070,129 +6150,54 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
6070
6150
  }
6071
6151
  return cart;
6072
6152
  };
6073
- const toCartographic = (cart) => {
6074
- try {
6075
- return C.Cartographic.fromCartesian(cart);
6076
- } catch {
6077
- return void 0;
6078
- }
6079
- };
6080
6153
  let handler;
6081
- const positions = [];
6082
- const tempHandles = [];
6083
6154
  handler = new C.ScreenSpaceEventHandler(viewer.scene.canvas);
6084
6155
  handler.setInputAction((movement) => {
6085
- if (hasStart) return;
6086
6156
  const picked = getPositionFromMouse(movement);
6087
6157
  if (!picked) return;
6088
- const carto = toCartographic(picked);
6089
- if (!carto) return;
6090
- startCartographic = new C.Cartographic(carto.longitude, carto.latitude, carto.height);
6091
- const groundPos = C.Cartesian3.fromDegrees(
6092
- C.Math.toDegrees(carto.longitude),
6093
- C.Math.toDegrees(carto.latitude),
6094
- startCartographic.height
6095
- );
6096
- positions.push(groundPos);
6097
- const arrowCarto = toCartographic(groundPos);
6098
- const baseHeight = arrowCarto ? arrowCarto.height : 0;
6099
- const arrowShape = new ArrowShape(CesiumNS, {
6100
- shaftHeight: 12,
6101
- headHeight: 12,
6102
- shaftRadius: 4,
6103
- headRadius: 8,
6104
- baseRadius: 12,
6105
- color: "#FFD700",
6106
- shaftAlpha: 1,
6107
- headAlpha: 1,
6108
- baseAlpha: 0.5,
6109
- // 启用动态缩放,和无人机模型一样根据相机距离调整大小
6110
- dynamicScale: true,
6111
- viewer,
6112
- referenceDistance: 300,
6113
- minScale: 0.3,
6114
- maxScale: 2
6115
- });
6116
- const arrowEntities = arrowShape.createEntities(
6117
- layer,
6118
- groundPos,
6119
- baseHeight,
6120
- { _type: "path-vertex", _vertexIndex: 0, _isStart: true }
6121
- );
6122
- tempHandles.push(...arrowEntities);
6123
- hasStart = true;
6124
- if (climbHeight > 0) {
6125
- const climbPos = C.Cartesian3.fromDegrees(
6126
- C.Math.toDegrees(startCartographic.longitude),
6127
- C.Math.toDegrees(startCartographic.latitude),
6128
- startCartographic.height + climbHeight
6129
- );
6130
- positions.push(climbPos);
6131
- }
6132
- let created;
6133
- try {
6134
- const id = `${options?.idPrefix ?? "path"}-${Date.now().toString(36)}`;
6135
- const pathPositions = new C.CallbackProperty(() => {
6136
- const p = created?.polyline?.positions;
6137
- if (p && typeof p.getValue === "function") {
6138
- try {
6139
- return p.getValue();
6140
- } catch {
6141
- }
6142
- }
6143
- return positions.slice();
6144
- }, false);
6145
- created = layer.entities.add({
6146
- id,
6147
- name: "Path",
6148
- polyline: {
6149
- positions: pathPositions,
6150
- width: DEFAULT_MAIN_WIDTH,
6151
- material: new C.PolylineOutlineMaterialProperty({
6152
- color: C.Color.LIME,
6153
- outlineColor: C.Color.WHITE,
6154
- outlineWidth: 2
6155
- }),
6156
- clampToGround: false
6157
- },
6158
- properties: {
6159
- _altitudeMode: altitudeMode,
6160
- _climbHeight: climbHeight,
6161
- _hasHiddenClimb: climbHeight > 0
6162
- }
6163
- });
6164
- createdEntity = created;
6158
+ if (!entityCreated) {
6159
+ entityCreated = true;
6160
+ let created;
6165
6161
  try {
6166
- tempHandles.forEach((hh, idx) => {
6167
- try {
6168
- hh.properties = {
6169
- ...hh.properties,
6170
- _type: "path-vertex",
6171
- _ownerId: id,
6172
- _vertexIndex: idx,
6173
- _isStart: idx === 0
6174
- };
6175
- } catch {
6162
+ const id = `${options?.idPrefix ?? "path"}-${Date.now().toString(36)}`;
6163
+ const positions = [];
6164
+ const pathPositions = new C.CallbackProperty(() => positions.slice(), false);
6165
+ created = layer.entities.add({
6166
+ id,
6167
+ name: "Path",
6168
+ polyline: {
6169
+ positions: pathPositions,
6170
+ width: DEFAULT_MAIN_WIDTH,
6171
+ material: C.Color.fromCssColorString("#00E676"),
6172
+ clampToGround: false
6173
+ },
6174
+ properties: {
6175
+ _altitudeMode: altitudeMode,
6176
+ _defaultAltitude: defaultAltitude,
6177
+ _climbHeight: climbHeight,
6178
+ _useGlobalHeightFlags: []
6179
+ // 🆕 空数组,航点由编辑模式添加
6176
6180
  }
6177
6181
  });
6178
- } catch {
6179
- }
6180
- } finally {
6181
- try {
6182
- handler?.destroy();
6183
- } catch {
6184
- }
6185
- handler = void 0;
6186
- }
6187
- if (created) {
6188
- try {
6189
- onComplete?.(created);
6190
- } catch {
6182
+ createdEntity = created;
6183
+ } finally {
6184
+ try {
6185
+ handler?.destroy();
6186
+ } catch {
6187
+ }
6188
+ handler = void 0;
6191
6189
  }
6192
- try {
6193
- const auto = options?.autoStartEditing;
6194
- if (auto) {
6195
- const editOptions = {};
6190
+ if (created) {
6191
+ try {
6192
+ onComplete?.(created);
6193
+ } catch {
6194
+ }
6195
+ try {
6196
+ const auto = options?.autoStartEditing;
6197
+ const editOptions = {
6198
+ // 🔧 默认使用自由编辑模式(快速编辑和自由编辑互斥)
6199
+ quickEdit: false
6200
+ };
6196
6201
  if (typeof auto === "object" && auto.preview) {
6197
6202
  editOptions.preview = auto.preview;
6198
6203
  } else {
@@ -6214,30 +6219,35 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
6214
6219
  if (autoOpts.onVertexDeleteDetail) {
6215
6220
  editOptions.onVertexDeleteDetail = autoOpts.onVertexDeleteDetail;
6216
6221
  }
6217
- const editSession = startPathEditing(CesiumNS, viewer, created, editOptions);
6222
+ editOptions.altitudeMode = altitudeMode;
6223
+ editOptions.defaultAltitude = defaultAltitude;
6224
+ editOptions.climbHeight = climbHeight;
6225
+ editSession = startPathEditing(CesiumNS, viewer, created, editOptions);
6226
+ 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");
6218
6227
  if (editSession && options?.onEditingStarted) {
6219
6228
  try {
6220
6229
  options.onEditingStarted(editSession);
6221
6230
  } catch {
6222
6231
  }
6223
6232
  }
6233
+ } catch (error) {
6234
+ console.error("[startPathDrawing] \u8FDB\u5165\u7F16\u8F91\u6A21\u5F0F\u5931\u8D25:", error);
6224
6235
  }
6225
- } catch {
6226
6236
  }
6227
6237
  }
6228
6238
  }, C.ScreenSpaceEventType.LEFT_CLICK);
6229
6239
  return {
6230
6240
  stop: () => {
6231
- for (const h2 of tempHandles) {
6232
- try {
6233
- layer.entities.remove(h2);
6234
- } catch {
6235
- }
6236
- }
6237
6241
  try {
6238
6242
  handler?.destroy();
6239
6243
  } catch {
6240
6244
  }
6245
+ if (editSession) {
6246
+ try {
6247
+ editSession.stop();
6248
+ } catch {
6249
+ }
6250
+ }
6241
6251
  if (createdEntity) {
6242
6252
  try {
6243
6253
  layer.entities.remove(createdEntity);
@@ -6322,77 +6332,6 @@ function parseCoordinate(value) {
6322
6332
  }
6323
6333
  return 0;
6324
6334
  }
6325
- function parseTakeOffRefPoint(takeOffRefPoint, takeOffSecurityHeight, CesiumNS) {
6326
- if (!takeOffRefPoint || typeof takeOffRefPoint !== "string" || takeOffRefPoint.trim() === "") {
6327
- return null;
6328
- }
6329
- const C = CesiumNS;
6330
- let longitude;
6331
- let latitude;
6332
- let height;
6333
- const trimmed = takeOffRefPoint.trim();
6334
- if (trimmed.includes(",")) {
6335
- try {
6336
- const parts = trimmed.split(",").map((p) => p.trim());
6337
- if (parts.length >= 2) {
6338
- latitude = parseFloat(parts[0]);
6339
- longitude = parseFloat(parts[1]);
6340
- height = parts.length >= 3 ? parseFloat(parts[2]) : takeOffSecurityHeight;
6341
- if (isNaN(latitude) || isNaN(longitude) || isNaN(height)) {
6342
- throw new Error("\u65E0\u6CD5\u89E3\u6790\u9017\u53F7\u5206\u9694\u7684\u5750\u6807\u503C");
6343
- }
6344
- const startPosition = C.Cartesian3.fromDegrees(longitude, latitude, height);
6345
- const climbPosition = C.Cartesian3.fromDegrees(
6346
- longitude,
6347
- latitude,
6348
- height + takeOffSecurityHeight
6349
- );
6350
- return {
6351
- startPosition,
6352
- climbPosition
6353
- };
6354
- }
6355
- } catch (error) {
6356
- }
6357
- }
6358
- try {
6359
- const parsed = JSON.parse(takeOffRefPoint);
6360
- if (typeof parsed !== "object" || parsed === null) {
6361
- return null;
6362
- }
6363
- if (typeof parsed.longitude === "number") {
6364
- longitude = parsed.longitude;
6365
- } else if (typeof parsed.lon === "number") {
6366
- longitude = parsed.lon;
6367
- } else {
6368
- return null;
6369
- }
6370
- if (typeof parsed.latitude === "number") {
6371
- latitude = parsed.latitude;
6372
- } else if (typeof parsed.lat === "number") {
6373
- latitude = parsed.lat;
6374
- } else {
6375
- return null;
6376
- }
6377
- if (typeof parsed.height === "number") {
6378
- height = parsed.height;
6379
- } else {
6380
- height = takeOffSecurityHeight;
6381
- }
6382
- const startPosition = C.Cartesian3.fromDegrees(longitude, latitude, height);
6383
- const climbPosition = C.Cartesian3.fromDegrees(
6384
- longitude,
6385
- latitude,
6386
- height + takeOffSecurityHeight
6387
- );
6388
- return {
6389
- startPosition,
6390
- climbPosition
6391
- };
6392
- } catch (error) {
6393
- return null;
6394
- }
6395
- }
6396
6335
  function convertSinoflyWayline(data, options) {
6397
6336
  const {
6398
6337
  CesiumNS,
@@ -6415,11 +6354,6 @@ function convertSinoflyWayline(data, options) {
6415
6354
  if (data.waylineId === void 0 || data.waylineId === null) {
6416
6355
  throw new Error("[sinoflyAdapter] waylineId \u4E0D\u80FD\u4E3A\u7A7A");
6417
6356
  }
6418
- const takeOffInfo = parseTakeOffRefPoint(
6419
- data.takeOffRefPoint,
6420
- data.takeOffSecurityHeight,
6421
- CesiumNS
6422
- );
6423
6357
  let waypoints = data.waypointInfo;
6424
6358
  if (filterWaypoint) {
6425
6359
  waypoints = waypoints.filter(filterWaypoint);
@@ -6427,31 +6361,6 @@ function convertSinoflyWayline(data, options) {
6427
6361
  if (sortByIndex) {
6428
6362
  waypoints = [...waypoints].sort((a, b) => a.index - b.index);
6429
6363
  }
6430
- const finalWaypointData = [];
6431
- if (takeOffInfo) {
6432
- finalWaypointData.push({
6433
- position: takeOffInfo.startPosition,
6434
- heading: defaultHeading,
6435
- pitch: defaultPitch,
6436
- roll: defaultRoll,
6437
- fov: defaultFov,
6438
- index: 0
6439
- });
6440
- finalWaypointData.push({
6441
- position: takeOffInfo.climbPosition,
6442
- heading: defaultHeading,
6443
- pitch: defaultPitch,
6444
- roll: defaultRoll,
6445
- fov: defaultFov,
6446
- index: 1
6447
- });
6448
- }
6449
- let waypointStartIndex;
6450
- if (takeOffInfo) {
6451
- waypointStartIndex = 2;
6452
- } else {
6453
- waypointStartIndex = 0;
6454
- }
6455
6364
  const convertedWaypoints = waypoints.map((wp, idx) => {
6456
6365
  try {
6457
6366
  if (!wp) {
@@ -6487,15 +6396,16 @@ function convertSinoflyWayline(data, options) {
6487
6396
  pitch,
6488
6397
  roll,
6489
6398
  fov,
6490
- index: waypointStartIndex + idx
6491
- // 重新分配 index,从 waypointStartIndex 开始
6399
+ index: idx,
6400
+ // 0 开始分配 index
6401
+ useGlobalHeight: false
6402
+ // 🆕 从服务端加载的航点默认使用实际高度
6492
6403
  };
6493
6404
  } catch (error) {
6494
6405
  throw new Error(`\u822A\u70B9 ${idx} \u8F6C\u6362\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
6495
6406
  }
6496
6407
  });
6497
- const waypointData = [...finalWaypointData, ...convertedWaypoints];
6498
- if (!Array.isArray(waypointData)) {
6408
+ if (!Array.isArray(convertedWaypoints)) {
6499
6409
  throw new Error("[sinoflyAdapter] waypointData \u4E0D\u662F\u6570\u7EC4");
6500
6410
  }
6501
6411
  const metadata = {
@@ -6506,8 +6416,8 @@ function convertSinoflyWayline(data, options) {
6506
6416
  autoFlightSpeed: data.autoFlightSpeed,
6507
6417
  distance: data.distance,
6508
6418
  duration: data.duration,
6509
- takeOffSecurityHeight: data.takeOffSecurityHeight,
6510
- hasTakeOffRefPoint: !!takeOffInfo
6419
+ climbHeight: data.takeOffSecurityHeight
6420
+ // 使用 climbHeight 作为字段名
6511
6421
  };
6512
6422
  if (data.stationId !== void 0) metadata.stationId = data.stationId;
6513
6423
  if (data.subarrayId !== void 0) metadata.subarrayId = data.subarrayId;
@@ -6517,7 +6427,7 @@ function convertSinoflyWayline(data, options) {
6517
6427
  id: `wayline-${data.waylineId}`,
6518
6428
  name: data.waylineName || `\u822A\u7EBF-${data.waylineId}`,
6519
6429
  description: `\u822A\u7EBFID: ${data.waylineId}, \u7C7B\u578B: ${data.waylineType}, \u822A\u70B9\u6570: ${data.waypointNumber}`,
6520
- waypointData,
6430
+ waypointData: convertedWaypoints,
6521
6431
  metadata
6522
6432
  };
6523
6433
  }
@@ -6530,7 +6440,6 @@ function renderFlightPath(CesiumNS, viewer, options) {
6530
6440
  let entityId;
6531
6441
  let entityName;
6532
6442
  let entityDescription;
6533
- let hasHiddenClimb;
6534
6443
  let altitudeMode;
6535
6444
  let climbHeight;
6536
6445
  try {
@@ -6546,12 +6455,8 @@ function renderFlightPath(CesiumNS, viewer, options) {
6546
6455
  entityId = options.id ?? converted.id ?? `wayline-${sinoflyData.waylineId}`;
6547
6456
  entityName = options.name ?? converted.name;
6548
6457
  entityDescription = options.description ?? converted.description;
6549
- const sortedWaypoints2 = [...waypointData].sort((a, b) => a.index - b.index);
6550
- const hasIndex1 = sortedWaypoints2.some((wp) => wp.index === 1);
6551
- const hasTakeOffRefPoint = converted.metadata?.hasTakeOffRefPoint ?? !!sinoflyData.takeOffRefPoint;
6552
- hasHiddenClimb = options.hasHiddenClimb ?? (hasTakeOffRefPoint && hasIndex1);
6553
6458
  altitudeMode = options.altitudeMode ?? converted.metadata?.altitudeMode;
6554
- climbHeight = options.climbHeight ?? converted.metadata?.climbHeight ?? converted.metadata?.takeOffSecurityHeight;
6459
+ climbHeight = options.climbHeight ?? converted.metadata?.climbHeight;
6555
6460
  } catch (error) {
6556
6461
  console.error("[renderFlightPath] Sinofly \u6570\u636E\u8F6C\u6362\u5931\u8D25:", error);
6557
6462
  throw new Error(`Sinofly \u6570\u636E\u8F6C\u6362\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
@@ -6573,66 +6478,25 @@ function renderFlightPath(CesiumNS, viewer, options) {
6573
6478
  layer = new C.CustomDataSource(`flight-path-datasource-${Date.now()}`);
6574
6479
  viewer.dataSources.add(layer);
6575
6480
  }
6576
- const hiddenClimbIndex = hasHiddenClimb && positions.length >= 2 ? 1 : void 0;
6577
- const hasStartPoint = positions.length > 0 && sortedWaypoints.some((wp) => wp.index === 0);
6481
+ const hiddenClimbIndex = void 0;
6482
+ const useGlobalHeightFlags = sortedWaypoints.map((wp) => wp.useGlobalHeight ?? false);
6578
6483
  const entity = layer.entities.add({
6579
6484
  id: entityId,
6580
6485
  name: entityName,
6581
6486
  description: entityDescription,
6582
6487
  polyline: {
6583
6488
  positions: positions.slice(),
6584
- width: options.style?.width ?? 8,
6585
- material: options.style?.material ?? options.style?.color ?? new C.PolylineOutlineMaterialProperty({
6586
- color: C.Color.GREEN,
6587
- outlineColor: C.Color.WHITE,
6588
- outlineWidth: 3
6589
- }),
6489
+ width: options.style?.width ?? 3,
6490
+ material: options.style?.material ?? options.style?.color ?? C.Color.fromCssColorString("#00E676"),
6590
6491
  clampToGround: false
6591
6492
  },
6592
6493
  properties: {
6593
6494
  _altitudeMode: altitudeMode,
6594
6495
  _climbHeight: climbHeight,
6595
- _hasHiddenClimb: hasHiddenClimb
6496
+ _useGlobalHeightFlags: useGlobalHeightFlags
6497
+ // 🆕 保存 useGlobalHeightFlags
6596
6498
  }
6597
6499
  });
6598
- const shouldShowArrow = hasStartPoint && hasHiddenClimb;
6599
- if (shouldShowArrow) {
6600
- try {
6601
- const startPosition = positions[0];
6602
- const carto = C.Cartographic.fromCartesian(startPosition);
6603
- const baseHeight = carto ? carto.height : 0;
6604
- const arrowShape = new ArrowShape(CesiumNS, {
6605
- shaftHeight: 12,
6606
- headHeight: 12,
6607
- shaftRadius: 4,
6608
- headRadius: 8,
6609
- baseRadius: 12,
6610
- color: "#FFD700",
6611
- shaftAlpha: 0.98,
6612
- headAlpha: 0.98,
6613
- baseAlpha: 0.65,
6614
- // 启用动态缩放
6615
- dynamicScale: true,
6616
- viewer,
6617
- referenceDistance: 300,
6618
- minScale: 0.3,
6619
- maxScale: 2
6620
- });
6621
- arrowShape.createEntities(
6622
- layer,
6623
- startPosition,
6624
- baseHeight,
6625
- {
6626
- _type: "path-vertex",
6627
- _ownerId: entityId,
6628
- _vertexIndex: 0,
6629
- _isStart: true
6630
- }
6631
- );
6632
- } catch (error) {
6633
- console.error("Failed to create start arrow:", error);
6634
- }
6635
- }
6636
6500
  try {
6637
6501
  const vertexLabelManager = new VertexLabelManager(
6638
6502
  CesiumNS,
@@ -6673,8 +6537,6 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6673
6537
  } catch (e) {
6674
6538
  console.warn("[renderFlightPathPreview] \u65E0\u6CD5\u83B7\u53D6\u822A\u70B9\u6570\u636E:", e);
6675
6539
  }
6676
- const hasHiddenClimb = entity.properties?._hasHiddenClimb?.getValue?.() ?? false;
6677
- const hiddenClimbIndex = hasHiddenClimb ? 1 : void 0;
6678
6540
  const updateWaypointHighlight = (newIndex) => {
6679
6541
  if (!vertexLabelManager || positions.length === 0) return;
6680
6542
  const editedIndices = /* @__PURE__ */ new Set();
@@ -6694,14 +6556,7 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6694
6556
  const ownerId = properties._ownerId?.getValue?.(C.JulianDate.now());
6695
6557
  const vertexIndex = properties._vertexIndex?.getValue?.(C.JulianDate.now());
6696
6558
  if (type === "vertex-label" && ownerId === entity.id && vertexIndex !== void 0) {
6697
- let displayIndex = vertexIndex;
6698
- if (hiddenClimbIndex === 1) {
6699
- if (vertexIndex >= 2) {
6700
- displayIndex = vertexIndex - 2;
6701
- }
6702
- } else {
6703
- displayIndex = vertexIndex;
6704
- }
6559
+ const displayIndex = vertexIndex;
6705
6560
  selectedWaypointIndex = vertexIndex;
6706
6561
  updateWaypointHighlight(selectedWaypointIndex);
6707
6562
  if (options.onWaypointClick) {
@@ -6719,18 +6574,10 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6719
6574
  updateWaypointHighlight(null);
6720
6575
  return;
6721
6576
  }
6722
- let actualIndex = index;
6723
- if (hiddenClimbIndex === 1) {
6724
- actualIndex = index + 2;
6725
- }
6726
- selectedWaypointIndex = actualIndex;
6577
+ selectedWaypointIndex = index;
6727
6578
  updateWaypointHighlight(selectedWaypointIndex);
6728
6579
  },
6729
6580
  getSelectedWaypoint: () => {
6730
- if (selectedWaypointIndex === null) return null;
6731
- if (hiddenClimbIndex === 1) {
6732
- return selectedWaypointIndex >= 2 ? selectedWaypointIndex - 2 : null;
6733
- }
6734
6581
  return selectedWaypointIndex;
6735
6582
  },
6736
6583
  destroy: () => {
@@ -6740,6 +6587,299 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6740
6587
  return { entity, controller };
6741
6588
  }
6742
6589
 
6590
+ // src/core/path-manager/FlightSimulator.ts
6591
+ var FlightSimulator = class {
6592
+ constructor(CesiumNS, viewer, options) {
6593
+ __publicField(this, "CesiumNS");
6594
+ __publicField(this, "viewer");
6595
+ __publicField(this, "options");
6596
+ __publicField(this, "state", "idle");
6597
+ __publicField(this, "progress", 0);
6598
+ __publicField(this, "waypoints", []);
6599
+ __publicField(this, "totalDistance", 0);
6600
+ __publicField(this, "currentDistance", 0);
6601
+ __publicField(this, "airplaneCursor");
6602
+ __publicField(this, "trackEntity");
6603
+ __publicField(this, "passedTrackEntity");
6604
+ __publicField(this, "animationFrameId");
6605
+ __publicField(this, "lastTimestamp");
6606
+ __publicField(this, "onStateChange", new Emitter());
6607
+ /**
6608
+ * 动画循环
6609
+ */
6610
+ __publicField(this, "animate", (timestamp) => {
6611
+ if (this.state !== "playing") return;
6612
+ if (this.lastTimestamp === void 0) {
6613
+ this.lastTimestamp = timestamp;
6614
+ }
6615
+ const deltaTime = (timestamp - this.lastTimestamp) / 1e3;
6616
+ this.lastTimestamp = timestamp;
6617
+ this.currentDistance += this.options.speed * deltaTime;
6618
+ if (this.currentDistance >= this.totalDistance) {
6619
+ if (this.options.loop) {
6620
+ this.currentDistance = 0;
6621
+ } else {
6622
+ this.currentDistance = this.totalDistance;
6623
+ this.setState("stopped");
6624
+ return;
6625
+ }
6626
+ }
6627
+ this.progress = this.totalDistance > 0 ? this.currentDistance / this.totalDistance : 0;
6628
+ if (this.airplaneCursor) {
6629
+ const position = this.getCurrentPosition();
6630
+ const { heading, pitch, roll } = this.getCurrentPose();
6631
+ this.airplaneCursor.setPose(position, heading, pitch, roll);
6632
+ }
6633
+ this.viewer.scene.requestRender();
6634
+ this.animationFrameId = requestAnimationFrame(this.animate);
6635
+ });
6636
+ this.CesiumNS = CesiumNS;
6637
+ this.viewer = viewer;
6638
+ this.options = {
6639
+ waylineData: options.waylineData,
6640
+ speed: options.speed ?? 10,
6641
+ loop: options.loop ?? false,
6642
+ showFrustum: options.showFrustum ?? true,
6643
+ layer: options.layer,
6644
+ trackHighlight: options.trackHighlight
6645
+ };
6646
+ this.initWaypoints();
6647
+ this.createEntities();
6648
+ }
6649
+ /**
6650
+ * 初始化航点数据
6651
+ */
6652
+ initWaypoints() {
6653
+ const C = this.CesiumNS;
6654
+ const converted = convertSinoflyWayline(this.options.waylineData, { CesiumNS: this.CesiumNS });
6655
+ if (!converted.waypointData || converted.waypointData.length < 2) {
6656
+ console.warn("[FlightSimulator] \u822A\u70B9\u6570\u91CF\u4E0D\u8DB3");
6657
+ return;
6658
+ }
6659
+ const tempWaypoints = converted.waypointData.map((wp) => ({
6660
+ cartesian: wp.position,
6661
+ heading: wp.heading ?? 0,
6662
+ pitch: wp.pitch ?? 0,
6663
+ roll: wp.roll ?? 0,
6664
+ cumulativeDistance: 0
6665
+ }));
6666
+ let cumulativeDistance = 0;
6667
+ for (let i = 0; i < tempWaypoints.length; i++) {
6668
+ if (i > 0) {
6669
+ const prevCartesian = tempWaypoints[i - 1].cartesian;
6670
+ const currCartesian = tempWaypoints[i].cartesian;
6671
+ cumulativeDistance += C.Cartesian3.distance(prevCartesian, currCartesian);
6672
+ }
6673
+ tempWaypoints[i].cumulativeDistance = cumulativeDistance;
6674
+ }
6675
+ this.waypoints = tempWaypoints;
6676
+ this.totalDistance = cumulativeDistance;
6677
+ }
6678
+ /**
6679
+ * 创建可视化实体
6680
+ */
6681
+ createEntities() {
6682
+ if (this.waypoints.length === 0) return;
6683
+ const firstWaypoint = this.waypoints[0];
6684
+ this.airplaneCursor = new AirplaneCursor(
6685
+ this.CesiumNS,
6686
+ this.viewer,
6687
+ firstWaypoint.cartesian,
6688
+ {
6689
+ // 禁用键盘控制(仿真模式下不需要手动控制)
6690
+ stepMeters: 0,
6691
+ angleStepDeg: 0,
6692
+ fovDeg: 50
6693
+ }
6694
+ );
6695
+ this.airplaneCursor.setPose(
6696
+ firstWaypoint.cartesian,
6697
+ firstWaypoint.heading,
6698
+ firstWaypoint.pitch,
6699
+ firstWaypoint.roll
6700
+ );
6701
+ if (this.options.trackHighlight?.enabled) {
6702
+ this.createTrackHighlight();
6703
+ }
6704
+ }
6705
+ /**
6706
+ * 创建航迹高亮
6707
+ */
6708
+ createTrackHighlight() {
6709
+ const C = this.CesiumNS;
6710
+ const entities = this.options.layer?.entities ?? this.viewer.entities;
6711
+ const highlightOpts = this.options.trackHighlight;
6712
+ const allPositions = this.waypoints.map((wp) => wp.cartesian);
6713
+ this.trackEntity = entities.add({
6714
+ id: `flight-simulator-track-${Date.now()}`,
6715
+ polyline: {
6716
+ positions: allPositions,
6717
+ width: highlightOpts.width ?? 4,
6718
+ material: (highlightOpts.color ?? C.Color.CYAN).withAlpha(0.3),
6719
+ clampToGround: false
6720
+ },
6721
+ properties: {
6722
+ _type: "flight-simulator-track"
6723
+ }
6724
+ });
6725
+ this.passedTrackEntity = entities.add({
6726
+ id: `flight-simulator-passed-track-${Date.now()}`,
6727
+ polyline: {
6728
+ positions: new C.CallbackProperty(() => this.getPassedPositions(), false),
6729
+ width: (highlightOpts.width ?? 4) + 2,
6730
+ material: highlightOpts.color ?? C.Color.CYAN,
6731
+ clampToGround: false
6732
+ },
6733
+ properties: {
6734
+ _type: "flight-simulator-passed-track"
6735
+ }
6736
+ });
6737
+ }
6738
+ /**
6739
+ * 获取当前位置
6740
+ */
6741
+ getCurrentPosition() {
6742
+ if (this.waypoints.length === 0) {
6743
+ return this.CesiumNS.Cartesian3.ZERO;
6744
+ }
6745
+ const C = this.CesiumNS;
6746
+ for (let i = 1; i < this.waypoints.length; i++) {
6747
+ const prev = this.waypoints[i - 1];
6748
+ const curr = this.waypoints[i];
6749
+ if (this.currentDistance <= curr.cumulativeDistance) {
6750
+ const segmentStart = prev.cumulativeDistance;
6751
+ const segmentEnd = curr.cumulativeDistance;
6752
+ const segmentLength = segmentEnd - segmentStart;
6753
+ if (segmentLength <= 0) {
6754
+ return prev.cartesian;
6755
+ }
6756
+ const t = (this.currentDistance - segmentStart) / segmentLength;
6757
+ return C.Cartesian3.lerp(prev.cartesian, curr.cartesian, t, new C.Cartesian3());
6758
+ }
6759
+ }
6760
+ return this.waypoints[this.waypoints.length - 1].cartesian;
6761
+ }
6762
+ /**
6763
+ * 获取当前姿态(用于更新 AirplaneCursor)
6764
+ */
6765
+ getCurrentPose() {
6766
+ const C = this.CesiumNS;
6767
+ let heading = 0;
6768
+ let pitch = 0;
6769
+ let roll = 0;
6770
+ for (let i = 1; i < this.waypoints.length; i++) {
6771
+ const prev = this.waypoints[i - 1];
6772
+ const curr = this.waypoints[i];
6773
+ if (this.currentDistance <= curr.cumulativeDistance) {
6774
+ const segmentStart = prev.cumulativeDistance;
6775
+ const segmentEnd = curr.cumulativeDistance;
6776
+ const segmentLength = segmentEnd - segmentStart;
6777
+ if (segmentLength > 0) {
6778
+ const t = (this.currentDistance - segmentStart) / segmentLength;
6779
+ heading = C.Math.lerp(prev.heading, curr.heading, t);
6780
+ pitch = C.Math.lerp(prev.pitch, curr.pitch, t);
6781
+ roll = C.Math.lerp(prev.roll, curr.roll, t);
6782
+ } else {
6783
+ heading = prev.heading;
6784
+ pitch = prev.pitch;
6785
+ roll = prev.roll;
6786
+ }
6787
+ break;
6788
+ }
6789
+ }
6790
+ return { heading, pitch, roll };
6791
+ }
6792
+ /**
6793
+ * 获取已飞过的位置数组
6794
+ */
6795
+ getPassedPositions() {
6796
+ const positions = [];
6797
+ for (let i = 0; i < this.waypoints.length; i++) {
6798
+ const wp = this.waypoints[i];
6799
+ if (wp.cumulativeDistance <= this.currentDistance) {
6800
+ positions.push(wp.cartesian);
6801
+ } else {
6802
+ positions.push(this.getCurrentPosition());
6803
+ break;
6804
+ }
6805
+ }
6806
+ return positions;
6807
+ }
6808
+ /**
6809
+ * 设置状态并发射事件
6810
+ */
6811
+ setState(newState) {
6812
+ if (this.state === newState) return;
6813
+ this.state = newState;
6814
+ this.onStateChange.emit({ state: newState, progress: this.progress });
6815
+ }
6816
+ // ==================== 公共方法 ====================
6817
+ start() {
6818
+ if (this.waypoints.length < 2) {
6819
+ console.warn("[FlightSimulator] \u822A\u70B9\u6570\u91CF\u4E0D\u8DB3\uFF0C\u65E0\u6CD5\u5F00\u59CB\u4EFF\u771F");
6820
+ return;
6821
+ }
6822
+ this.currentDistance = 0;
6823
+ this.progress = 0;
6824
+ this.lastTimestamp = void 0;
6825
+ this.setState("playing");
6826
+ this.animationFrameId = requestAnimationFrame(this.animate);
6827
+ }
6828
+ pause() {
6829
+ if (this.state !== "playing") return;
6830
+ if (this.animationFrameId !== void 0) {
6831
+ cancelAnimationFrame(this.animationFrameId);
6832
+ this.animationFrameId = void 0;
6833
+ }
6834
+ this.lastTimestamp = void 0;
6835
+ this.setState("paused");
6836
+ }
6837
+ resume() {
6838
+ if (this.state !== "paused") return;
6839
+ this.lastTimestamp = void 0;
6840
+ this.setState("playing");
6841
+ this.animationFrameId = requestAnimationFrame(this.animate);
6842
+ }
6843
+ stop() {
6844
+ if (this.animationFrameId !== void 0) {
6845
+ cancelAnimationFrame(this.animationFrameId);
6846
+ this.animationFrameId = void 0;
6847
+ }
6848
+ this.currentDistance = 0;
6849
+ this.progress = 0;
6850
+ this.lastTimestamp = void 0;
6851
+ this.setState("stopped");
6852
+ this.viewer.scene.requestRender();
6853
+ }
6854
+ destroy() {
6855
+ this.stop();
6856
+ const entities = this.options.layer?.entities ?? this.viewer.entities;
6857
+ if (this.airplaneCursor) {
6858
+ this.airplaneCursor.destroy();
6859
+ this.airplaneCursor = void 0;
6860
+ }
6861
+ if (this.trackEntity) {
6862
+ entities.remove(this.trackEntity);
6863
+ this.trackEntity = void 0;
6864
+ }
6865
+ if (this.passedTrackEntity) {
6866
+ entities.remove(this.passedTrackEntity);
6867
+ this.passedTrackEntity = void 0;
6868
+ }
6869
+ this.waypoints = [];
6870
+ this.setState("idle");
6871
+ }
6872
+ setSpeed(speed) {
6873
+ this.options.speed = Math.max(0.1, speed);
6874
+ }
6875
+ getState() {
6876
+ return this.state;
6877
+ }
6878
+ getProgress() {
6879
+ return this.progress;
6880
+ }
6881
+ };
6882
+
6743
6883
  // src/core/CZMLPathManager.ts
6744
6884
  var _CZMLPathManager = class _CZMLPathManager {
6745
6885
  constructor(CesiumNS, viewer) {
@@ -7016,6 +7156,20 @@ var _CZMLPathManager = class _CZMLPathManager {
7016
7156
  options
7017
7157
  );
7018
7158
  }
7159
+ /**
7160
+ * Start flight simulation/preview for a wayline.
7161
+ * Animates an aircraft model along the wayline path with controls for play/pause/stop.
7162
+ * @param options - Configuration for the flight simulator
7163
+ * @returns A controller object with start/pause/resume/stop/destroy methods
7164
+ */
7165
+ startFlightPreview(options) {
7166
+ const simulator = new FlightSimulator(
7167
+ this.CesiumNS,
7168
+ this.viewer,
7169
+ options
7170
+ );
7171
+ return simulator;
7172
+ }
7019
7173
  // ensureLayerForEntity moved into path-editing module
7020
7174
  };
7021
7175
  __publicField(_CZMLPathManager, "SAMPLES_KEY", "__czmlPathSamples__");
@@ -8094,7 +8248,9 @@ var PolygonEditor = class {
8094
8248
  polygon: {
8095
8249
  hierarchy: positions,
8096
8250
  material: faceColor,
8097
- outline: false
8251
+ outline: false,
8252
+ // 🔧 修复:设置 heightReference 使多边形贴合地形
8253
+ heightReference: C.HeightReference.CLAMP_TO_GROUND
8098
8254
  }
8099
8255
  });
8100
8256
  const ring = positions.slice();
@@ -8107,15 +8263,51 @@ var PolygonEditor = class {
8107
8263
  width: 1,
8108
8264
  material: lineColor,
8109
8265
  clampToGround: true
8266
+ // 轮廓线已经设置了 clampToGround
8110
8267
  },
8111
8268
  properties: { _ownerId: id, _type: "polygon-outline" }
8112
8269
  });
8113
8270
  this.applyDashedOutlineStyle(outlineEntity, lineColor, 3, 10);
8114
8271
  const displayText = name.includes("_") ? name.split("_")[1] : name;
8272
+ const firstPoint = points[0];
8273
+ this.createLabelWithTerrainHeight(
8274
+ layer,
8275
+ id,
8276
+ displayText,
8277
+ firstPoint.lon,
8278
+ firstPoint.lat,
8279
+ lineColor
8280
+ );
8281
+ return entity;
8282
+ } catch (err) {
8283
+ console.error(`[PolygonEditor] \u521B\u5EFA\u591A\u8FB9\u5F62\u5931\u8D25:`, err);
8284
+ return null;
8285
+ }
8286
+ }
8287
+ /**
8288
+ * 辅助方法:使用地形采样创建标签
8289
+ * 先尝试获取地形高度,如果失败则使用 heightReference
8290
+ */
8291
+ async createLabelWithTerrainHeight(layer, ownerId, displayText, lon, lat, lineColor) {
8292
+ const C = this.CesiumNS;
8293
+ try {
8294
+ let terrainHeight = 0;
8295
+ const terrainProvider = this.viewer.terrainProvider;
8296
+ if (terrainProvider && typeof C.sampleTerrainMostDetailed === "function") {
8297
+ try {
8298
+ const positions = [C.Cartographic.fromDegrees(lon, lat)];
8299
+ const updatedPositions = await C.sampleTerrainMostDetailed(terrainProvider, positions);
8300
+ if (updatedPositions && updatedPositions[0] && updatedPositions[0].height !== void 0) {
8301
+ terrainHeight = updatedPositions[0].height;
8302
+ }
8303
+ } catch (e) {
8304
+ }
8305
+ }
8306
+ const labelPosition = C.Cartesian3.fromDegrees(lon, lat, terrainHeight + 1);
8115
8307
  layer.entities.add({
8116
- id: `${id}-label`,
8308
+ id: `${ownerId}-label`,
8117
8309
  name: "Polygon Label",
8118
- position: positions[0],
8310
+ position: labelPosition,
8119
8311
  label: {
8120
8312
  text: displayText,
8121
8313
  font: "bold 16px Microsoft YaHei, SimHei, sans-serif",
@@ -8125,14 +8317,14 @@ var PolygonEditor = class {
8125
8317
  style: C.LabelStyle.FILL_AND_OUTLINE,
8126
8318
  verticalOrigin: C.VerticalOrigin.BOTTOM,
8127
8319
  pixelOffset: new C.Cartesian2(0, -10),
8128
- disableDepthTestDistance: Number.POSITIVE_INFINITY
8320
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
8321
+ // 仍然设置 heightReference 作为备用
8322
+ heightReference: C.HeightReference.CLAMP_TO_GROUND
8129
8323
  },
8130
- properties: { _ownerId: id, _type: "polygon-label" }
8324
+ properties: { _ownerId: ownerId, _type: "polygon-label" }
8131
8325
  });
8132
- return entity;
8133
- } catch (err) {
8134
- console.error(`[PolygonEditor] \u521B\u5EFA\u591A\u8FB9\u5F62\u5931\u8D25:`, err);
8135
- return null;
8326
+ } catch (e) {
8327
+ console.warn(`[PolygonEditor] \u521B\u5EFA\u6807\u7B7E\u5931\u8D25:`, e);
8136
8328
  }
8137
8329
  }
8138
8330
  /**