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