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