@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/index.js CHANGED
@@ -11,7 +11,7 @@ var __publicField = (obj, key, value) => __defNormalProp(obj, typeof key !== "sy
11
11
  // package.json
12
12
  var package_default = {
13
13
  name: "@jorgmoritz/gis-manager",
14
- version: "0.1.36"};
14
+ version: "0.1.37"};
15
15
 
16
16
  // src/utils/version.ts
17
17
  var version = package_default.version;
@@ -2631,7 +2631,7 @@ var FrustumPyramid = class {
2631
2631
  showAt(apex, headingDeg, pitchDeg = 0, rollDeg = 0, fovDeg, lengthOverride) {
2632
2632
  if (this.destroyed) return;
2633
2633
  const C = this.CesiumNS;
2634
- const length = Math.max(1, lengthOverride ?? this.opts.length ?? 1e3);
2634
+ const length = Math.max(1, lengthOverride ?? this.opts.length ?? 500);
2635
2635
  const lakeEdge = this.opts.color ?? C.Color.fromCssColorString("#1e90ff");
2636
2636
  const alpha = this.opts.fillAlpha ?? 0.25;
2637
2637
  const lakeFill = (this.opts.fillColor ?? C.Color.fromCssColorString("#1e90ff")).withAlpha(alpha);
@@ -2814,8 +2814,6 @@ var AirplaneCursor = class {
2814
2814
  __publicField(this, "angleStep");
2815
2815
  /** 加速倍率(按住 Shift 生效) */
2816
2816
  __publicField(this, "fastFactor");
2817
- /** 最小高度(米) */
2818
- __publicField(this, "minHeight");
2819
2817
  /** 外部传入的行为配置与回调 */
2820
2818
  __publicField(this, "opts");
2821
2819
  // 使用内联的 _FrustumPyramid
@@ -2843,7 +2841,6 @@ var AirplaneCursor = class {
2843
2841
  this.step = opts.stepMeters ?? 2;
2844
2842
  this.angleStep = opts.angleStepDeg ?? 1;
2845
2843
  this.fastFactor = opts.fastFactor ?? 5;
2846
- this.minHeight = opts.minHeight ?? 0;
2847
2844
  this.currentFOV = opts.fovDeg ?? 50;
2848
2845
  this.ensureEntity(opts.color ?? C.Color.CYAN.withAlpha(0.9));
2849
2846
  this.attachKeyboard(opts);
@@ -3068,11 +3065,8 @@ var AirplaneCursor = class {
3068
3065
  }
3069
3066
  if (this.keysPressed.has("z")) {
3070
3067
  const newPos = addVec(pose.position, C.Cartesian3.multiplyByScalar(u3, -step, new C.Cartesian3()));
3071
- const newCarto = C.Cartographic.fromCartesian(newPos);
3072
- if (newCarto && newCarto.height >= this.minHeight) {
3073
- setPos(newPos);
3074
- moved = true;
3075
- }
3068
+ setPos(newPos);
3069
+ moved = true;
3076
3070
  }
3077
3071
  if (this.keysPressed.has("q")) {
3078
3072
  pose.heading = clampDeg(pose.heading - ang);
@@ -3125,8 +3119,8 @@ var AirplaneCursor = class {
3125
3119
  this.viewer.dataSources.add(layer);
3126
3120
  }
3127
3121
  this.frustum = new FrustumPyramid(this.CesiumNS, layer, {
3128
- fov: this.opts.fovDeg ?? 50,
3129
- length: 500,
3122
+ fov: this.opts.fovDeg ?? 40,
3123
+ length: 80,
3130
3124
  color: this.opts.color,
3131
3125
  fillAlpha: 0.25,
3132
3126
  width: 2,
@@ -3134,14 +3128,17 @@ var AirplaneCursor = class {
3134
3128
  logAngles: false,
3135
3129
  logThrottleMs: 300
3136
3130
  });
3131
+ const frustumFovScale = 0.6;
3132
+ const frustumLengthScale = 0.1;
3137
3133
  this.frustum.showAtDynamic(
3138
3134
  () => this.pose.position,
3139
3135
  () => this.pose.heading,
3140
3136
  () => this.pose.pitch,
3141
3137
  () => this.pose.roll,
3142
- () => this.currentFOV,
3143
- // 使用动态更新的 currentFOV
3144
- () => 1e3
3138
+ () => this.currentFOV * frustumFovScale,
3139
+ // 缩小 FOV
3140
+ () => 1e3 * frustumLengthScale
3141
+ // 缩小深度(1000 -> 100)
3145
3142
  );
3146
3143
  try {
3147
3144
  this.heightMarker = new HeightMarker(this.CesiumNS, this.viewer, layer);
@@ -3657,7 +3654,7 @@ function calculatePathDistance(CesiumNS, positions, targetIndex, hiddenClimbInde
3657
3654
  if (targetIndex < 0 || targetIndex >= positions.length) return 0;
3658
3655
  const C = CesiumNS;
3659
3656
  let totalDistance = 0;
3660
- const startIndex = hiddenClimbIndex === 1 ? 2 : 1;
3657
+ const startIndex = 1;
3661
3658
  if (targetIndex <= startIndex) return 0;
3662
3659
  for (let i = startIndex; i <= targetIndex; i++) {
3663
3660
  try {
@@ -3700,9 +3697,11 @@ var VertexInsertionHandler = class {
3700
3697
  * @param heightMarkers 高度标记数组(引用)
3701
3698
  * @param airplaneCursor 飞机游标(可选)
3702
3699
  * @param poseOrient 顶点姿态(可选)
3700
+ * @param useGlobalHeightFlags 🆕 是否使用全局高度标志数组(引用,可选)
3701
+ * @param altitudeModeOptions 🆕 高度模式选项(可选)
3703
3702
  * @returns 实际插入的索引
3704
3703
  */
3705
- insertVertex(insertAt, position, positions, handles, headings, pitches, rolls, fovs, editedIndices, vertexLabelManager, heightMarkers, airplaneCursor, poseOrient) {
3704
+ insertVertex(insertAt, position, positions, handles, headings, pitches, rolls, fovs, editedIndices, vertexLabelManager, heightMarkers, airplaneCursor, poseOrient, useGlobalHeightFlags) {
3706
3705
  const C = this.CesiumNS;
3707
3706
  if (this.hiddenClimbIndex === 1 && insertAt <= 1) {
3708
3707
  insertAt = 2;
@@ -3737,6 +3736,12 @@ var VertexInsertionHandler = class {
3737
3736
  pitches.splice(insertAt, 0, pose.pitch);
3738
3737
  rolls.splice(insertAt, 0, pose.roll);
3739
3738
  fovs.splice(insertAt, 0, 50);
3739
+ if (useGlobalHeightFlags) {
3740
+ for (let i = useGlobalHeightFlags.length - 1; i >= insertAt; i--) {
3741
+ useGlobalHeightFlags[i + 1] = useGlobalHeightFlags[i];
3742
+ }
3743
+ useGlobalHeightFlags[insertAt] = true;
3744
+ }
3740
3745
  return insertAt;
3741
3746
  }
3742
3747
  /**
@@ -3945,7 +3950,6 @@ var VertexDragHandler = class {
3945
3950
  __publicField(this, "CesiumNS");
3946
3951
  __publicField(this, "viewer");
3947
3952
  __publicField(this, "hiddenClimbIndex");
3948
- __publicField(this, "minHeight");
3949
3953
  __publicField(this, "callbacks");
3950
3954
  __publicField(this, "dragState", null);
3951
3955
  __publicField(this, "canvas");
@@ -3954,7 +3958,6 @@ var VertexDragHandler = class {
3954
3958
  this.CesiumNS = options.CesiumNS;
3955
3959
  this.viewer = options.viewer;
3956
3960
  this.hiddenClimbIndex = options.hiddenClimbIndex;
3957
- this.minHeight = options.minHeight ?? 0;
3958
3961
  this.callbacks = callbacks;
3959
3962
  this.canvas = this.viewer.scene.canvas;
3960
3963
  this.setupKeyboardListeners();
@@ -4130,7 +4133,7 @@ var VertexDragHandler = class {
4130
4133
  const cameraHeight = this.viewer.camera.positionCartographic.height;
4131
4134
  const scaleFactor = Math.max(cameraHeight / 1e3, 0.5);
4132
4135
  const heightDelta = -deltaY * scaleFactor;
4133
- const newHeight = Math.max(this.minHeight, this.dragState.startHeight + heightDelta);
4136
+ const newHeight = this.dragState.startHeight + heightDelta;
4134
4137
  const newPosition = C.Cartesian3.fromDegrees(
4135
4138
  this.dragState.startLonLat.lon,
4136
4139
  this.dragState.startLonLat.lat,
@@ -4215,6 +4218,83 @@ var VertexDragHandler = class {
4215
4218
  }
4216
4219
  };
4217
4220
 
4221
+ // src/core/path-manager/utils/TerrainHeightQuery.ts
4222
+ async function queryTerrainHeights(CesiumNS, viewer, positions) {
4223
+ const C = CesiumNS;
4224
+ if (!positions || positions.length === 0) {
4225
+ return [];
4226
+ }
4227
+ const cartographics = positions.map((pos) => {
4228
+ try {
4229
+ return C.Cartographic.fromCartesian(pos);
4230
+ } catch {
4231
+ return new C.Cartographic(0, 0, 0);
4232
+ }
4233
+ });
4234
+ const terrainProvider = viewer.terrainProvider;
4235
+ if (!terrainProvider || !C.sampleTerrainMostDetailed) {
4236
+ console.warn("[TerrainHeightQuery] \u65E0\u53EF\u7528\u5730\u5F62\u63D0\u4F9B\u8005\uFF0C\u4F7F\u7528\u540C\u6B65\u56DE\u9000\u65B9\u6848");
4237
+ return positions.map((pos) => queryTerrainHeightSync(CesiumNS, viewer, pos));
4238
+ }
4239
+ try {
4240
+ const sampledPositions = await C.sampleTerrainMostDetailed(
4241
+ terrainProvider,
4242
+ cartographics
4243
+ );
4244
+ return sampledPositions.map((carto) => {
4245
+ const height = carto?.height;
4246
+ return typeof height === "number" && !isNaN(height) ? height : 0;
4247
+ });
4248
+ } catch (error) {
4249
+ console.warn("[TerrainHeightQuery] \u5F02\u6B65\u67E5\u8BE2\u5931\u8D25\uFF0C\u4F7F\u7528\u540C\u6B65\u56DE\u9000:", error);
4250
+ return positions.map((pos) => queryTerrainHeightSync(CesiumNS, viewer, pos));
4251
+ }
4252
+ }
4253
+ function queryTerrainHeightSync(CesiumNS, viewer, position) {
4254
+ const C = CesiumNS;
4255
+ try {
4256
+ const cartographic = C.Cartographic.fromCartesian(position);
4257
+ const globe = viewer.scene.globe;
4258
+ if (globe && typeof globe.getHeight === "function") {
4259
+ const height = globe.getHeight(cartographic);
4260
+ if (typeof height === "number" && !isNaN(height)) {
4261
+ return height;
4262
+ }
4263
+ }
4264
+ } catch (error) {
4265
+ console.warn("[TerrainHeightQuery] \u540C\u6B65\u67E5\u8BE2\u5931\u8D25:", error);
4266
+ }
4267
+ return 0;
4268
+ }
4269
+ async function queryTerrainHeightAsync(CesiumNS, viewer, position) {
4270
+ const heights = await queryTerrainHeights(CesiumNS, viewer, [position]);
4271
+ return heights[0] ?? 0;
4272
+ }
4273
+ function calculateAbsoluteHeight(altitudeMode, defaultAltitude, startPointAltitude, terrainHeight) {
4274
+ switch (altitudeMode) {
4275
+ case "absolute":
4276
+ return defaultAltitude;
4277
+ case "relativeToStart":
4278
+ return startPointAltitude + defaultAltitude;
4279
+ case "relativeToGround":
4280
+ return terrainHeight + defaultAltitude;
4281
+ default:
4282
+ return defaultAltitude;
4283
+ }
4284
+ }
4285
+ function calculateRelativeHeight(altitudeMode, absoluteHeight, startPointAltitude, terrainHeight) {
4286
+ switch (altitudeMode) {
4287
+ case "absolute":
4288
+ return absoluteHeight;
4289
+ case "relativeToStart":
4290
+ return absoluteHeight - startPointAltitude;
4291
+ case "relativeToGround":
4292
+ return absoluteHeight - terrainHeight;
4293
+ default:
4294
+ return absoluteHeight;
4295
+ }
4296
+ }
4297
+
4218
4298
  // src/core/path-manager/editing/PathEditingEventHandler.ts
4219
4299
  var PathEditingEventHandler = class {
4220
4300
  constructor(options, callbacks) {
@@ -4237,8 +4317,7 @@ var PathEditingEventHandler = class {
4237
4317
  {
4238
4318
  CesiumNS: this.CesiumNS,
4239
4319
  viewer: this.viewer,
4240
- hiddenClimbIndex: this.hiddenClimbIndex,
4241
- minHeight: options.minHeight
4320
+ hiddenClimbIndex: this.hiddenClimbIndex
4242
4321
  },
4243
4322
  {
4244
4323
  onDragStart: (index) => {
@@ -4473,23 +4552,14 @@ var PathEditingEventHandler = class {
4473
4552
  buildVertexContextMenuItems(vertexIndex) {
4474
4553
  const menuItems = [];
4475
4554
  const positions = this.callbacks.getPositions?.() || [];
4476
- const visibleVertexCount = positions.length - (this.hiddenClimbIndex === 1 ? 1 : 0);
4477
- const canDelete = visibleVertexCount > 1;
4478
- const isStartPoint = vertexIndex === 0 || this.hiddenClimbIndex === 1 && vertexIndex === 1;
4479
- if (canDelete && !isStartPoint) {
4555
+ const canDelete = positions.length > 1;
4556
+ if (canDelete) {
4480
4557
  menuItems.push({
4481
4558
  label: `\u5220\u9664\u822A\u70B9 ${this.getVertexDisplayNumber(vertexIndex)}`,
4482
4559
  action: () => {
4483
4560
  this.handleDeleteVertex(vertexIndex);
4484
4561
  }
4485
4562
  });
4486
- } else if (isStartPoint) {
4487
- menuItems.push({
4488
- label: "\u8D77\u70B9\u4E0D\u53EF\u5220\u9664",
4489
- action: () => {
4490
- },
4491
- disabled: true
4492
- });
4493
4563
  } else {
4494
4564
  menuItems.push({
4495
4565
  label: "\u81F3\u5C11\u4FDD\u7559\u4E00\u4E2A\u822A\u70B9",
@@ -4501,12 +4571,10 @@ var PathEditingEventHandler = class {
4501
4571
  return menuItems;
4502
4572
  }
4503
4573
  /**
4504
- * 获取顶点的显示编号(跳过隐藏爬升点)
4574
+ * 获取顶点的显示编号
4575
+ * 🆕 不再跳过隐藏爬升点,直接返回索引
4505
4576
  */
4506
4577
  getVertexDisplayNumber(index) {
4507
- if (this.hiddenClimbIndex === 1 && index > 1) {
4508
- return index - 1;
4509
- }
4510
4578
  return index;
4511
4579
  }
4512
4580
  /**
@@ -4538,18 +4606,17 @@ var PathEditingEventHandler = class {
4538
4606
  };
4539
4607
  }
4540
4608
  const totalVertices = positions.length;
4541
- const totalVisibleVertices = totalVertices - (this.hiddenClimbIndex === 1 ? 1 : 0);
4542
- const isStartPoint = index === 0 || this.hiddenClimbIndex === 1 && index === 1;
4543
- const isHiddenClimb = this.hiddenClimbIndex === 1 && index === 1;
4544
- const canDelete = totalVisibleVertices > 1 && !isStartPoint;
4609
+ const totalVisibleVertices = totalVertices;
4610
+ const isStartPoint = false;
4611
+ const isHiddenClimb = false;
4612
+ const canDelete = totalVertices > 1;
4545
4613
  let pathProperties;
4546
4614
  if (entity) {
4547
4615
  try {
4548
4616
  const props = entity.properties;
4549
4617
  pathProperties = {
4550
4618
  altitudeMode: props?._altitudeMode?.getValue?.() ?? props?._altitudeMode,
4551
- climbHeight: props?._climbHeight?.getValue?.() ?? props?._climbHeight,
4552
- hasHiddenClimb: props?._hasHiddenClimb?.getValue?.() ?? props?._hasHiddenClimb
4619
+ climbHeight: props?._climbHeight?.getValue?.() ?? props?._climbHeight
4553
4620
  };
4554
4621
  } catch {
4555
4622
  }
@@ -4588,11 +4655,9 @@ var PathEditingEventHandler = class {
4588
4655
  }
4589
4656
  /**
4590
4657
  * 处理插入顶点
4658
+ * 🆕 不再跳过隐藏爬升点
4591
4659
  */
4592
4660
  handleInsertVertex(insertAt, pose, type) {
4593
- if (this.hiddenClimbIndex === 1 && insertAt <= 1) {
4594
- insertAt = 2;
4595
- }
4596
4661
  if (this.callbacks.onInsertVertex) {
4597
4662
  this.callbacks.onInsertVertex(insertAt, pose.position, {
4598
4663
  heading: pose.heading,
@@ -4623,6 +4688,10 @@ var PathEditingEventHandler = class {
4623
4688
  }
4624
4689
  /**
4625
4690
  * 🆕 处理快速编辑模式的点击
4691
+ * 根据 altitudeMode 计算航点高度:
4692
+ * - absolute: 航点高度 = defaultAltitude
4693
+ * - relativeToGround: 航点高度 = 地形高度 + defaultAltitude
4694
+ * - relativeToStart: 航点高度 = 起飞点高度 + defaultAltitude
4626
4695
  */
4627
4696
  handleQuickEditClick(movement) {
4628
4697
  const position = this.getPositionFromMouse(movement);
@@ -4632,11 +4701,9 @@ var PathEditingEventHandler = class {
4632
4701
  }
4633
4702
  const quickEditOptions = this.callbacks.getQuickEditOptions?.();
4634
4703
  if (!quickEditOptions) return;
4635
- const { climbHeight, altitudeMode } = quickEditOptions;
4704
+ const { defaultAltitude, altitudeMode } = quickEditOptions;
4705
+ const newPosition = this.calculatePositionByAltitudeMode(position, defaultAltitude, altitudeMode);
4636
4706
  const C = this.CesiumNS;
4637
- const originalCarto = C.Cartographic.fromCartesian(position);
4638
- const originalHeight = originalCarto.height;
4639
- const newPosition = this.applyClimbHeight(position, climbHeight, altitudeMode);
4640
4707
  const newCarto = C.Cartographic.fromCartesian(newPosition);
4641
4708
  const newHeight = newCarto.height;
4642
4709
  const positions = this.callbacks.getPositions?.() || [];
@@ -4651,8 +4718,7 @@ var PathEditingEventHandler = class {
4651
4718
  insertAt,
4652
4719
  heading: heading.toFixed(2),
4653
4720
  altitudeMode,
4654
- climbHeight,
4655
- originalHeight: originalHeight.toFixed(2),
4721
+ defaultAltitude,
4656
4722
  finalHeight: newHeight.toFixed(2)
4657
4723
  });
4658
4724
  this.callbacks.onInsertVertex(insertAt, newPosition, {
@@ -4664,21 +4730,35 @@ var PathEditingEventHandler = class {
4664
4730
  }
4665
4731
  }
4666
4732
  /**
4667
- * 🆕 应用爬升高度到位置
4733
+ * 🆕 根据高度模式计算航点位置
4734
+ * - absolute: 航点高度 = defaultAltitude
4735
+ * - relativeToGround: 航点高度 = 地形高度 + defaultAltitude
4736
+ * - relativeToStart: 航点高度 = 起飞点高度 + defaultAltitude
4668
4737
  */
4669
- applyClimbHeight(position, climbHeight, altitudeMode) {
4738
+ calculatePositionByAltitudeMode(position, defaultAltitude, altitudeMode) {
4670
4739
  const C = this.CesiumNS;
4671
4740
  try {
4672
4741
  const cartographic = C.Cartographic.fromCartesian(position);
4673
4742
  const lon = C.Math.toDegrees(cartographic.longitude);
4674
4743
  const lat = C.Math.toDegrees(cartographic.latitude);
4675
- let height = cartographic.height;
4676
- if (altitudeMode === "relativeToGround" || altitudeMode === "relativeToStart") {
4677
- height += climbHeight;
4678
- } else if (altitudeMode === "absolute") {
4679
- height = climbHeight;
4744
+ let finalHeight;
4745
+ if (altitudeMode === "absolute") {
4746
+ finalHeight = defaultAltitude;
4747
+ } else if (altitudeMode === "relativeToGround") {
4748
+ const terrainHeight = queryTerrainHeightSync(this.CesiumNS, this.viewer, position);
4749
+ finalHeight = terrainHeight + defaultAltitude;
4750
+ } else if (altitudeMode === "relativeToStart") {
4751
+ const startPointAltitude = this.callbacks.getStartPointAltitude?.() ?? 0;
4752
+ finalHeight = startPointAltitude + defaultAltitude;
4753
+ } else {
4754
+ finalHeight = defaultAltitude;
4680
4755
  }
4681
- return C.Cartesian3.fromDegrees(lon, lat, height);
4756
+ console.log("[QuickEdit] \u9AD8\u5EA6\u8BA1\u7B97:", {
4757
+ altitudeMode,
4758
+ defaultAltitude,
4759
+ finalHeight
4760
+ });
4761
+ return C.Cartesian3.fromDegrees(lon, lat, finalHeight);
4682
4762
  } catch (error) {
4683
4763
  console.error("[QuickEdit] \u9AD8\u5EA6\u8BA1\u7B97\u5931\u8D25:", error);
4684
4764
  return position;
@@ -4770,19 +4850,19 @@ var VertexHandleStyleManager = class {
4770
4850
  __publicField(this, "selectedStyle");
4771
4851
  const C = CesiumNS;
4772
4852
  this.normalStyle = {
4773
- pixelSize: options?.normal?.pixelSize ?? 10,
4853
+ pixelSize: options?.normal?.pixelSize ?? 6,
4774
4854
  color: options?.normal?.color ?? C.Color.YELLOW,
4775
4855
  outlineColor: options?.normal?.outlineColor ?? C.Color.BLACK,
4776
4856
  outlineWidth: options?.normal?.outlineWidth ?? 1
4777
4857
  };
4778
4858
  this.editedStyle = {
4779
- pixelSize: options?.edited?.pixelSize ?? 10,
4859
+ pixelSize: options?.edited?.pixelSize ?? 6,
4780
4860
  color: options?.edited?.color ?? C.Color.CYAN,
4781
4861
  outlineColor: options?.edited?.outlineColor ?? C.Color.BLACK,
4782
4862
  outlineWidth: options?.edited?.outlineWidth ?? 1
4783
4863
  };
4784
4864
  this.selectedStyle = {
4785
- pixelSize: options?.selected?.pixelSize ?? 14,
4865
+ pixelSize: options?.selected?.pixelSize ?? 8,
4786
4866
  color: options?.selected?.color ?? C.Color.ORANGE,
4787
4867
  outlineColor: options?.selected?.outlineColor ?? C.Color.BLACK,
4788
4868
  outlineWidth: options?.selected?.outlineWidth ?? 2
@@ -4990,18 +5070,57 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
4990
5070
  } catch {
4991
5071
  positions = [];
4992
5072
  }
4993
- let hiddenClimbIndex;
5073
+ const hiddenClimbIndex = void 0;
5074
+ let currentAltitudeMode = "absolute";
5075
+ let currentDefaultAltitude = 100;
4994
5076
  try {
4995
5077
  const props = entity.properties;
4996
- const hFlag = props?._hasHiddenClimb?.getValue?.() ?? props?._hasHiddenClimb;
4997
- if (hFlag && positions.length >= 2) hiddenClimbIndex = 1;
5078
+ const storedMode = props?._altitudeMode?.getValue?.() ?? props?._altitudeMode;
5079
+ if (storedMode === "absolute" || storedMode === "relativeToGround" || storedMode === "relativeToStart") {
5080
+ currentAltitudeMode = storedMode;
5081
+ }
5082
+ const storedDefaultAlt = props?._defaultAltitude?.getValue?.() ?? props?._defaultAltitude;
5083
+ if (typeof storedDefaultAlt === "number") {
5084
+ currentDefaultAltitude = storedDefaultAlt;
5085
+ }
5086
+ } catch {
5087
+ }
5088
+ if (options?.altitudeMode) {
5089
+ currentAltitudeMode = options.altitudeMode;
5090
+ }
5091
+ if (options?.defaultAltitude !== void 0) {
5092
+ currentDefaultAltitude = options.defaultAltitude;
5093
+ }
5094
+ let startPointAltitude = 0;
5095
+ try {
5096
+ if (positions.length > 0) {
5097
+ const startCarto = C.Cartographic.fromCartesian(positions[0]);
5098
+ startPointAltitude = startCarto.height ?? 0;
5099
+ }
4998
5100
  } catch {
4999
5101
  }
5102
+ const terrainHeightsCache = /* @__PURE__ */ new Map();
5103
+ const useGlobalHeightFlags = [];
5104
+ try {
5105
+ const props = entity.properties;
5106
+ const storedFlags = props?._useGlobalHeightFlags?.getValue?.() ?? props?._useGlobalHeightFlags;
5107
+ if (Array.isArray(storedFlags)) {
5108
+ storedFlags.forEach((flag, idx) => {
5109
+ useGlobalHeightFlags[idx] = flag;
5110
+ });
5111
+ }
5112
+ } catch {
5113
+ }
5114
+ while (useGlobalHeightFlags.length < positions.length) {
5115
+ useGlobalHeightFlags.push(false);
5116
+ }
5000
5117
  let quickEditEnabled = false;
5001
5118
  let quickEditOptions = {
5002
5119
  enabled: false,
5003
- climbHeight: 100,
5004
- altitudeMode: "absolute"
5120
+ climbHeight: currentDefaultAltitude,
5121
+ // 使用当前默认高度
5122
+ altitudeMode: currentAltitudeMode
5123
+ // 使用当前高度模式
5005
5124
  };
5006
5125
  if (options?.quickEdit) {
5007
5126
  if (typeof options.quickEdit === "boolean") {
@@ -5034,33 +5153,23 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5034
5153
  } catch {
5035
5154
  }
5036
5155
  const handles = positions.map((p, i) => {
5037
- if (hiddenClimbIndex === i) return void 0;
5038
- const isStartPoint = i === 0;
5039
5156
  const h = existing[i];
5040
5157
  if (h) {
5041
5158
  try {
5042
5159
  h.position = p;
5043
- if (!isStartPoint) {
5044
- h.point = h.point ?? { pixelSize: 10 };
5045
- h.point.pixelSize = 10;
5046
- h.point.color = C.Color.YELLOW;
5047
- h.point.outlineColor = C.Color.BLACK;
5048
- h.point.outlineWidth = 1;
5049
- }
5160
+ h.point = h.point ?? { pixelSize: 6 };
5161
+ h.point.pixelSize = 6;
5162
+ h.point.color = C.Color.YELLOW;
5163
+ h.point.outlineColor = C.Color.BLACK;
5164
+ h.point.outlineWidth = 1;
5050
5165
  h.properties = { _type: "path-vertex", _ownerId: ownerId, _vertexIndex: i };
5051
5166
  } catch {
5052
5167
  }
5053
5168
  return h;
5054
5169
  }
5055
- if (isStartPoint) {
5056
- return layer.entities.add({
5057
- position: p,
5058
- properties: { _type: "path-vertex", _ownerId: ownerId, _vertexIndex: i }
5059
- });
5060
- }
5061
5170
  return layer.entities.add({
5062
5171
  position: p,
5063
- point: { pixelSize: 10, color: C.Color.YELLOW, outlineColor: C.Color.BLACK, outlineWidth: 1 },
5172
+ point: { pixelSize: 6, color: C.Color.YELLOW, outlineColor: C.Color.BLACK, outlineWidth: 1 },
5064
5173
  properties: { _type: "path-vertex", _ownerId: ownerId, _vertexIndex: i }
5065
5174
  });
5066
5175
  });
@@ -5077,8 +5186,6 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5077
5186
  };
5078
5187
  const createOrUpdateMarkerForIndex = (idx) => {
5079
5188
  try {
5080
- if (hiddenClimbIndex === 1 && idx === 1) return;
5081
- if (idx === 0) return;
5082
5189
  const pos = positions[idx];
5083
5190
  if (!pos) return;
5084
5191
  const markerLayer = ensureMarkerLayer();
@@ -5160,7 +5267,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5160
5267
  };
5161
5268
  const getTotalDistance = () => {
5162
5269
  if (positions.length < 2) return 0;
5163
- return calculatePathDistance(CesiumNS, positions, positions.length - 1, hiddenClimbIndex);
5270
+ return calculatePathDistance(CesiumNS, positions, positions.length - 1);
5164
5271
  };
5165
5272
  const vertexInsertionHandler = new VertexInsertionHandler({
5166
5273
  CesiumNS,
@@ -5169,7 +5276,40 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5169
5276
  hiddenClimbIndex
5170
5277
  });
5171
5278
  let cursorStart = positions[positions.length - 1];
5172
- if (!cursorStart) cursorStart = positions[hiddenClimbIndex === 1 ? 1 : 0];
5279
+ if (!cursorStart) cursorStart = positions[0];
5280
+ if (!cursorStart) {
5281
+ try {
5282
+ const cameraPos = viewer.camera.positionCartographic;
5283
+ if (cameraPos) {
5284
+ let initialHeight = currentDefaultAltitude;
5285
+ if (currentAltitudeMode === "relativeToGround") {
5286
+ const terrainHeight = queryTerrainHeightSync(
5287
+ CesiumNS,
5288
+ viewer,
5289
+ C.Cartesian3.fromRadians(cameraPos.longitude, cameraPos.latitude, 0)
5290
+ );
5291
+ initialHeight = terrainHeight + currentDefaultAltitude;
5292
+ console.log("[PathEditing] relativeToGround \u6A21\u5F0F\uFF0C\u5730\u5F62\u9AD8\u5EA6:", terrainHeight, "\u521D\u59CB\u9AD8\u5EA6:", initialHeight);
5293
+ } else if (currentAltitudeMode === "relativeToStart") {
5294
+ initialHeight = currentDefaultAltitude;
5295
+ console.log("[PathEditing] relativeToStart \u6A21\u5F0F\uFF08\u7A7A\u822A\u7EBF\uFF09\uFF0C\u4F7F\u7528\u9ED8\u8BA4\u9AD8\u5EA6:", initialHeight);
5296
+ }
5297
+ cursorStart = C.Cartesian3.fromRadians(
5298
+ cameraPos.longitude,
5299
+ cameraPos.latitude,
5300
+ initialHeight
5301
+ );
5302
+ console.log("[PathEditing] \u7A7A\u822A\u7EBF\uFF0C\u4F7F\u7528\u76F8\u673A\u4F4D\u7F6E\u521D\u59CB\u5316\u6E38\u6807:", {
5303
+ lon: C.Math.toDegrees(cameraPos.longitude),
5304
+ lat: C.Math.toDegrees(cameraPos.latitude),
5305
+ height: initialHeight,
5306
+ altitudeMode: currentAltitudeMode
5307
+ });
5308
+ }
5309
+ } catch (error) {
5310
+ console.warn("[PathEditing] \u65E0\u6CD5\u83B7\u53D6\u76F8\u673A\u4F4D\u7F6E:", error);
5311
+ }
5312
+ }
5173
5313
  let airplaneCursor;
5174
5314
  const insertVertex = (insertAt, p3, poseOrient) => {
5175
5315
  console.log("[PathEditing] \u63D2\u5165\u9876\u70B9\u524D positions \u957F\u5EA6:", positions.length, "insertAt:", insertAt);
@@ -5186,7 +5326,9 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5186
5326
  vertexLabelManager,
5187
5327
  heightMarkers,
5188
5328
  airplaneCursor,
5189
- poseOrient
5329
+ poseOrient,
5330
+ useGlobalHeightFlags
5331
+ // 🆕 传递 useGlobalHeightFlags
5190
5332
  );
5191
5333
  console.log("[PathEditing] \u63D2\u5165\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length, "actualIndex:", actualIndex);
5192
5334
  entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
@@ -5195,16 +5337,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5195
5337
  };
5196
5338
  const deleteVertex = (deleteAt) => {
5197
5339
  try {
5198
- if (deleteAt === 0 || hiddenClimbIndex === 1 && deleteAt === 1) {
5199
- console.warn("Cannot delete start point");
5200
- return;
5201
- }
5202
- if (hiddenClimbIndex !== void 0 && deleteAt === hiddenClimbIndex) {
5203
- console.warn("Cannot delete hidden climb point");
5204
- return;
5205
- }
5206
- const visibleVertexCount = positions.length - (hiddenClimbIndex === 1 ? 1 : 0);
5207
- if (visibleVertexCount <= 1) {
5340
+ if (positions.length <= 1) {
5208
5341
  console.warn("Cannot delete last vertex");
5209
5342
  return;
5210
5343
  }
@@ -5228,6 +5361,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5228
5361
  rolls.splice(deleteAt, 1);
5229
5362
  fovs.splice(deleteAt, 1);
5230
5363
  heightMarkers.splice(deleteAt, 1);
5364
+ useGlobalHeightFlags.splice(deleteAt, 1);
5231
5365
  console.log("[PathEditing] \u5220\u9664\u9876\u70B9\u540E positions \u957F\u5EA6:", positions.length);
5232
5366
  entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
5233
5367
  const newEditedIndices = /* @__PURE__ */ new Set();
@@ -5289,9 +5423,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5289
5423
  }
5290
5424
  },
5291
5425
  // frustumLengthFactor: options?.preview?.lengthFactor ?? 0.3,
5292
- fovDeg: options?.preview?.fov ?? 50,
5293
- minHeight: options?.minHeight
5294
- // 传递最小高度限制
5426
+ fovDeg: options?.preview?.fov ?? 50
5295
5427
  });
5296
5428
  if (options?.preview?.enabled !== false && airplaneCursor) {
5297
5429
  preview = new PathPreview(CesiumNS, viewer, {
@@ -5335,8 +5467,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5335
5467
  {
5336
5468
  CesiumNS,
5337
5469
  viewer,
5338
- hiddenClimbIndex,
5339
- minHeight: options?.minHeight
5470
+ hiddenClimbIndex
5340
5471
  },
5341
5472
  {
5342
5473
  onVertexSelect: (index) => setActiveIndex(index),
@@ -5367,7 +5498,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5367
5498
  },
5368
5499
  onDeleteVertex: (index) => {
5369
5500
  const totalBefore = positions.length;
5370
- const displayNumber = hiddenClimbIndex === 1 && index > 1 ? index - 1 : index;
5501
+ const displayNumber = index;
5371
5502
  deleteVertex(index);
5372
5503
  if (options?.onVertexDeleteDetail) {
5373
5504
  try {
@@ -5420,7 +5551,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5420
5551
  };
5421
5552
  } catch {
5422
5553
  }
5423
- const displayNumber = hiddenClimbIndex === 1 && index > 1 ? index - 1 : index;
5554
+ const displayNumber = index;
5424
5555
  const dragMoveInfo = {
5425
5556
  index,
5426
5557
  displayNumber,
@@ -5437,6 +5568,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5437
5568
  onVertexDragEnd: (index, finalPosition) => {
5438
5569
  editedIndices.add(index);
5439
5570
  positions[index] = finalPosition;
5571
+ useGlobalHeightFlags[index] = false;
5440
5572
  if (handles[index]) {
5441
5573
  handleStyleManager.applyStyle(handles[index], "edited");
5442
5574
  }
@@ -5483,10 +5615,29 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5483
5615
  getCursorPose: () => airplaneCursor?.getPose(),
5484
5616
  // 🆕 快速编辑回调
5485
5617
  getQuickEditEnabled: () => quickEditEnabled,
5486
- getQuickEditOptions: () => ({
5487
- climbHeight: quickEditOptions.climbHeight ?? 100,
5488
- altitudeMode: quickEditOptions.altitudeMode ?? "absolute"
5489
- })
5618
+ getQuickEditOptions: () => {
5619
+ let climbHeight = 0;
5620
+ try {
5621
+ const props = entity.properties;
5622
+ climbHeight = props?._climbHeight?.getValue?.() ?? props?._climbHeight ?? 0;
5623
+ } catch {
5624
+ }
5625
+ return {
5626
+ climbHeight,
5627
+ defaultAltitude: currentDefaultAltitude,
5628
+ altitudeMode: currentAltitudeMode
5629
+ };
5630
+ },
5631
+ // 🆕 高度模式相关回调
5632
+ getStartPointAltitude: () => startPointAltitude,
5633
+ getDefaultAltitude: () => currentDefaultAltitude,
5634
+ // 🆕 useGlobalHeightFlags 相关回调
5635
+ getUseGlobalHeightFlags: () => useGlobalHeightFlags,
5636
+ setUseGlobalHeightFlag: (index, value) => {
5637
+ if (index >= 0 && index < useGlobalHeightFlags.length) {
5638
+ useGlobalHeightFlags[index] = value;
5639
+ }
5640
+ }
5490
5641
  }
5491
5642
  );
5492
5643
  const cleanupSession = () => {
@@ -5526,6 +5677,7 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5526
5677
  const buildVertexDetailInfo = (index) => {
5527
5678
  const position = positions[index];
5528
5679
  let coordinates = { longitude: 0, latitude: 0, height: 0 };
5680
+ let ellipsoidHeight = 0;
5529
5681
  try {
5530
5682
  const cartographic = C.Cartographic.fromCartesian(position);
5531
5683
  coordinates = {
@@ -5533,8 +5685,16 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5533
5685
  latitude: C.Math.toDegrees(cartographic.latitude),
5534
5686
  height: cartographic.height
5535
5687
  };
5688
+ ellipsoidHeight = cartographic.height ?? 0;
5536
5689
  } catch {
5537
5690
  }
5691
+ const terrainHeight = terrainHeightsCache.get(index) ?? 0;
5692
+ const relativeHeight = calculateRelativeHeight(
5693
+ currentAltitudeMode,
5694
+ ellipsoidHeight,
5695
+ startPointAltitude,
5696
+ terrainHeight
5697
+ );
5538
5698
  const pose = {
5539
5699
  heading: headings[index] ?? 0,
5540
5700
  pitch: pitches[index] ?? 0,
@@ -5542,18 +5702,17 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5542
5702
  fov: fovs[index] ?? 50
5543
5703
  };
5544
5704
  const totalVertices = positions.length;
5545
- const totalVisibleVertices = totalVertices - (hiddenClimbIndex === 1 ? 1 : 0);
5546
- const isStartPoint = index === 0 || hiddenClimbIndex === 1 && index === 1;
5547
- const isHiddenClimb = hiddenClimbIndex === 1 && index === 1;
5548
- const canDelete = totalVisibleVertices > 1 && !isStartPoint;
5549
- const displayNumber = hiddenClimbIndex === 1 && index > 1 ? index - 1 : index;
5705
+ const totalVisibleVertices = totalVertices;
5706
+ const isStartPoint = false;
5707
+ const isHiddenClimb = false;
5708
+ const canDelete = totalVertices > 1;
5709
+ const displayNumber = index;
5550
5710
  let pathProperties;
5551
5711
  try {
5552
5712
  const props = entity.properties;
5553
5713
  pathProperties = {
5554
5714
  altitudeMode: props?._altitudeMode?.getValue?.() ?? props?._altitudeMode,
5555
- climbHeight: props?._climbHeight?.getValue?.() ?? props?._climbHeight,
5556
- hasHiddenClimb: props?._hasHiddenClimb?.getValue?.() ?? props?._hasHiddenClimb
5715
+ climbHeight: props?._climbHeight?.getValue?.() ?? props?._climbHeight
5557
5716
  };
5558
5717
  } catch {
5559
5718
  }
@@ -5569,44 +5728,68 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5569
5728
  totalVisibleVertices,
5570
5729
  totalVertices,
5571
5730
  entity,
5572
- pathProperties
5731
+ pathProperties,
5732
+ useGlobalHeight: useGlobalHeightFlags[index] ?? false,
5733
+ // 🆕 添加 useGlobalHeight
5734
+ ellipsoidHeight,
5735
+ // 🆕 添加椭球高度
5736
+ relativeHeight
5737
+ // 🆕 添加相对高度
5573
5738
  };
5574
5739
  };
5575
5740
  return {
5576
5741
  /** 保存并退出编辑模式 */
5577
5742
  saveAndStop: () => {
5578
5743
  stateManager.persistToEntity(positions, headings, pitches, rolls, fovs);
5579
- const startPoint = {
5580
- position: positions[0]
5581
- };
5582
- let altitudeMode;
5744
+ try {
5745
+ entity.properties._altitudeMode = currentAltitudeMode;
5746
+ entity.properties._defaultAltitude = currentDefaultAltitude;
5747
+ entity.properties._useGlobalHeightFlags = useGlobalHeightFlags.slice();
5748
+ } catch {
5749
+ }
5750
+ let altitudeMode = currentAltitudeMode;
5583
5751
  let climbHeight;
5584
- let hasHiddenClimb;
5585
5752
  try {
5586
5753
  const props = entity.properties;
5587
- altitudeMode = props?._altitudeMode?.getValue?.() ?? props?._altitudeMode;
5588
5754
  climbHeight = props?._climbHeight?.getValue?.() ?? props?._climbHeight;
5589
- hasHiddenClimb = props?._hasHiddenClimb?.getValue?.() ?? props?._hasHiddenClimb;
5590
5755
  } catch {
5591
5756
  }
5592
- const waypointData = positions.map((position, index) => ({
5593
- position,
5594
- heading: headings[index] ?? 0,
5595
- pitch: pitches[index] ?? -10,
5596
- roll: rolls[index] ?? 0,
5597
- fov: fovs[index] ?? 50,
5598
- index,
5599
- distance: calculatePathDistance(CesiumNS, positions, index, hiddenClimbIndex)
5600
- })).filter((wp) => wp.index >= 2);
5757
+ const waypointData = positions.map((position, index) => {
5758
+ let ellipsoidHeight = 0;
5759
+ try {
5760
+ const carto = C.Cartographic.fromCartesian(position);
5761
+ ellipsoidHeight = carto.height ?? 0;
5762
+ } catch {
5763
+ }
5764
+ const terrainHeight = terrainHeightsCache.get(index) ?? 0;
5765
+ const relativeHeight = calculateRelativeHeight(
5766
+ currentAltitudeMode,
5767
+ ellipsoidHeight,
5768
+ startPointAltitude,
5769
+ terrainHeight
5770
+ );
5771
+ return {
5772
+ position,
5773
+ heading: headings[index] ?? 0,
5774
+ pitch: pitches[index] ?? -10,
5775
+ roll: rolls[index] ?? 0,
5776
+ fov: fovs[index] ?? 50,
5777
+ index,
5778
+ distance: calculatePathDistance(CesiumNS, positions, index),
5779
+ ellipsoidHeight,
5780
+ relativeHeight,
5781
+ useGlobalHeight: useGlobalHeightFlags[index] ?? false
5782
+ // 🆕 添加 useGlobalHeight
5783
+ };
5784
+ });
5601
5785
  cleanupSession();
5602
5786
  return {
5603
5787
  entity,
5604
5788
  positions: positions.slice(),
5605
- startPoint,
5606
5789
  climbHeight,
5607
5790
  waypointData,
5608
5791
  altitudeMode,
5609
- hasHiddenClimb
5792
+ defaultAltitude: currentDefaultAltitude
5610
5793
  };
5611
5794
  },
5612
5795
  /** 不保存退出(回滚到进入编辑时的状态) */
@@ -5829,45 +6012,32 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5829
6012
  */
5830
6013
  getCursorPose: () => airplaneCursor?.getPose(),
5831
6014
  /**
5832
- * 🆕 动态更新爬升高度(climbHeight)
5833
- * 更新隐藏爬升点(index 1)的高度,实现安全飞行高度的动态调节
5834
- * @param climbHeight 新的爬升高度(米)
6015
+ * 🆕 更新爬升高度(climbHeight)
6016
+ * 更新实体属性中的 _climbHeight 值
6017
+ * @param newClimbHeight 新的爬升高度(米)
5835
6018
  * @returns 是否更新成功
5836
6019
  */
5837
- updateClimbHeight: function(climbHeight) {
5838
- if (hiddenClimbIndex !== 1 || positions.length < 2) {
5839
- console.warn("[updateClimbHeight] No hidden climb point exists");
5840
- return false;
5841
- }
6020
+ updateClimbHeight: function(newClimbHeight) {
5842
6021
  try {
5843
- const startPos = positions[0];
5844
- const startCarto = C.Cartographic.fromCartesian(startPos);
5845
- const newAltitude = startCarto.height + climbHeight;
5846
- const success = this.updateWaypointAltitude(1, newAltitude);
5847
- if (success) {
5848
- try {
5849
- entity.properties._climbHeight = climbHeight;
5850
- } catch {
5851
- }
5852
- console.log("[updateClimbHeight] \u2705 \u722C\u5347\u9AD8\u5EA6\u5DF2\u66F4\u65B0:", climbHeight, "\u7C73");
5853
- }
5854
- return success;
6022
+ entity.properties._climbHeight = newClimbHeight;
6023
+ console.log("[updateClimbHeight] \u2705 \u722C\u5347\u9AD8\u5EA6\u5DF2\u66F4\u65B0:", newClimbHeight, "\u7C73");
6024
+ return true;
5855
6025
  } catch (error) {
5856
6026
  console.error("[updateClimbHeight] Error:", error);
5857
6027
  return false;
5858
6028
  }
5859
6029
  },
5860
6030
  /**
5861
- * 🆕 获取起飞点信息
5862
- * @returns 起飞点的经纬度和高度
6031
+ * 🆕 获取第一个航点信息
6032
+ * @returns 第一个航点的经纬度和高度
5863
6033
  */
5864
6034
  getStartPoint: () => {
5865
6035
  if (positions.length < 1) return null;
5866
6036
  try {
5867
- const startPos = positions[0];
5868
- const carto = C.Cartographic.fromCartesian(startPos);
6037
+ const firstPos = positions[0];
6038
+ const carto = C.Cartographic.fromCartesian(firstPos);
5869
6039
  return {
5870
- position: startPos,
6040
+ position: firstPos,
5871
6041
  latitude: C.Math.toDegrees(carto.latitude),
5872
6042
  longitude: C.Math.toDegrees(carto.longitude),
5873
6043
  altitude: carto.height
@@ -5875,6 +6045,123 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5875
6045
  } catch {
5876
6046
  return null;
5877
6047
  }
6048
+ },
6049
+ /**
6050
+ * 🆕 获取当前高度模式
6051
+ */
6052
+ getAltitudeMode: () => currentAltitudeMode,
6053
+ /**
6054
+ * 🆕 获取当前默认高度
6055
+ */
6056
+ getDefaultAltitude: () => currentDefaultAltitude,
6057
+ /**
6058
+ * 🆕 更新默认高度
6059
+ */
6060
+ updateDefaultAltitude: (altitude) => {
6061
+ currentDefaultAltitude = altitude;
6062
+ quickEditOptions.climbHeight = altitude;
6063
+ try {
6064
+ entity.properties._defaultAltitude = altitude;
6065
+ } catch {
6066
+ }
6067
+ console.log("[updateDefaultAltitude] \u2705 \u9ED8\u8BA4\u9AD8\u5EA6\u5DF2\u66F4\u65B0:", altitude, "\u7C73");
6068
+ },
6069
+ /**
6070
+ * 🆕 切换高度计算模式(异步,会重算所有航点高度)
6071
+ * @param newMode 新的高度模式
6072
+ * @param newDefaultAltitude 新的默认高度(可选)
6073
+ * @param options 选项(terrainProvider, onProgress)
6074
+ * @returns 切换结果 { success: boolean; error?: string }
6075
+ */
6076
+ changeAltitudeMode: async (newMode, newDefaultAltitude, options2) => {
6077
+ try {
6078
+ const onProgress = options2?.onProgress;
6079
+ if (newMode === "relativeToGround") {
6080
+ const terrainProvider = options2?.terrainProvider ?? viewer.terrainProvider;
6081
+ const C_any = C;
6082
+ if (!terrainProvider || C_any.EllipsoidTerrainProvider && terrainProvider instanceof C_any.EllipsoidTerrainProvider) {
6083
+ console.error("[changeAltitudeMode] \u274C relativeToGround \u6A21\u5F0F\u9700\u8981\u6709\u6548\u7684 terrain \u56FE\u5C42");
6084
+ onProgress?.("error");
6085
+ return {
6086
+ success: false,
6087
+ error: "Terrain provider required for relativeToGround mode"
6088
+ };
6089
+ }
6090
+ }
6091
+ onProgress?.("loading");
6092
+ const oldMode = currentAltitudeMode;
6093
+ if (newDefaultAltitude !== void 0) {
6094
+ currentDefaultAltitude = newDefaultAltitude;
6095
+ quickEditOptions.climbHeight = newDefaultAltitude;
6096
+ }
6097
+ if (newMode === "relativeToGround" || oldMode === "relativeToGround") {
6098
+ const terrainHeights = await queryTerrainHeights(CesiumNS, viewer, positions);
6099
+ terrainHeights.forEach((height, index) => {
6100
+ terrainHeightsCache.set(index, height);
6101
+ });
6102
+ console.log("[changeAltitudeMode] \u5DF2\u67E5\u8BE2\u5730\u5F62\u9AD8\u5EA6:", terrainHeights);
6103
+ }
6104
+ if (newMode !== oldMode) {
6105
+ console.log("[changeAltitudeMode] \u5207\u6362\u9AD8\u5EA6\u6A21\u5F0F:", oldMode, "->", newMode);
6106
+ for (let i = 0; i < positions.length; i++) {
6107
+ const currentPos = positions[i];
6108
+ const carto = C.Cartographic.fromCartesian(currentPos);
6109
+ const currentAbsoluteHeight = carto.height ?? 0;
6110
+ const oldTerrainHeight = terrainHeightsCache.get(i) ?? 0;
6111
+ const relativeHeight = calculateRelativeHeight(
6112
+ oldMode,
6113
+ currentAbsoluteHeight,
6114
+ startPointAltitude,
6115
+ oldTerrainHeight
6116
+ );
6117
+ const newTerrainHeight = terrainHeightsCache.get(i) ?? 0;
6118
+ const newAbsoluteHeight = calculateAbsoluteHeight(
6119
+ newMode,
6120
+ relativeHeight,
6121
+ startPointAltitude,
6122
+ newTerrainHeight
6123
+ );
6124
+ const newPosition = C.Cartesian3.fromRadians(
6125
+ carto.longitude,
6126
+ carto.latitude,
6127
+ newAbsoluteHeight
6128
+ );
6129
+ positions[i] = newPosition;
6130
+ if (handles[i]) {
6131
+ try {
6132
+ handles[i].position = newPosition;
6133
+ } catch {
6134
+ }
6135
+ }
6136
+ try {
6137
+ vertexLabelManager.updateLabelPosition(i, newPosition);
6138
+ } catch {
6139
+ }
6140
+ try {
6141
+ createOrUpdateMarkerForIndex(i);
6142
+ } catch {
6143
+ }
6144
+ }
6145
+ currentAltitudeMode = newMode;
6146
+ quickEditOptions.altitudeMode = newMode;
6147
+ try {
6148
+ entity.properties._altitudeMode = newMode;
6149
+ entity.properties._defaultAltitude = currentDefaultAltitude;
6150
+ } catch {
6151
+ }
6152
+ entity.polyline.positions = new C.CallbackProperty(() => positions.slice(), false);
6153
+ }
6154
+ console.log("[changeAltitudeMode] \u2705 \u9AD8\u5EA6\u6A21\u5F0F\u5207\u6362\u5B8C\u6210:", {
6155
+ mode: newMode,
6156
+ defaultAltitude: currentDefaultAltitude
6157
+ });
6158
+ onProgress?.("done");
6159
+ return { success: true };
6160
+ } catch (error) {
6161
+ console.error("[changeAltitudeMode] Error:", error);
6162
+ options2?.onProgress?.("error");
6163
+ return { success: false, error: String(error) };
6164
+ }
5878
6165
  }
5879
6166
  };
5880
6167
  }
@@ -5884,221 +6171,18 @@ function fovToFocalLength(fovDeg) {
5884
6171
  return sensorWidth / (2 * Math.tan(fovRad / 2));
5885
6172
  }
5886
6173
 
5887
- // src/core/path-manager/ArrowShape.ts
5888
- var ArrowShape = class {
5889
- constructor(CesiumNS, opts = {}) {
5890
- this.CesiumNS = CesiumNS;
5891
- __publicField(this, "height");
5892
- __publicField(this, "shaftHeight");
5893
- __publicField(this, "headHeight");
5894
- __publicField(this, "shaftRadius");
5895
- __publicField(this, "headRadius");
5896
- __publicField(this, "baseRadius");
5897
- __publicField(this, "color");
5898
- __publicField(this, "shaftAlpha");
5899
- __publicField(this, "headAlpha");
5900
- __publicField(this, "baseAlpha");
5901
- __publicField(this, "outline");
5902
- __publicField(this, "outlineColor");
5903
- // 动态缩放相关
5904
- __publicField(this, "dynamicScale");
5905
- __publicField(this, "viewer");
5906
- __publicField(this, "referenceDistance");
5907
- __publicField(this, "minScale");
5908
- __publicField(this, "maxScale");
5909
- __publicField(this, "cachedScaleFactor", 1);
5910
- const C = this.CesiumNS;
5911
- this.shaftHeight = opts.shaftHeight ?? 17;
5912
- this.headHeight = opts.headHeight ?? 8;
5913
- this.height = opts.height ?? this.shaftHeight + this.headHeight;
5914
- this.shaftRadius = opts.shaftRadius ?? 3;
5915
- this.headRadius = opts.headRadius ?? 6;
5916
- this.baseRadius = opts.baseRadius ?? 8;
5917
- this.color = opts.color ? C.Color.fromCssColorString(opts.color) : C.Color.fromCssColorString("#FFD700");
5918
- this.shaftAlpha = opts.shaftAlpha ?? 1;
5919
- this.headAlpha = opts.headAlpha ?? 1;
5920
- this.baseAlpha = opts.baseAlpha ?? 0.65;
5921
- this.outline = opts.outline ?? false;
5922
- this.outlineColor = opts.outlineColor ? C.Color.fromCssColorString(opts.outlineColor) : this.color.darken(0.3, new C.Color());
5923
- this.dynamicScale = opts.dynamicScale ?? false;
5924
- this.viewer = opts.viewer;
5925
- this.referenceDistance = opts.referenceDistance ?? 500;
5926
- this.minScale = opts.minScale ?? 0.3;
5927
- this.maxScale = opts.maxScale ?? 3;
5928
- }
5929
- /**
5930
- * Create arrow entities at given position
5931
- * Returns array of 3 entities: [shaft, head, base]
5932
- *
5933
- * @param layer - Cesium CustomDataSource to add entities to
5934
- * @param position - Ground position (Cartesian3)
5935
- * @param baseHeight - Ground height in meters
5936
- * @param properties - Additional properties for entities
5937
- * @returns Array of created entities [shaft, head, base]
5938
- */
5939
- createEntities(layer, position, baseHeight, properties) {
5940
- const C = this.CesiumNS;
5941
- const entities = [];
5942
- const carto = C.Cartographic.fromCartesian(position);
5943
- const lon = C.Math.toDegrees(carto.longitude);
5944
- const lat = C.Math.toDegrees(carto.latitude);
5945
- const computeScaleFactor = () => {
5946
- if (!this.dynamicScale || !this.viewer) return 1;
5947
- try {
5948
- const cameraPos = this.viewer.camera.positionWC;
5949
- const distance = C.Cartesian3.distance(cameraPos, position);
5950
- const rawScale = distance / this.referenceDistance;
5951
- return Math.max(this.minScale, Math.min(this.maxScale, rawScale));
5952
- } catch {
5953
- return 1;
5954
- }
5955
- };
5956
- if (this.dynamicScale && this.viewer) {
5957
- this.viewer.scene.preRender.addEventListener(() => {
5958
- this.cachedScaleFactor = computeScaleFactor();
5959
- });
5960
- }
5961
- const hpr = new C.HeadingPitchRoll(0, 0, 0);
5962
- const baseOrientation = C.Transforms.headingPitchRollQuaternion(position, hpr);
5963
- const fixedOrientation = new C.ConstantProperty(baseOrientation);
5964
- const getShaftPosition = () => {
5965
- const scale = this.cachedScaleFactor;
5966
- return C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight * scale / 2);
5967
- };
5968
- const getHeadPosition = () => {
5969
- const scale = this.cachedScaleFactor;
5970
- return C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight * scale + this.headHeight * scale / 2);
5971
- };
5972
- const shaftEntity = layer.entities.add({
5973
- position: this.dynamicScale ? new C.CallbackProperty(getShaftPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight / 2)),
5974
- orientation: fixedOrientation,
5975
- cylinder: {
5976
- length: this.dynamicScale ? new C.CallbackProperty(() => this.shaftHeight * this.cachedScaleFactor, false) : this.shaftHeight,
5977
- topRadius: this.dynamicScale ? new C.CallbackProperty(() => this.shaftRadius * this.cachedScaleFactor, false) : this.shaftRadius,
5978
- bottomRadius: this.dynamicScale ? new C.CallbackProperty(() => this.shaftRadius * this.cachedScaleFactor, false) : this.shaftRadius,
5979
- material: this.color.withAlpha(this.shaftAlpha),
5980
- outline: this.outline,
5981
- outlineColor: this.outlineColor,
5982
- outlineWidth: 2
5983
- },
5984
- properties: {
5985
- ...properties,
5986
- _arrowPart: "shaft"
5987
- }
5988
- });
5989
- entities.push(shaftEntity);
5990
- const headEntity = layer.entities.add({
5991
- position: this.dynamicScale ? new C.CallbackProperty(getHeadPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight + this.headHeight / 2)),
5992
- orientation: fixedOrientation,
5993
- cylinder: {
5994
- length: this.dynamicScale ? new C.CallbackProperty(() => this.headHeight * this.cachedScaleFactor, false) : this.headHeight,
5995
- topRadius: 0,
5996
- // Creates cone shape
5997
- bottomRadius: this.dynamicScale ? new C.CallbackProperty(() => this.headRadius * this.cachedScaleFactor, false) : this.headRadius,
5998
- material: this.color.withAlpha(this.headAlpha),
5999
- outline: this.outline,
6000
- outlineColor: this.outlineColor,
6001
- outlineWidth: 2
6002
- },
6003
- properties: {
6004
- ...properties,
6005
- _arrowPart: "head"
6006
- }
6007
- });
6008
- entities.push(headEntity);
6009
- const baseEntity = layer.entities.add({
6010
- position,
6011
- ellipse: {
6012
- semiMajorAxis: this.dynamicScale ? new C.CallbackProperty(() => this.baseRadius * this.cachedScaleFactor, false) : this.baseRadius,
6013
- semiMinorAxis: this.dynamicScale ? new C.CallbackProperty(() => this.baseRadius * this.cachedScaleFactor, false) : this.baseRadius,
6014
- height: baseHeight,
6015
- material: this.color.withAlpha(this.baseAlpha),
6016
- outline: this.outline,
6017
- outlineColor: this.outlineColor,
6018
- outlineWidth: 2
6019
- },
6020
- properties: {
6021
- ...properties,
6022
- _arrowPart: "base"
6023
- }
6024
- });
6025
- entities.push(baseEntity);
6026
- return entities;
6027
- }
6028
- /**
6029
- * Get the color
6030
- */
6031
- getColor() {
6032
- return this.color;
6033
- }
6034
- /**
6035
- * Update arrow color
6036
- */
6037
- setColor(color) {
6038
- const C = this.CesiumNS;
6039
- this.color = C.Color.fromCssColorString(color);
6040
- }
6041
- /**
6042
- * Update dimensions
6043
- */
6044
- setDimensions(opts) {
6045
- if (opts.shaftHeight !== void 0) this.shaftHeight = opts.shaftHeight;
6046
- if (opts.headHeight !== void 0) this.headHeight = opts.headHeight;
6047
- if (opts.shaftRadius !== void 0) this.shaftRadius = opts.shaftRadius;
6048
- if (opts.headRadius !== void 0) this.headRadius = opts.headRadius;
6049
- if (opts.baseRadius !== void 0) this.baseRadius = opts.baseRadius;
6050
- this.height = this.shaftHeight + this.headHeight;
6051
- }
6052
- /**
6053
- * Update transparency
6054
- */
6055
- setAlpha(opts) {
6056
- if (opts.shaftAlpha !== void 0) this.shaftAlpha = opts.shaftAlpha;
6057
- if (opts.headAlpha !== void 0) this.headAlpha = opts.headAlpha;
6058
- if (opts.baseAlpha !== void 0) this.baseAlpha = opts.baseAlpha;
6059
- }
6060
- /**
6061
- * Get current dimensions
6062
- */
6063
- getDimensions() {
6064
- return {
6065
- height: this.height,
6066
- shaftHeight: this.shaftHeight,
6067
- headHeight: this.headHeight,
6068
- shaftRadius: this.shaftRadius,
6069
- headRadius: this.headRadius,
6070
- baseRadius: this.baseRadius
6071
- };
6072
- }
6073
- /**
6074
- * Get current alpha values
6075
- */
6076
- getAlpha() {
6077
- return {
6078
- shaftAlpha: this.shaftAlpha,
6079
- headAlpha: this.headAlpha,
6080
- baseAlpha: this.baseAlpha
6081
- };
6082
- }
6083
- /**
6084
- * Get total height
6085
- */
6086
- getHeight() {
6087
- return this.height;
6088
- }
6089
- };
6090
-
6091
6174
  // src/core/path-manager/startPathDrawing.ts
6092
6175
  function startPathDrawing(CesiumNS, viewer, options, onComplete) {
6093
6176
  const C = CesiumNS;
6094
6177
  const layer = options?.layer ?? new C.CustomDataSource("Paths");
6095
6178
  if (!options?.layer) viewer.dataSources.add(layer);
6096
6179
  const altitudeMode = options?.altitudeMode ?? "absolute";
6097
- const climbHeight = Number(options?.climbHeight) || 100;
6098
- const DEFAULT_MAIN_WIDTH = options?.width ?? 6;
6099
- let startCartographic;
6100
- let hasStart = false;
6180
+ const climbHeight = Number(options?.climbHeight) || 0;
6181
+ const defaultAltitude = options?.defaultAltitude ?? options?.defaultHeight ?? 100;
6182
+ const DEFAULT_MAIN_WIDTH = options?.width ?? 3;
6101
6183
  let createdEntity = void 0;
6184
+ let entityCreated = false;
6185
+ let editSession = void 0;
6102
6186
  const getPositionFromMouse = (movement) => {
6103
6187
  const scene = viewer.scene;
6104
6188
  const winPos = movement?.position ?? movement?.endPosition ?? movement;
@@ -6114,129 +6198,54 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
6114
6198
  }
6115
6199
  return cart;
6116
6200
  };
6117
- const toCartographic = (cart) => {
6118
- try {
6119
- return C.Cartographic.fromCartesian(cart);
6120
- } catch {
6121
- return void 0;
6122
- }
6123
- };
6124
6201
  let handler;
6125
- const positions = [];
6126
- const tempHandles = [];
6127
6202
  handler = new C.ScreenSpaceEventHandler(viewer.scene.canvas);
6128
6203
  handler.setInputAction((movement) => {
6129
- if (hasStart) return;
6130
6204
  const picked = getPositionFromMouse(movement);
6131
6205
  if (!picked) return;
6132
- const carto = toCartographic(picked);
6133
- if (!carto) return;
6134
- startCartographic = new C.Cartographic(carto.longitude, carto.latitude, carto.height);
6135
- const groundPos = C.Cartesian3.fromDegrees(
6136
- C.Math.toDegrees(carto.longitude),
6137
- C.Math.toDegrees(carto.latitude),
6138
- startCartographic.height
6139
- );
6140
- positions.push(groundPos);
6141
- const arrowCarto = toCartographic(groundPos);
6142
- const baseHeight = arrowCarto ? arrowCarto.height : 0;
6143
- const arrowShape = new ArrowShape(CesiumNS, {
6144
- shaftHeight: 12,
6145
- headHeight: 12,
6146
- shaftRadius: 4,
6147
- headRadius: 8,
6148
- baseRadius: 12,
6149
- color: "#FFD700",
6150
- shaftAlpha: 1,
6151
- headAlpha: 1,
6152
- baseAlpha: 0.5,
6153
- // 启用动态缩放,和无人机模型一样根据相机距离调整大小
6154
- dynamicScale: true,
6155
- viewer,
6156
- referenceDistance: 300,
6157
- minScale: 0.3,
6158
- maxScale: 2
6159
- });
6160
- const arrowEntities = arrowShape.createEntities(
6161
- layer,
6162
- groundPos,
6163
- baseHeight,
6164
- { _type: "path-vertex", _vertexIndex: 0, _isStart: true }
6165
- );
6166
- tempHandles.push(...arrowEntities);
6167
- hasStart = true;
6168
- if (climbHeight > 0) {
6169
- const climbPos = C.Cartesian3.fromDegrees(
6170
- C.Math.toDegrees(startCartographic.longitude),
6171
- C.Math.toDegrees(startCartographic.latitude),
6172
- startCartographic.height + climbHeight
6173
- );
6174
- positions.push(climbPos);
6175
- }
6176
- let created;
6177
- try {
6178
- const id = `${options?.idPrefix ?? "path"}-${Date.now().toString(36)}`;
6179
- const pathPositions = new C.CallbackProperty(() => {
6180
- const p = created?.polyline?.positions;
6181
- if (p && typeof p.getValue === "function") {
6182
- try {
6183
- return p.getValue();
6184
- } catch {
6185
- }
6186
- }
6187
- return positions.slice();
6188
- }, false);
6189
- created = layer.entities.add({
6190
- id,
6191
- name: "Path",
6192
- polyline: {
6193
- positions: pathPositions,
6194
- width: DEFAULT_MAIN_WIDTH,
6195
- material: new C.PolylineOutlineMaterialProperty({
6196
- color: C.Color.LIME,
6197
- outlineColor: C.Color.WHITE,
6198
- outlineWidth: 2
6199
- }),
6200
- clampToGround: false
6201
- },
6202
- properties: {
6203
- _altitudeMode: altitudeMode,
6204
- _climbHeight: climbHeight,
6205
- _hasHiddenClimb: climbHeight > 0
6206
- }
6207
- });
6208
- createdEntity = created;
6206
+ if (!entityCreated) {
6207
+ entityCreated = true;
6208
+ let created;
6209
6209
  try {
6210
- tempHandles.forEach((hh, idx) => {
6211
- try {
6212
- hh.properties = {
6213
- ...hh.properties,
6214
- _type: "path-vertex",
6215
- _ownerId: id,
6216
- _vertexIndex: idx,
6217
- _isStart: idx === 0
6218
- };
6219
- } catch {
6210
+ const id = `${options?.idPrefix ?? "path"}-${Date.now().toString(36)}`;
6211
+ const positions = [];
6212
+ const pathPositions = new C.CallbackProperty(() => positions.slice(), false);
6213
+ created = layer.entities.add({
6214
+ id,
6215
+ name: "Path",
6216
+ polyline: {
6217
+ positions: pathPositions,
6218
+ width: DEFAULT_MAIN_WIDTH,
6219
+ material: C.Color.fromCssColorString("#00E676"),
6220
+ clampToGround: false
6221
+ },
6222
+ properties: {
6223
+ _altitudeMode: altitudeMode,
6224
+ _defaultAltitude: defaultAltitude,
6225
+ _climbHeight: climbHeight,
6226
+ _useGlobalHeightFlags: []
6227
+ // 🆕 空数组,航点由编辑模式添加
6220
6228
  }
6221
6229
  });
6222
- } catch {
6223
- }
6224
- } finally {
6225
- try {
6226
- handler?.destroy();
6227
- } catch {
6228
- }
6229
- handler = void 0;
6230
- }
6231
- if (created) {
6232
- try {
6233
- onComplete?.(created);
6234
- } catch {
6230
+ createdEntity = created;
6231
+ } finally {
6232
+ try {
6233
+ handler?.destroy();
6234
+ } catch {
6235
+ }
6236
+ handler = void 0;
6235
6237
  }
6236
- try {
6237
- const auto = options?.autoStartEditing;
6238
- if (auto) {
6239
- const editOptions = {};
6238
+ if (created) {
6239
+ try {
6240
+ onComplete?.(created);
6241
+ } catch {
6242
+ }
6243
+ try {
6244
+ const auto = options?.autoStartEditing;
6245
+ const editOptions = {
6246
+ // 🔧 默认使用自由编辑模式(快速编辑和自由编辑互斥)
6247
+ quickEdit: false
6248
+ };
6240
6249
  if (typeof auto === "object" && auto.preview) {
6241
6250
  editOptions.preview = auto.preview;
6242
6251
  } else {
@@ -6258,30 +6267,35 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
6258
6267
  if (autoOpts.onVertexDeleteDetail) {
6259
6268
  editOptions.onVertexDeleteDetail = autoOpts.onVertexDeleteDetail;
6260
6269
  }
6261
- const editSession = startPathEditing(CesiumNS, viewer, created, editOptions);
6270
+ editOptions.altitudeMode = altitudeMode;
6271
+ editOptions.defaultAltitude = defaultAltitude;
6272
+ editOptions.climbHeight = climbHeight;
6273
+ editSession = startPathEditing(CesiumNS, viewer, created, editOptions);
6274
+ 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");
6262
6275
  if (editSession && options?.onEditingStarted) {
6263
6276
  try {
6264
6277
  options.onEditingStarted(editSession);
6265
6278
  } catch {
6266
6279
  }
6267
6280
  }
6281
+ } catch (error) {
6282
+ console.error("[startPathDrawing] \u8FDB\u5165\u7F16\u8F91\u6A21\u5F0F\u5931\u8D25:", error);
6268
6283
  }
6269
- } catch {
6270
6284
  }
6271
6285
  }
6272
6286
  }, C.ScreenSpaceEventType.LEFT_CLICK);
6273
6287
  return {
6274
6288
  stop: () => {
6275
- for (const h of tempHandles) {
6276
- try {
6277
- layer.entities.remove(h);
6278
- } catch {
6279
- }
6280
- }
6281
6289
  try {
6282
6290
  handler?.destroy();
6283
6291
  } catch {
6284
6292
  }
6293
+ if (editSession) {
6294
+ try {
6295
+ editSession.stop();
6296
+ } catch {
6297
+ }
6298
+ }
6285
6299
  if (createdEntity) {
6286
6300
  try {
6287
6301
  layer.entities.remove(createdEntity);
@@ -6366,77 +6380,6 @@ function parseCoordinate(value) {
6366
6380
  }
6367
6381
  return 0;
6368
6382
  }
6369
- function parseTakeOffRefPoint(takeOffRefPoint, takeOffSecurityHeight, CesiumNS) {
6370
- if (!takeOffRefPoint || typeof takeOffRefPoint !== "string" || takeOffRefPoint.trim() === "") {
6371
- return null;
6372
- }
6373
- const C = CesiumNS;
6374
- let longitude;
6375
- let latitude;
6376
- let height;
6377
- const trimmed = takeOffRefPoint.trim();
6378
- if (trimmed.includes(",")) {
6379
- try {
6380
- const parts = trimmed.split(",").map((p) => p.trim());
6381
- if (parts.length >= 2) {
6382
- latitude = parseFloat(parts[0]);
6383
- longitude = parseFloat(parts[1]);
6384
- height = parts.length >= 3 ? parseFloat(parts[2]) : takeOffSecurityHeight;
6385
- if (isNaN(latitude) || isNaN(longitude) || isNaN(height)) {
6386
- throw new Error("\u65E0\u6CD5\u89E3\u6790\u9017\u53F7\u5206\u9694\u7684\u5750\u6807\u503C");
6387
- }
6388
- const startPosition = C.Cartesian3.fromDegrees(longitude, latitude, height);
6389
- const climbPosition = C.Cartesian3.fromDegrees(
6390
- longitude,
6391
- latitude,
6392
- height + takeOffSecurityHeight
6393
- );
6394
- return {
6395
- startPosition,
6396
- climbPosition
6397
- };
6398
- }
6399
- } catch (error) {
6400
- }
6401
- }
6402
- try {
6403
- const parsed = JSON.parse(takeOffRefPoint);
6404
- if (typeof parsed !== "object" || parsed === null) {
6405
- return null;
6406
- }
6407
- if (typeof parsed.longitude === "number") {
6408
- longitude = parsed.longitude;
6409
- } else if (typeof parsed.lon === "number") {
6410
- longitude = parsed.lon;
6411
- } else {
6412
- return null;
6413
- }
6414
- if (typeof parsed.latitude === "number") {
6415
- latitude = parsed.latitude;
6416
- } else if (typeof parsed.lat === "number") {
6417
- latitude = parsed.lat;
6418
- } else {
6419
- return null;
6420
- }
6421
- if (typeof parsed.height === "number") {
6422
- height = parsed.height;
6423
- } else {
6424
- height = takeOffSecurityHeight;
6425
- }
6426
- const startPosition = C.Cartesian3.fromDegrees(longitude, latitude, height);
6427
- const climbPosition = C.Cartesian3.fromDegrees(
6428
- longitude,
6429
- latitude,
6430
- height + takeOffSecurityHeight
6431
- );
6432
- return {
6433
- startPosition,
6434
- climbPosition
6435
- };
6436
- } catch (error) {
6437
- return null;
6438
- }
6439
- }
6440
6383
  function convertSinoflyWayline(data, options) {
6441
6384
  const {
6442
6385
  CesiumNS,
@@ -6459,42 +6402,12 @@ function convertSinoflyWayline(data, options) {
6459
6402
  if (data.waylineId === void 0 || data.waylineId === null) {
6460
6403
  throw new Error("[sinoflyAdapter] waylineId \u4E0D\u80FD\u4E3A\u7A7A");
6461
6404
  }
6462
- const takeOffInfo = parseTakeOffRefPoint(
6463
- data.takeOffRefPoint,
6464
- data.takeOffSecurityHeight,
6465
- CesiumNS
6466
- );
6467
6405
  let waypoints = data.waypointInfo;
6468
6406
  if (filterWaypoint) {
6469
6407
  waypoints = waypoints.filter(filterWaypoint);
6470
6408
  }
6471
- if (sortByIndex) {
6472
- waypoints = [...waypoints].sort((a, b) => a.index - b.index);
6473
- }
6474
- const finalWaypointData = [];
6475
- if (takeOffInfo) {
6476
- finalWaypointData.push({
6477
- position: takeOffInfo.startPosition,
6478
- heading: defaultHeading,
6479
- pitch: defaultPitch,
6480
- roll: defaultRoll,
6481
- fov: defaultFov,
6482
- index: 0
6483
- });
6484
- finalWaypointData.push({
6485
- position: takeOffInfo.climbPosition,
6486
- heading: defaultHeading,
6487
- pitch: defaultPitch,
6488
- roll: defaultRoll,
6489
- fov: defaultFov,
6490
- index: 1
6491
- });
6492
- }
6493
- let waypointStartIndex;
6494
- if (takeOffInfo) {
6495
- waypointStartIndex = 2;
6496
- } else {
6497
- waypointStartIndex = 0;
6409
+ if (sortByIndex) {
6410
+ waypoints = [...waypoints].sort((a, b) => a.index - b.index);
6498
6411
  }
6499
6412
  const convertedWaypoints = waypoints.map((wp, idx) => {
6500
6413
  try {
@@ -6531,15 +6444,16 @@ function convertSinoflyWayline(data, options) {
6531
6444
  pitch,
6532
6445
  roll,
6533
6446
  fov,
6534
- index: waypointStartIndex + idx
6535
- // 重新分配 index,从 waypointStartIndex 开始
6447
+ index: idx,
6448
+ // 0 开始分配 index
6449
+ useGlobalHeight: false
6450
+ // 🆕 从服务端加载的航点默认使用实际高度
6536
6451
  };
6537
6452
  } catch (error) {
6538
6453
  throw new Error(`\u822A\u70B9 ${idx} \u8F6C\u6362\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
6539
6454
  }
6540
6455
  });
6541
- const waypointData = [...finalWaypointData, ...convertedWaypoints];
6542
- if (!Array.isArray(waypointData)) {
6456
+ if (!Array.isArray(convertedWaypoints)) {
6543
6457
  throw new Error("[sinoflyAdapter] waypointData \u4E0D\u662F\u6570\u7EC4");
6544
6458
  }
6545
6459
  const metadata = {
@@ -6550,8 +6464,8 @@ function convertSinoflyWayline(data, options) {
6550
6464
  autoFlightSpeed: data.autoFlightSpeed,
6551
6465
  distance: data.distance,
6552
6466
  duration: data.duration,
6553
- takeOffSecurityHeight: data.takeOffSecurityHeight,
6554
- hasTakeOffRefPoint: !!takeOffInfo
6467
+ climbHeight: data.takeOffSecurityHeight
6468
+ // 使用 climbHeight 作为字段名
6555
6469
  };
6556
6470
  if (data.stationId !== void 0) metadata.stationId = data.stationId;
6557
6471
  if (data.subarrayId !== void 0) metadata.subarrayId = data.subarrayId;
@@ -6561,7 +6475,7 @@ function convertSinoflyWayline(data, options) {
6561
6475
  id: `wayline-${data.waylineId}`,
6562
6476
  name: data.waylineName || `\u822A\u7EBF-${data.waylineId}`,
6563
6477
  description: `\u822A\u7EBFID: ${data.waylineId}, \u7C7B\u578B: ${data.waylineType}, \u822A\u70B9\u6570: ${data.waypointNumber}`,
6564
- waypointData,
6478
+ waypointData: convertedWaypoints,
6565
6479
  metadata
6566
6480
  };
6567
6481
  }
@@ -6592,7 +6506,6 @@ function renderFlightPath(CesiumNS, viewer, options) {
6592
6506
  let entityId;
6593
6507
  let entityName;
6594
6508
  let entityDescription;
6595
- let hasHiddenClimb;
6596
6509
  let altitudeMode;
6597
6510
  let climbHeight;
6598
6511
  try {
@@ -6608,12 +6521,8 @@ function renderFlightPath(CesiumNS, viewer, options) {
6608
6521
  entityId = options.id ?? converted.id ?? `wayline-${sinoflyData.waylineId}`;
6609
6522
  entityName = options.name ?? converted.name;
6610
6523
  entityDescription = options.description ?? converted.description;
6611
- const sortedWaypoints2 = [...waypointData].sort((a, b) => a.index - b.index);
6612
- const hasIndex1 = sortedWaypoints2.some((wp) => wp.index === 1);
6613
- const hasTakeOffRefPoint = converted.metadata?.hasTakeOffRefPoint ?? !!sinoflyData.takeOffRefPoint;
6614
- hasHiddenClimb = options.hasHiddenClimb ?? (hasTakeOffRefPoint && hasIndex1);
6615
6524
  altitudeMode = options.altitudeMode ?? converted.metadata?.altitudeMode;
6616
- climbHeight = options.climbHeight ?? converted.metadata?.climbHeight ?? converted.metadata?.takeOffSecurityHeight;
6525
+ climbHeight = options.climbHeight ?? converted.metadata?.climbHeight;
6617
6526
  } catch (error) {
6618
6527
  console.error("[renderFlightPath] Sinofly \u6570\u636E\u8F6C\u6362\u5931\u8D25:", error);
6619
6528
  throw new Error(`Sinofly \u6570\u636E\u8F6C\u6362\u5931\u8D25: ${error instanceof Error ? error.message : String(error)}`);
@@ -6635,66 +6544,25 @@ function renderFlightPath(CesiumNS, viewer, options) {
6635
6544
  layer = new C.CustomDataSource(`flight-path-datasource-${Date.now()}`);
6636
6545
  viewer.dataSources.add(layer);
6637
6546
  }
6638
- const hiddenClimbIndex = hasHiddenClimb && positions.length >= 2 ? 1 : void 0;
6639
- const hasStartPoint = positions.length > 0 && sortedWaypoints.some((wp) => wp.index === 0);
6547
+ const hiddenClimbIndex = void 0;
6548
+ const useGlobalHeightFlags = sortedWaypoints.map((wp) => wp.useGlobalHeight ?? false);
6640
6549
  const entity = layer.entities.add({
6641
6550
  id: entityId,
6642
6551
  name: entityName,
6643
6552
  description: entityDescription,
6644
6553
  polyline: {
6645
6554
  positions: positions.slice(),
6646
- width: options.style?.width ?? 8,
6647
- material: options.style?.material ?? options.style?.color ?? new C.PolylineOutlineMaterialProperty({
6648
- color: C.Color.GREEN,
6649
- outlineColor: C.Color.WHITE,
6650
- outlineWidth: 3
6651
- }),
6555
+ width: options.style?.width ?? 3,
6556
+ material: options.style?.material ?? options.style?.color ?? C.Color.fromCssColorString("#00E676"),
6652
6557
  clampToGround: false
6653
6558
  },
6654
6559
  properties: {
6655
6560
  _altitudeMode: altitudeMode,
6656
6561
  _climbHeight: climbHeight,
6657
- _hasHiddenClimb: hasHiddenClimb
6562
+ _useGlobalHeightFlags: useGlobalHeightFlags
6563
+ // 🆕 保存 useGlobalHeightFlags
6658
6564
  }
6659
6565
  });
6660
- const shouldShowArrow = hasStartPoint && hasHiddenClimb;
6661
- if (shouldShowArrow) {
6662
- try {
6663
- const startPosition = positions[0];
6664
- const carto = C.Cartographic.fromCartesian(startPosition);
6665
- const baseHeight = carto ? carto.height : 0;
6666
- const arrowShape = new ArrowShape(CesiumNS, {
6667
- shaftHeight: 12,
6668
- headHeight: 12,
6669
- shaftRadius: 4,
6670
- headRadius: 8,
6671
- baseRadius: 12,
6672
- color: "#FFD700",
6673
- shaftAlpha: 0.98,
6674
- headAlpha: 0.98,
6675
- baseAlpha: 0.65,
6676
- // 启用动态缩放
6677
- dynamicScale: true,
6678
- viewer,
6679
- referenceDistance: 300,
6680
- minScale: 0.3,
6681
- maxScale: 2
6682
- });
6683
- arrowShape.createEntities(
6684
- layer,
6685
- startPosition,
6686
- baseHeight,
6687
- {
6688
- _type: "path-vertex",
6689
- _ownerId: entityId,
6690
- _vertexIndex: 0,
6691
- _isStart: true
6692
- }
6693
- );
6694
- } catch (error) {
6695
- console.error("Failed to create start arrow:", error);
6696
- }
6697
- }
6698
6566
  try {
6699
6567
  const vertexLabelManager = new VertexLabelManager(
6700
6568
  CesiumNS,
@@ -6735,8 +6603,6 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6735
6603
  } catch (e) {
6736
6604
  console.warn("[renderFlightPathPreview] \u65E0\u6CD5\u83B7\u53D6\u822A\u70B9\u6570\u636E:", e);
6737
6605
  }
6738
- const hasHiddenClimb = entity.properties?._hasHiddenClimb?.getValue?.() ?? false;
6739
- const hiddenClimbIndex = hasHiddenClimb ? 1 : void 0;
6740
6606
  const updateWaypointHighlight = (newIndex) => {
6741
6607
  if (!vertexLabelManager || positions.length === 0) return;
6742
6608
  const editedIndices = /* @__PURE__ */ new Set();
@@ -6756,14 +6622,7 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6756
6622
  const ownerId = properties._ownerId?.getValue?.(C.JulianDate.now());
6757
6623
  const vertexIndex = properties._vertexIndex?.getValue?.(C.JulianDate.now());
6758
6624
  if (type === "vertex-label" && ownerId === entity.id && vertexIndex !== void 0) {
6759
- let displayIndex = vertexIndex;
6760
- if (hiddenClimbIndex === 1) {
6761
- if (vertexIndex >= 2) {
6762
- displayIndex = vertexIndex - 2;
6763
- }
6764
- } else {
6765
- displayIndex = vertexIndex;
6766
- }
6625
+ const displayIndex = vertexIndex;
6767
6626
  selectedWaypointIndex = vertexIndex;
6768
6627
  updateWaypointHighlight(selectedWaypointIndex);
6769
6628
  if (options.onWaypointClick) {
@@ -6781,18 +6640,10 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6781
6640
  updateWaypointHighlight(null);
6782
6641
  return;
6783
6642
  }
6784
- let actualIndex = index;
6785
- if (hiddenClimbIndex === 1) {
6786
- actualIndex = index + 2;
6787
- }
6788
- selectedWaypointIndex = actualIndex;
6643
+ selectedWaypointIndex = index;
6789
6644
  updateWaypointHighlight(selectedWaypointIndex);
6790
6645
  },
6791
6646
  getSelectedWaypoint: () => {
6792
- if (selectedWaypointIndex === null) return null;
6793
- if (hiddenClimbIndex === 1) {
6794
- return selectedWaypointIndex >= 2 ? selectedWaypointIndex - 2 : null;
6795
- }
6796
6647
  return selectedWaypointIndex;
6797
6648
  },
6798
6649
  destroy: () => {
@@ -6802,6 +6653,299 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6802
6653
  return { entity, controller };
6803
6654
  }
6804
6655
 
6656
+ // src/core/path-manager/FlightSimulator.ts
6657
+ var FlightSimulator = class {
6658
+ constructor(CesiumNS, viewer, options) {
6659
+ __publicField(this, "CesiumNS");
6660
+ __publicField(this, "viewer");
6661
+ __publicField(this, "options");
6662
+ __publicField(this, "state", "idle");
6663
+ __publicField(this, "progress", 0);
6664
+ __publicField(this, "waypoints", []);
6665
+ __publicField(this, "totalDistance", 0);
6666
+ __publicField(this, "currentDistance", 0);
6667
+ __publicField(this, "airplaneCursor");
6668
+ __publicField(this, "trackEntity");
6669
+ __publicField(this, "passedTrackEntity");
6670
+ __publicField(this, "animationFrameId");
6671
+ __publicField(this, "lastTimestamp");
6672
+ __publicField(this, "onStateChange", new Emitter());
6673
+ /**
6674
+ * 动画循环
6675
+ */
6676
+ __publicField(this, "animate", (timestamp) => {
6677
+ if (this.state !== "playing") return;
6678
+ if (this.lastTimestamp === void 0) {
6679
+ this.lastTimestamp = timestamp;
6680
+ }
6681
+ const deltaTime = (timestamp - this.lastTimestamp) / 1e3;
6682
+ this.lastTimestamp = timestamp;
6683
+ this.currentDistance += this.options.speed * deltaTime;
6684
+ if (this.currentDistance >= this.totalDistance) {
6685
+ if (this.options.loop) {
6686
+ this.currentDistance = 0;
6687
+ } else {
6688
+ this.currentDistance = this.totalDistance;
6689
+ this.setState("stopped");
6690
+ return;
6691
+ }
6692
+ }
6693
+ this.progress = this.totalDistance > 0 ? this.currentDistance / this.totalDistance : 0;
6694
+ if (this.airplaneCursor) {
6695
+ const position = this.getCurrentPosition();
6696
+ const { heading, pitch, roll } = this.getCurrentPose();
6697
+ this.airplaneCursor.setPose(position, heading, pitch, roll);
6698
+ }
6699
+ this.viewer.scene.requestRender();
6700
+ this.animationFrameId = requestAnimationFrame(this.animate);
6701
+ });
6702
+ this.CesiumNS = CesiumNS;
6703
+ this.viewer = viewer;
6704
+ this.options = {
6705
+ waylineData: options.waylineData,
6706
+ speed: options.speed ?? 10,
6707
+ loop: options.loop ?? false,
6708
+ showFrustum: options.showFrustum ?? true,
6709
+ layer: options.layer,
6710
+ trackHighlight: options.trackHighlight
6711
+ };
6712
+ this.initWaypoints();
6713
+ this.createEntities();
6714
+ }
6715
+ /**
6716
+ * 初始化航点数据
6717
+ */
6718
+ initWaypoints() {
6719
+ const C = this.CesiumNS;
6720
+ const converted = convertSinoflyWayline(this.options.waylineData, { CesiumNS: this.CesiumNS });
6721
+ if (!converted.waypointData || converted.waypointData.length < 2) {
6722
+ console.warn("[FlightSimulator] \u822A\u70B9\u6570\u91CF\u4E0D\u8DB3");
6723
+ return;
6724
+ }
6725
+ const tempWaypoints = converted.waypointData.map((wp) => ({
6726
+ cartesian: wp.position,
6727
+ heading: wp.heading ?? 0,
6728
+ pitch: wp.pitch ?? 0,
6729
+ roll: wp.roll ?? 0,
6730
+ cumulativeDistance: 0
6731
+ }));
6732
+ let cumulativeDistance = 0;
6733
+ for (let i = 0; i < tempWaypoints.length; i++) {
6734
+ if (i > 0) {
6735
+ const prevCartesian = tempWaypoints[i - 1].cartesian;
6736
+ const currCartesian = tempWaypoints[i].cartesian;
6737
+ cumulativeDistance += C.Cartesian3.distance(prevCartesian, currCartesian);
6738
+ }
6739
+ tempWaypoints[i].cumulativeDistance = cumulativeDistance;
6740
+ }
6741
+ this.waypoints = tempWaypoints;
6742
+ this.totalDistance = cumulativeDistance;
6743
+ }
6744
+ /**
6745
+ * 创建可视化实体
6746
+ */
6747
+ createEntities() {
6748
+ if (this.waypoints.length === 0) return;
6749
+ const firstWaypoint = this.waypoints[0];
6750
+ this.airplaneCursor = new AirplaneCursor(
6751
+ this.CesiumNS,
6752
+ this.viewer,
6753
+ firstWaypoint.cartesian,
6754
+ {
6755
+ // 禁用键盘控制(仿真模式下不需要手动控制)
6756
+ stepMeters: 0,
6757
+ angleStepDeg: 0,
6758
+ fovDeg: 50
6759
+ }
6760
+ );
6761
+ this.airplaneCursor.setPose(
6762
+ firstWaypoint.cartesian,
6763
+ firstWaypoint.heading,
6764
+ firstWaypoint.pitch,
6765
+ firstWaypoint.roll
6766
+ );
6767
+ if (this.options.trackHighlight?.enabled) {
6768
+ this.createTrackHighlight();
6769
+ }
6770
+ }
6771
+ /**
6772
+ * 创建航迹高亮
6773
+ */
6774
+ createTrackHighlight() {
6775
+ const C = this.CesiumNS;
6776
+ const entities = this.options.layer?.entities ?? this.viewer.entities;
6777
+ const highlightOpts = this.options.trackHighlight;
6778
+ const allPositions = this.waypoints.map((wp) => wp.cartesian);
6779
+ this.trackEntity = entities.add({
6780
+ id: `flight-simulator-track-${Date.now()}`,
6781
+ polyline: {
6782
+ positions: allPositions,
6783
+ width: highlightOpts.width ?? 4,
6784
+ material: (highlightOpts.color ?? C.Color.CYAN).withAlpha(0.3),
6785
+ clampToGround: false
6786
+ },
6787
+ properties: {
6788
+ _type: "flight-simulator-track"
6789
+ }
6790
+ });
6791
+ this.passedTrackEntity = entities.add({
6792
+ id: `flight-simulator-passed-track-${Date.now()}`,
6793
+ polyline: {
6794
+ positions: new C.CallbackProperty(() => this.getPassedPositions(), false),
6795
+ width: (highlightOpts.width ?? 4) + 2,
6796
+ material: highlightOpts.color ?? C.Color.CYAN,
6797
+ clampToGround: false
6798
+ },
6799
+ properties: {
6800
+ _type: "flight-simulator-passed-track"
6801
+ }
6802
+ });
6803
+ }
6804
+ /**
6805
+ * 获取当前位置
6806
+ */
6807
+ getCurrentPosition() {
6808
+ if (this.waypoints.length === 0) {
6809
+ return this.CesiumNS.Cartesian3.ZERO;
6810
+ }
6811
+ const C = this.CesiumNS;
6812
+ for (let i = 1; i < this.waypoints.length; i++) {
6813
+ const prev = this.waypoints[i - 1];
6814
+ const curr = this.waypoints[i];
6815
+ if (this.currentDistance <= curr.cumulativeDistance) {
6816
+ const segmentStart = prev.cumulativeDistance;
6817
+ const segmentEnd = curr.cumulativeDistance;
6818
+ const segmentLength = segmentEnd - segmentStart;
6819
+ if (segmentLength <= 0) {
6820
+ return prev.cartesian;
6821
+ }
6822
+ const t = (this.currentDistance - segmentStart) / segmentLength;
6823
+ return C.Cartesian3.lerp(prev.cartesian, curr.cartesian, t, new C.Cartesian3());
6824
+ }
6825
+ }
6826
+ return this.waypoints[this.waypoints.length - 1].cartesian;
6827
+ }
6828
+ /**
6829
+ * 获取当前姿态(用于更新 AirplaneCursor)
6830
+ */
6831
+ getCurrentPose() {
6832
+ const C = this.CesiumNS;
6833
+ let heading = 0;
6834
+ let pitch = 0;
6835
+ let roll = 0;
6836
+ for (let i = 1; i < this.waypoints.length; i++) {
6837
+ const prev = this.waypoints[i - 1];
6838
+ const curr = this.waypoints[i];
6839
+ if (this.currentDistance <= curr.cumulativeDistance) {
6840
+ const segmentStart = prev.cumulativeDistance;
6841
+ const segmentEnd = curr.cumulativeDistance;
6842
+ const segmentLength = segmentEnd - segmentStart;
6843
+ if (segmentLength > 0) {
6844
+ const t = (this.currentDistance - segmentStart) / segmentLength;
6845
+ heading = C.Math.lerp(prev.heading, curr.heading, t);
6846
+ pitch = C.Math.lerp(prev.pitch, curr.pitch, t);
6847
+ roll = C.Math.lerp(prev.roll, curr.roll, t);
6848
+ } else {
6849
+ heading = prev.heading;
6850
+ pitch = prev.pitch;
6851
+ roll = prev.roll;
6852
+ }
6853
+ break;
6854
+ }
6855
+ }
6856
+ return { heading, pitch, roll };
6857
+ }
6858
+ /**
6859
+ * 获取已飞过的位置数组
6860
+ */
6861
+ getPassedPositions() {
6862
+ const positions = [];
6863
+ for (let i = 0; i < this.waypoints.length; i++) {
6864
+ const wp = this.waypoints[i];
6865
+ if (wp.cumulativeDistance <= this.currentDistance) {
6866
+ positions.push(wp.cartesian);
6867
+ } else {
6868
+ positions.push(this.getCurrentPosition());
6869
+ break;
6870
+ }
6871
+ }
6872
+ return positions;
6873
+ }
6874
+ /**
6875
+ * 设置状态并发射事件
6876
+ */
6877
+ setState(newState) {
6878
+ if (this.state === newState) return;
6879
+ this.state = newState;
6880
+ this.onStateChange.emit({ state: newState, progress: this.progress });
6881
+ }
6882
+ // ==================== 公共方法 ====================
6883
+ start() {
6884
+ if (this.waypoints.length < 2) {
6885
+ console.warn("[FlightSimulator] \u822A\u70B9\u6570\u91CF\u4E0D\u8DB3\uFF0C\u65E0\u6CD5\u5F00\u59CB\u4EFF\u771F");
6886
+ return;
6887
+ }
6888
+ this.currentDistance = 0;
6889
+ this.progress = 0;
6890
+ this.lastTimestamp = void 0;
6891
+ this.setState("playing");
6892
+ this.animationFrameId = requestAnimationFrame(this.animate);
6893
+ }
6894
+ pause() {
6895
+ if (this.state !== "playing") return;
6896
+ if (this.animationFrameId !== void 0) {
6897
+ cancelAnimationFrame(this.animationFrameId);
6898
+ this.animationFrameId = void 0;
6899
+ }
6900
+ this.lastTimestamp = void 0;
6901
+ this.setState("paused");
6902
+ }
6903
+ resume() {
6904
+ if (this.state !== "paused") return;
6905
+ this.lastTimestamp = void 0;
6906
+ this.setState("playing");
6907
+ this.animationFrameId = requestAnimationFrame(this.animate);
6908
+ }
6909
+ stop() {
6910
+ if (this.animationFrameId !== void 0) {
6911
+ cancelAnimationFrame(this.animationFrameId);
6912
+ this.animationFrameId = void 0;
6913
+ }
6914
+ this.currentDistance = 0;
6915
+ this.progress = 0;
6916
+ this.lastTimestamp = void 0;
6917
+ this.setState("stopped");
6918
+ this.viewer.scene.requestRender();
6919
+ }
6920
+ destroy() {
6921
+ this.stop();
6922
+ const entities = this.options.layer?.entities ?? this.viewer.entities;
6923
+ if (this.airplaneCursor) {
6924
+ this.airplaneCursor.destroy();
6925
+ this.airplaneCursor = void 0;
6926
+ }
6927
+ if (this.trackEntity) {
6928
+ entities.remove(this.trackEntity);
6929
+ this.trackEntity = void 0;
6930
+ }
6931
+ if (this.passedTrackEntity) {
6932
+ entities.remove(this.passedTrackEntity);
6933
+ this.passedTrackEntity = void 0;
6934
+ }
6935
+ this.waypoints = [];
6936
+ this.setState("idle");
6937
+ }
6938
+ setSpeed(speed) {
6939
+ this.options.speed = Math.max(0.1, speed);
6940
+ }
6941
+ getState() {
6942
+ return this.state;
6943
+ }
6944
+ getProgress() {
6945
+ return this.progress;
6946
+ }
6947
+ };
6948
+
6805
6949
  // src/core/CZMLPathManager.ts
6806
6950
  var _CZMLPathManager = class _CZMLPathManager {
6807
6951
  constructor(CesiumNS, viewer) {
@@ -7078,6 +7222,20 @@ var _CZMLPathManager = class _CZMLPathManager {
7078
7222
  options
7079
7223
  );
7080
7224
  }
7225
+ /**
7226
+ * Start flight simulation/preview for a wayline.
7227
+ * Animates an aircraft model along the wayline path with controls for play/pause/stop.
7228
+ * @param options - Configuration for the flight simulator
7229
+ * @returns A controller object with start/pause/resume/stop/destroy methods
7230
+ */
7231
+ startFlightPreview(options) {
7232
+ const simulator = new FlightSimulator(
7233
+ this.CesiumNS,
7234
+ this.viewer,
7235
+ options
7236
+ );
7237
+ return simulator;
7238
+ }
7081
7239
  // ensureLayerForEntity moved into path-editing module
7082
7240
  };
7083
7241
  __publicField(_CZMLPathManager, "SAMPLES_KEY", "__czmlPathSamples__");
@@ -8156,7 +8314,9 @@ var PolygonEditor = class {
8156
8314
  polygon: {
8157
8315
  hierarchy: positions,
8158
8316
  material: faceColor,
8159
- outline: false
8317
+ outline: false,
8318
+ // 🔧 修复:设置 heightReference 使多边形贴合地形
8319
+ heightReference: C.HeightReference.CLAMP_TO_GROUND
8160
8320
  }
8161
8321
  });
8162
8322
  const ring = positions.slice();
@@ -8169,15 +8329,51 @@ var PolygonEditor = class {
8169
8329
  width: 1,
8170
8330
  material: lineColor,
8171
8331
  clampToGround: true
8332
+ // 轮廓线已经设置了 clampToGround
8172
8333
  },
8173
8334
  properties: { _ownerId: id, _type: "polygon-outline" }
8174
8335
  });
8175
8336
  this.applyDashedOutlineStyle(outlineEntity, lineColor, 3, 10);
8176
8337
  const displayText = name.includes("_") ? name.split("_")[1] : name;
8338
+ const firstPoint = points[0];
8339
+ this.createLabelWithTerrainHeight(
8340
+ layer,
8341
+ id,
8342
+ displayText,
8343
+ firstPoint.lon,
8344
+ firstPoint.lat,
8345
+ lineColor
8346
+ );
8347
+ return entity;
8348
+ } catch (err) {
8349
+ console.error(`[PolygonEditor] \u521B\u5EFA\u591A\u8FB9\u5F62\u5931\u8D25:`, err);
8350
+ return null;
8351
+ }
8352
+ }
8353
+ /**
8354
+ * 辅助方法:使用地形采样创建标签
8355
+ * 先尝试获取地形高度,如果失败则使用 heightReference
8356
+ */
8357
+ async createLabelWithTerrainHeight(layer, ownerId, displayText, lon, lat, lineColor) {
8358
+ const C = this.CesiumNS;
8359
+ try {
8360
+ let terrainHeight = 0;
8361
+ const terrainProvider = this.viewer.terrainProvider;
8362
+ if (terrainProvider && typeof C.sampleTerrainMostDetailed === "function") {
8363
+ try {
8364
+ const positions = [C.Cartographic.fromDegrees(lon, lat)];
8365
+ const updatedPositions = await C.sampleTerrainMostDetailed(terrainProvider, positions);
8366
+ if (updatedPositions && updatedPositions[0] && updatedPositions[0].height !== void 0) {
8367
+ terrainHeight = updatedPositions[0].height;
8368
+ }
8369
+ } catch (e) {
8370
+ }
8371
+ }
8372
+ const labelPosition = C.Cartesian3.fromDegrees(lon, lat, terrainHeight + 1);
8177
8373
  layer.entities.add({
8178
- id: `${id}-label`,
8374
+ id: `${ownerId}-label`,
8179
8375
  name: "Polygon Label",
8180
- position: positions[0],
8376
+ position: labelPosition,
8181
8377
  label: {
8182
8378
  text: displayText,
8183
8379
  font: "bold 16px Microsoft YaHei, SimHei, sans-serif",
@@ -8187,14 +8383,14 @@ var PolygonEditor = class {
8187
8383
  style: C.LabelStyle.FILL_AND_OUTLINE,
8188
8384
  verticalOrigin: C.VerticalOrigin.BOTTOM,
8189
8385
  pixelOffset: new C.Cartesian2(0, -10),
8190
- disableDepthTestDistance: Number.POSITIVE_INFINITY
8386
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
8387
+ // 仍然设置 heightReference 作为备用
8388
+ heightReference: C.HeightReference.CLAMP_TO_GROUND
8191
8389
  },
8192
- properties: { _ownerId: id, _type: "polygon-label" }
8390
+ properties: { _ownerId: ownerId, _type: "polygon-label" }
8193
8391
  });
8194
- return entity;
8195
- } catch (err) {
8196
- console.error(`[PolygonEditor] \u521B\u5EFA\u591A\u8FB9\u5F62\u5931\u8D25:`, err);
8197
- return null;
8392
+ } catch (e) {
8393
+ console.warn(`[PolygonEditor] \u521B\u5EFA\u6807\u7B7E\u5931\u8D25:`, e);
8198
8394
  }
8199
8395
  }
8200
8396
  /**
@@ -10729,6 +10925,226 @@ var Selector = class {
10729
10925
  }
10730
10926
  };
10731
10927
 
10928
+ // src/core/PointCloudPicker.ts
10929
+ var PointCloudPicker = class {
10930
+ constructor(CesiumNS, viewer) {
10931
+ this.CesiumNS = CesiumNS;
10932
+ this.viewer = viewer;
10933
+ __publicField(this, "handler");
10934
+ __publicField(this, "markerEntity");
10935
+ __publicField(this, "isPaused", false);
10936
+ }
10937
+ /**
10938
+ * 在指定屏幕位置进行一次点云拾取
10939
+ * @param windowPosition 屏幕坐标 { x, y }
10940
+ * @param options 拾取选项
10941
+ * @returns 拾取结果
10942
+ */
10943
+ pickAt(windowPosition, options) {
10944
+ const C = this.CesiumNS;
10945
+ const scene = this.viewer.scene;
10946
+ const fetchProperties = options?.fetchProperties ?? true;
10947
+ const pointCloudOnly = options?.pointCloudOnly ?? true;
10948
+ const picked = scene.pick(windowPosition);
10949
+ let tileset;
10950
+ let content;
10951
+ let featureId;
10952
+ if (picked) {
10953
+ if (picked.primitive instanceof C.Cesium3DTileset) {
10954
+ tileset = picked.primitive;
10955
+ } else if (picked.tileset instanceof C.Cesium3DTileset) {
10956
+ tileset = picked.tileset;
10957
+ }
10958
+ if (picked.content) {
10959
+ content = picked.content;
10960
+ }
10961
+ if (typeof picked.featureId === "number") {
10962
+ featureId = picked.featureId;
10963
+ }
10964
+ }
10965
+ if (pointCloudOnly && !tileset) {
10966
+ return { success: false, screenPosition: windowPosition };
10967
+ }
10968
+ let position;
10969
+ if (scene.pickPositionSupported) {
10970
+ try {
10971
+ position = scene.pickPosition(windowPosition);
10972
+ } catch {
10973
+ }
10974
+ }
10975
+ if (!position || !C.defined(position)) {
10976
+ try {
10977
+ const ray = this.viewer.camera.getPickRay(windowPosition);
10978
+ if (ray) {
10979
+ position = scene.globe?.pick(ray, scene);
10980
+ }
10981
+ } catch {
10982
+ }
10983
+ }
10984
+ if (!position || !C.defined(position)) {
10985
+ return { success: false, screenPosition: windowPosition, tileset };
10986
+ }
10987
+ let coordinates;
10988
+ try {
10989
+ const cartographic = C.Cartographic.fromCartesian(position);
10990
+ coordinates = {
10991
+ longitude: C.Math.toDegrees(cartographic.longitude),
10992
+ latitude: C.Math.toDegrees(cartographic.latitude),
10993
+ height: cartographic.height
10994
+ };
10995
+ } catch {
10996
+ }
10997
+ let properties;
10998
+ let color;
10999
+ if (fetchProperties && content && featureId !== void 0) {
11000
+ try {
11001
+ const feature = content.getFeature?.(featureId);
11002
+ if (feature) {
11003
+ properties = {};
11004
+ const propertyIds = feature.getPropertyIds?.() ?? [];
11005
+ for (const propId of propertyIds) {
11006
+ try {
11007
+ properties[propId] = feature.getProperty(propId);
11008
+ } catch {
11009
+ }
11010
+ }
11011
+ try {
11012
+ const c = feature.color;
11013
+ if (c) {
11014
+ color = {
11015
+ red: c.red,
11016
+ green: c.green,
11017
+ blue: c.blue,
11018
+ alpha: c.alpha
11019
+ };
11020
+ }
11021
+ } catch {
11022
+ }
11023
+ }
11024
+ } catch {
11025
+ }
11026
+ }
11027
+ return {
11028
+ success: true,
11029
+ position,
11030
+ coordinates,
11031
+ tileset,
11032
+ content,
11033
+ featureId,
11034
+ properties,
11035
+ color,
11036
+ screenPosition: windowPosition
11037
+ };
11038
+ }
11039
+ /**
11040
+ * 开启点云拾取会话
11041
+ * @param onPick 拾取回调函数
11042
+ * @param options 拾取选项
11043
+ * @returns 拾取会话对象
11044
+ */
11045
+ startPicking(onPick, options) {
11046
+ const C = this.CesiumNS;
11047
+ const canvas = this.viewer.scene.canvas;
11048
+ const prevCursor = canvas?.style?.cursor;
11049
+ const cursor = options?.cursor ?? "crosshair";
11050
+ if (canvas) canvas.style.cursor = cursor;
11051
+ this.handler = new C.ScreenSpaceEventHandler(canvas);
11052
+ this.isPaused = false;
11053
+ const showMarker = options?.showMarker ?? true;
11054
+ const markerColor = options?.markerColor ?? C.Color.YELLOW;
11055
+ const markerSize = options?.markerSize ?? 10;
11056
+ const handler = this.handler;
11057
+ if (handler) {
11058
+ handler.setInputAction(
11059
+ (movement) => {
11060
+ if (this.isPaused) return;
11061
+ const result = this.pickAt(movement.position, {
11062
+ fetchProperties: options?.fetchProperties ?? true,
11063
+ pointCloudOnly: options?.pointCloudOnly ?? true
11064
+ });
11065
+ if (showMarker && result.success && result.position) {
11066
+ this.showMarker(result.position, markerColor, markerSize);
11067
+ }
11068
+ onPick(result);
11069
+ },
11070
+ C.ScreenSpaceEventType.LEFT_CLICK
11071
+ );
11072
+ }
11073
+ return {
11074
+ stop: () => {
11075
+ this.stopPicking();
11076
+ if (canvas) canvas.style.cursor = prevCursor ?? "";
11077
+ },
11078
+ pause: () => {
11079
+ this.isPaused = true;
11080
+ },
11081
+ resume: () => {
11082
+ this.isPaused = false;
11083
+ },
11084
+ isPaused: () => this.isPaused
11085
+ };
11086
+ }
11087
+ /**
11088
+ * 停止拾取会话
11089
+ */
11090
+ stopPicking() {
11091
+ try {
11092
+ this.handler?.destroy();
11093
+ } catch {
11094
+ }
11095
+ this.handler = void 0;
11096
+ this.removeMarker();
11097
+ }
11098
+ /**
11099
+ * 显示标记点
11100
+ */
11101
+ showMarker(position, color, size) {
11102
+ const C = this.CesiumNS;
11103
+ this.removeMarker();
11104
+ this.markerEntity = this.viewer.entities.add({
11105
+ position,
11106
+ point: {
11107
+ pixelSize: size,
11108
+ color,
11109
+ outlineColor: C.Color.WHITE,
11110
+ outlineWidth: 2,
11111
+ disableDepthTestDistance: Number.POSITIVE_INFINITY,
11112
+ heightReference: C.HeightReference.NONE
11113
+ }
11114
+ });
11115
+ }
11116
+ /**
11117
+ * 移除标记点
11118
+ */
11119
+ removeMarker() {
11120
+ if (this.markerEntity) {
11121
+ try {
11122
+ this.viewer.entities.remove(this.markerEntity);
11123
+ } catch {
11124
+ }
11125
+ this.markerEntity = void 0;
11126
+ }
11127
+ }
11128
+ /**
11129
+ * 获取当前标记点位置
11130
+ */
11131
+ getMarkerPosition() {
11132
+ if (!this.markerEntity) return void 0;
11133
+ const pos = this.markerEntity.position;
11134
+ if (typeof pos?.getValue === "function") {
11135
+ return pos.getValue(this.CesiumNS.JulianDate.now());
11136
+ }
11137
+ return pos;
11138
+ }
11139
+ /**
11140
+ * 销毁拾取器
11141
+ */
11142
+ destroy() {
11143
+ this.stopPicking();
11144
+ this.removeMarker();
11145
+ }
11146
+ };
11147
+
10732
11148
  // src/utils/pathToSinoflyAdapter.ts
10733
11149
  function convertCartesian3ToLatLonHeight(CesiumNS, position) {
10734
11150
  const C = CesiumNS;
@@ -10807,23 +11223,6 @@ function convertPoseToActionGroup(heading, pitch, roll, fov, waypointIndex) {
10807
11223
  return "{}";
10808
11224
  }
10809
11225
  }
10810
- function convertStartPointToTakeOffRefPoint(CesiumNS, position) {
10811
- const C = CesiumNS;
10812
- try {
10813
- const cartographic = C.Cartographic.fromCartesian(position);
10814
- const longitude = C.Math.toDegrees(cartographic.longitude);
10815
- const latitude = C.Math.toDegrees(cartographic.latitude);
10816
- const height = cartographic.height;
10817
- return JSON.stringify({
10818
- longitude,
10819
- latitude,
10820
- height
10821
- });
10822
- } catch (error) {
10823
- console.error("[pathToSinoflyAdapter] takeOffRefPoint \u751F\u6210\u5931\u8D25:", error);
10824
- return "";
10825
- }
10826
- }
10827
11226
  function convertPathToSinofly(data, options) {
10828
11227
  const {
10829
11228
  CesiumNS,
@@ -10867,10 +11266,6 @@ function convertPathToSinofly(data, options) {
10867
11266
  }
10868
11267
  const distance = data.waypointData[data.waypointData.length - 1]?.distance ?? 0;
10869
11268
  const takeOffSecurityHeight = data.climbHeight ?? 0;
10870
- let takeOffRefPoint;
10871
- if (data.hasHiddenClimb && data.startPoint?.position) {
10872
- takeOffRefPoint = convertStartPointToTakeOffRefPoint(CesiumNS, data.startPoint.position);
10873
- }
10874
11269
  const waypointInfo = data.waypointData.map((wp, idx) => {
10875
11270
  const newIndex = idx;
10876
11271
  const { latitude, longitude, altitude } = convertCartesian3ToLatLonHeight(
@@ -10887,7 +11282,11 @@ function convertPathToSinofly(data, options) {
10887
11282
  longitude,
10888
11283
  altitude,
10889
11284
  action,
10890
- noReturn: defaultNoReturn
11285
+ noReturn: defaultNoReturn,
11286
+ // 🆕 添加高度相关字段
11287
+ ellipsoidHeight: wp.ellipsoidHeight,
11288
+ relativeHeight: wp.relativeHeight,
11289
+ useGlobalHeight: wp.useGlobalHeight
10891
11290
  };
10892
11291
  if (waypointCreateUserId) waypoint.createUserId = waypointCreateUserId;
10893
11292
  if (waypointCreateTime) waypoint.createTime = waypointCreateTime;
@@ -10915,7 +11314,6 @@ function convertPathToSinofly(data, options) {
10915
11314
  photoNumber,
10916
11315
  waypointInfo
10917
11316
  };
10918
- if (takeOffRefPoint) sinoflyData.takeOffRefPoint = takeOffRefPoint;
10919
11317
  if (thumbnailFileId !== void 0) sinoflyData.thumbnailFileId = thumbnailFileId;
10920
11318
  if (stationId !== void 0) sinoflyData.stationId = stationId;
10921
11319
  if (subarrayId !== void 0) sinoflyData.subarrayId = subarrayId;
@@ -11314,6 +11712,6 @@ var PathSafetyChecker = class {
11314
11712
  // src/index.ts
11315
11713
  var placeholder = { ready: true };
11316
11714
 
11317
- export { CZMLManager, CameraEventBus, CameraFOVController, CameraManager, Emitter, FrustumPyramid, LayerManager, PathSafetyChecker, PolygonEditor, SceneManager, Selector, StateManager, assertCesiumAssetsConfigured, configureCesiumAssets, configureCesiumIonToken, convertPathToSinofly, convertSinoflyWayline, convertSinoflyWaylines, ensureCesiumIonToken, getCesiumBaseUrl, getCesiumIonToken, globalCameraEventBus, globalState, placeholder, renderFlightPath, renderFlightPathPreview, toggle2D3D, version, versionInfo };
11715
+ export { CZMLManager, CameraEventBus, CameraFOVController, CameraManager, Emitter, FlightSimulator, FrustumPyramid, LayerManager, PathSafetyChecker, PointCloudPicker, PolygonEditor, SceneManager, Selector, StateManager, assertCesiumAssetsConfigured, calculateAbsoluteHeight, calculateRelativeHeight, configureCesiumAssets, configureCesiumIonToken, convertPathToSinofly, convertSinoflyWayline, convertSinoflyWaylines, ensureCesiumIonToken, getCesiumBaseUrl, getCesiumIonToken, globalCameraEventBus, globalState, placeholder, queryTerrainHeightAsync, queryTerrainHeightSync, queryTerrainHeights, renderFlightPath, renderFlightPathPreview, toggle2D3D, version, versionInfo };
11318
11716
  //# sourceMappingURL=index.js.map
11319
11717
  //# sourceMappingURL=index.js.map