@jorgmoritz/gis-manager 0.1.30 → 0.1.32

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.29"};
16
+ version: "0.1.31"};
17
17
 
18
18
  // src/utils/version.ts
19
19
  var version = package_default.version;
@@ -1330,12 +1330,10 @@ var StateManager = class {
1330
1330
  __publicField(this, "editing", false);
1331
1331
  __publicField(this, "editingTarget");
1332
1332
  __publicField(this, "previousView");
1333
- __publicField(this, "flightPreviewing", false);
1334
1333
  // Emitters
1335
1334
  __publicField(this, "onSelectionChange", new Emitter());
1336
1335
  __publicField(this, "onEditingChange", new Emitter());
1337
1336
  __publicField(this, "onPreviousViewChange", new Emitter());
1338
- __publicField(this, "onFlightPreviewChange", new Emitter());
1339
1337
  }
1340
1338
  // Selection
1341
1339
  setSelected(next) {
@@ -1382,35 +1380,6 @@ var StateManager = class {
1382
1380
  getPreviousCameraView() {
1383
1381
  return this.previousView;
1384
1382
  }
1385
- // Flight preview mode
1386
- /**
1387
- * 开始飞行预览模式(锁定交互)
1388
- */
1389
- startFlightPreview() {
1390
- if (this.flightPreviewing) return;
1391
- this.flightPreviewing = true;
1392
- this.onFlightPreviewChange.emit({ previewing: true });
1393
- }
1394
- /**
1395
- * 停止飞行预览模式(恢复交互)
1396
- */
1397
- stopFlightPreview() {
1398
- if (!this.flightPreviewing) return;
1399
- this.flightPreviewing = false;
1400
- this.onFlightPreviewChange.emit({ previewing: false });
1401
- }
1402
- /**
1403
- * 检查是否处于飞行预览模式
1404
- */
1405
- isFlightPreviewing() {
1406
- return this.flightPreviewing;
1407
- }
1408
- /**
1409
- * 检查当前是否允许交互(非编辑模式且非飞行预览模式)
1410
- */
1411
- isInteractionAllowed() {
1412
- return !this.editing && !this.flightPreviewing;
1413
- }
1414
1383
  };
1415
1384
  var globalState = new StateManager();
1416
1385
 
@@ -2864,6 +2833,10 @@ var AirplaneCursor = class {
2864
2833
  __publicField(this, "pyramidEntities", []);
2865
2834
  // 临时数据源,用于存储金字塔实体(避免污染宿主图层)
2866
2835
  __publicField(this, "pyramidLayer");
2836
+ // 缓存的缩放因子,仅在相机变化时更新
2837
+ __publicField(this, "cachedScaleFactor", 1);
2838
+ // 相机变化事件监听器
2839
+ __publicField(this, "cameraChangedListener");
2867
2840
  const C = this.CesiumNS;
2868
2841
  this.opts = opts;
2869
2842
  this.pose = { position: startPosition, heading: 0, pitch: -10, roll: 0 };
@@ -2883,14 +2856,39 @@ var AirplaneCursor = class {
2883
2856
  this.viewer.dataSources.add(this.pyramidLayer);
2884
2857
  }
2885
2858
  const modelHeadingOffset = 270;
2886
- const modelVerticalOffset = 35;
2887
- const modelForwardOffset = 5;
2888
- const modelRightOffset = -30;
2859
+ const baseVerticalOffset = 10;
2860
+ const baseForwardOffset = 5;
2861
+ const baseRightOffset = -9;
2862
+ const baseScale = 0.15;
2863
+ const minScale = 0.025;
2864
+ const maxScale = 1.5;
2865
+ const referenceDistance = 800;
2866
+ const computeScaleFactor = () => {
2867
+ try {
2868
+ const cameraPos = this.viewer.camera.positionWC;
2869
+ const modelPos = this.pose.position;
2870
+ const distance = C.Cartesian3.distance(cameraPos, modelPos);
2871
+ const rawScale = baseScale * (distance / referenceDistance);
2872
+ const clampedScale = Math.max(minScale, Math.min(maxScale, rawScale));
2873
+ return clampedScale / baseScale;
2874
+ } catch {
2875
+ return 1;
2876
+ }
2877
+ };
2878
+ this.cachedScaleFactor = computeScaleFactor();
2879
+ this.cameraChangedListener = this.viewer.camera.changed.addEventListener(() => {
2880
+ this.cachedScaleFactor = computeScaleFactor();
2881
+ this.viewer.scene?.requestRender?.();
2882
+ });
2889
2883
  this.modelEntity = this.pyramidLayer.entities.add({
2890
2884
  position: new C.CallbackProperty(() => {
2891
2885
  const pos = this.pose.position;
2892
2886
  const headingRad = C.Math.toRadians(this.pose.heading);
2893
2887
  const pitchRad = C.Math.toRadians(this.pose.pitch);
2888
+ const scaleFactor = this.cachedScaleFactor;
2889
+ const modelVerticalOffset = baseVerticalOffset * scaleFactor;
2890
+ const modelForwardOffset = baseForwardOffset * scaleFactor;
2891
+ const modelRightOffset = baseRightOffset * scaleFactor;
2894
2892
  const enu = C.Transforms.eastNorthUpToFixedFrame(pos);
2895
2893
  const east = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 0, new C.Cartesian4()));
2896
2894
  const north = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 1, new C.Cartesian4()));
@@ -2931,9 +2929,10 @@ var AirplaneCursor = class {
2931
2929
  }, false),
2932
2930
  model: {
2933
2931
  uri: wurenji_default,
2934
- scale: 0.5,
2935
- minimumPixelSize: 32,
2936
- maximumScale: 100,
2932
+ scale: new C.CallbackProperty(() => {
2933
+ return baseScale * this.cachedScaleFactor;
2934
+ }, false),
2935
+ minimumPixelSize: 24,
2937
2936
  silhouetteColor: C.Color.CYAN,
2938
2937
  silhouetteSize: 1
2939
2938
  },
@@ -3295,6 +3294,11 @@ var AirplaneCursor = class {
3295
3294
  } catch {
3296
3295
  }
3297
3296
  this.pyramidLayer = void 0;
3297
+ try {
3298
+ this.cameraChangedListener?.();
3299
+ } catch {
3300
+ }
3301
+ this.cameraChangedListener = void 0;
3298
3302
  try {
3299
3303
  this.frustum?.destroy();
3300
3304
  } catch {
@@ -4613,34 +4617,6 @@ var PathEditingEventHandler = class {
4613
4617
  * 🆕 处理快速编辑模式的点击
4614
4618
  */
4615
4619
  handleQuickEditClick(movement) {
4616
- const C = this.CesiumNS;
4617
- try {
4618
- const picked = this.viewer.scene.pick?.(movement.position);
4619
- const entity = picked?.id;
4620
- if (entity?.properties) {
4621
- const props = entity.properties;
4622
- const vertexIndex = props._vertexIndex?.getValue?.() ?? props._vertexIndex;
4623
- const type = props._type?.getValue?.() ?? props._type;
4624
- if (typeof vertexIndex === "number" && (type === "path-vertex" || type === "vertex-label")) {
4625
- if (vertexIndex !== this.hiddenClimbIndex && vertexIndex !== 0) {
4626
- console.log("[QuickEdit] \u70B9\u51FB\u5230\u5DF2\u5B58\u5728\u822A\u70B9\uFF0C\u9009\u4E2D\u800C\u975E\u65B0\u589E:", vertexIndex);
4627
- if (this.callbacks.onVertexSelect) {
4628
- this.callbacks.onVertexSelect(vertexIndex);
4629
- }
4630
- if (this.callbacks.onVertexSelectDetail) {
4631
- try {
4632
- const detailInfo = this.buildVertexDetailInfo(vertexIndex);
4633
- this.callbacks.onVertexSelectDetail(detailInfo);
4634
- } catch (error) {
4635
- console.error("Error building vertex detail info:", error);
4636
- }
4637
- }
4638
- return;
4639
- }
4640
- }
4641
- }
4642
- } catch (error) {
4643
- }
4644
4620
  const position = this.getPositionFromMouse(movement);
4645
4621
  if (!position) {
4646
4622
  console.warn("[QuickEdit] \u65E0\u6CD5\u83B7\u53D6\u70B9\u51FB\u4F4D\u7F6E");
@@ -4649,6 +4625,7 @@ var PathEditingEventHandler = class {
4649
4625
  const quickEditOptions = this.callbacks.getQuickEditOptions?.();
4650
4626
  if (!quickEditOptions) return;
4651
4627
  const { climbHeight, altitudeMode } = quickEditOptions;
4628
+ const C = this.CesiumNS;
4652
4629
  const originalCarto = C.Cartographic.fromCartesian(position);
4653
4630
  const originalHeight = originalCarto.height;
4654
4631
  const newPosition = this.applyClimbHeight(position, climbHeight, altitudeMode);
@@ -5503,10 +5480,6 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5503
5480
  );
5504
5481
  const cleanupSession = () => {
5505
5482
  setActiveIndex(void 0);
5506
- try {
5507
- preview?.destroy();
5508
- } catch {
5509
- }
5510
5483
  try {
5511
5484
  handles.forEach((handle) => {
5512
5485
  if (handle) {
@@ -5916,6 +5889,13 @@ var ArrowShape = class {
5916
5889
  __publicField(this, "baseAlpha");
5917
5890
  __publicField(this, "outline");
5918
5891
  __publicField(this, "outlineColor");
5892
+ // 动态缩放相关
5893
+ __publicField(this, "dynamicScale");
5894
+ __publicField(this, "viewer");
5895
+ __publicField(this, "referenceDistance");
5896
+ __publicField(this, "minScale");
5897
+ __publicField(this, "maxScale");
5898
+ __publicField(this, "cachedScaleFactor", 1);
5919
5899
  const C = this.CesiumNS;
5920
5900
  this.shaftHeight = opts.shaftHeight ?? 17;
5921
5901
  this.headHeight = opts.headHeight ?? 8;
@@ -5923,12 +5903,17 @@ var ArrowShape = class {
5923
5903
  this.shaftRadius = opts.shaftRadius ?? 3;
5924
5904
  this.headRadius = opts.headRadius ?? 6;
5925
5905
  this.baseRadius = opts.baseRadius ?? 8;
5926
- this.color = opts.color ? C.Color.fromCssColorString(opts.color) : C.Color.fromCssColorString("#2E77FB");
5906
+ this.color = opts.color ? C.Color.fromCssColorString(opts.color) : C.Color.fromCssColorString("#FFD700");
5927
5907
  this.shaftAlpha = opts.shaftAlpha ?? 1;
5928
5908
  this.headAlpha = opts.headAlpha ?? 1;
5929
- this.baseAlpha = opts.baseAlpha ?? 1;
5909
+ this.baseAlpha = opts.baseAlpha ?? 0.65;
5930
5910
  this.outline = opts.outline ?? false;
5931
5911
  this.outlineColor = opts.outlineColor ? C.Color.fromCssColorString(opts.outlineColor) : this.color.darken(0.3, new C.Color());
5912
+ this.dynamicScale = opts.dynamicScale ?? false;
5913
+ this.viewer = opts.viewer;
5914
+ this.referenceDistance = opts.referenceDistance ?? 500;
5915
+ this.minScale = opts.minScale ?? 0.3;
5916
+ this.maxScale = opts.maxScale ?? 3;
5932
5917
  }
5933
5918
  /**
5934
5919
  * Create arrow entities at given position
@@ -5946,22 +5931,40 @@ var ArrowShape = class {
5946
5931
  const carto = C.Cartographic.fromCartesian(position);
5947
5932
  const lon = C.Math.toDegrees(carto.longitude);
5948
5933
  const lat = C.Math.toDegrees(carto.latitude);
5949
- const shaftPosition = C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight / 2);
5950
- const headPosition = C.Cartesian3.fromDegrees(
5951
- lon,
5952
- lat,
5953
- baseHeight + this.shaftHeight + this.headHeight / 2
5954
- );
5934
+ const computeScaleFactor = () => {
5935
+ if (!this.dynamicScale || !this.viewer) return 1;
5936
+ try {
5937
+ const cameraPos = this.viewer.camera.positionWC;
5938
+ const distance = C.Cartesian3.distance(cameraPos, position);
5939
+ const rawScale = distance / this.referenceDistance;
5940
+ return Math.max(this.minScale, Math.min(this.maxScale, rawScale));
5941
+ } catch {
5942
+ return 1;
5943
+ }
5944
+ };
5945
+ if (this.dynamicScale && this.viewer) {
5946
+ this.viewer.scene.preRender.addEventListener(() => {
5947
+ this.cachedScaleFactor = computeScaleFactor();
5948
+ });
5949
+ }
5955
5950
  const hpr = new C.HeadingPitchRoll(0, 0, 0);
5956
5951
  const baseOrientation = C.Transforms.headingPitchRollQuaternion(position, hpr);
5957
5952
  const fixedOrientation = new C.ConstantProperty(baseOrientation);
5953
+ const getShaftPosition = () => {
5954
+ const scale = this.cachedScaleFactor;
5955
+ return C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight * scale / 2);
5956
+ };
5957
+ const getHeadPosition = () => {
5958
+ const scale = this.cachedScaleFactor;
5959
+ return C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight * scale + this.headHeight * scale / 2);
5960
+ };
5958
5961
  const shaftEntity = layer.entities.add({
5959
- position: new C.ConstantPositionProperty(shaftPosition),
5962
+ position: this.dynamicScale ? new C.CallbackProperty(getShaftPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight / 2)),
5960
5963
  orientation: fixedOrientation,
5961
5964
  cylinder: {
5962
- length: this.shaftHeight,
5963
- topRadius: this.shaftRadius,
5964
- bottomRadius: this.shaftRadius,
5965
+ length: this.dynamicScale ? new C.CallbackProperty(() => this.shaftHeight * this.cachedScaleFactor, false) : this.shaftHeight,
5966
+ topRadius: this.dynamicScale ? new C.CallbackProperty(() => this.shaftRadius * this.cachedScaleFactor, false) : this.shaftRadius,
5967
+ bottomRadius: this.dynamicScale ? new C.CallbackProperty(() => this.shaftRadius * this.cachedScaleFactor, false) : this.shaftRadius,
5965
5968
  material: this.color.withAlpha(this.shaftAlpha),
5966
5969
  outline: this.outline,
5967
5970
  outlineColor: this.outlineColor,
@@ -5974,13 +5977,13 @@ var ArrowShape = class {
5974
5977
  });
5975
5978
  entities.push(shaftEntity);
5976
5979
  const headEntity = layer.entities.add({
5977
- position: new C.ConstantPositionProperty(headPosition),
5980
+ position: this.dynamicScale ? new C.CallbackProperty(getHeadPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight + this.headHeight / 2)),
5978
5981
  orientation: fixedOrientation,
5979
5982
  cylinder: {
5980
- length: this.headHeight,
5983
+ length: this.dynamicScale ? new C.CallbackProperty(() => this.headHeight * this.cachedScaleFactor, false) : this.headHeight,
5981
5984
  topRadius: 0,
5982
5985
  // Creates cone shape
5983
- bottomRadius: this.headRadius,
5986
+ bottomRadius: this.dynamicScale ? new C.CallbackProperty(() => this.headRadius * this.cachedScaleFactor, false) : this.headRadius,
5984
5987
  material: this.color.withAlpha(this.headAlpha),
5985
5988
  outline: this.outline,
5986
5989
  outlineColor: this.outlineColor,
@@ -5995,8 +5998,8 @@ var ArrowShape = class {
5995
5998
  const baseEntity = layer.entities.add({
5996
5999
  position,
5997
6000
  ellipse: {
5998
- semiMajorAxis: this.baseRadius,
5999
- semiMinorAxis: this.baseRadius,
6001
+ semiMajorAxis: this.dynamicScale ? new C.CallbackProperty(() => this.baseRadius * this.cachedScaleFactor, false) : this.baseRadius,
6002
+ semiMinorAxis: this.dynamicScale ? new C.CallbackProperty(() => this.baseRadius * this.cachedScaleFactor, false) : this.baseRadius,
6000
6003
  height: baseHeight,
6001
6004
  material: this.color.withAlpha(this.baseAlpha),
6002
6005
  outline: this.outline,
@@ -6135,7 +6138,13 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
6135
6138
  color: "#FFD700",
6136
6139
  shaftAlpha: 1,
6137
6140
  headAlpha: 1,
6138
- baseAlpha: 0.5
6141
+ baseAlpha: 0.5,
6142
+ // 启用动态缩放,和无人机模型一样根据相机距离调整大小
6143
+ dynamicScale: true,
6144
+ viewer,
6145
+ referenceDistance: 300,
6146
+ minScale: 0.3,
6147
+ maxScale: 2
6139
6148
  });
6140
6149
  const arrowEntities = arrowShape.createEntities(
6141
6150
  layer,
@@ -6652,7 +6661,13 @@ function renderFlightPath(CesiumNS, viewer, options) {
6652
6661
  color: "#FFD700",
6653
6662
  shaftAlpha: 0.98,
6654
6663
  headAlpha: 0.98,
6655
- baseAlpha: 0.65
6664
+ baseAlpha: 0.65,
6665
+ // 启用动态缩放
6666
+ dynamicScale: true,
6667
+ viewer,
6668
+ referenceDistance: 300,
6669
+ minScale: 0.3,
6670
+ maxScale: 2
6656
6671
  });
6657
6672
  arrowShape.createEntities(
6658
6673
  layer,
@@ -6776,515 +6791,6 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6776
6791
  return { entity, controller };
6777
6792
  }
6778
6793
 
6779
- // src/core/path-manager/FlightPreviewController.ts
6780
- var FlightPreviewController = class {
6781
- constructor(CesiumNS, viewer, options) {
6782
- this.CesiumNS = CesiumNS;
6783
- this.viewer = viewer;
6784
- this.options = options;
6785
- // ========== 状态 ==========
6786
- __publicField(this, "state", "stopped");
6787
- __publicField(this, "destroyed", false);
6788
- // ========== 配置 ==========
6789
- __publicField(this, "speed");
6790
- __publicField(this, "loop");
6791
- __publicField(this, "showFrustum");
6792
- __publicField(this, "trackHighlightOptions");
6793
- // ========== 路径数据 ==========
6794
- __publicField(this, "waypoints", []);
6795
- __publicField(this, "totalDistance", 0);
6796
- // ========== 动画状态 ==========
6797
- __publicField(this, "currentDistance", 0);
6798
- // 当前已飞行距离
6799
- __publicField(this, "animationFrameId");
6800
- __publicField(this, "lastFrameTime");
6801
- // ========== 可视化组件 ==========
6802
- __publicField(this, "layer");
6803
- __publicField(this, "droneEntity");
6804
- __publicField(this, "frustum");
6805
- __publicField(this, "pathPreview");
6806
- // ========== 轨迹高亮 ==========
6807
- __publicField(this, "traveledPathEntity");
6808
- __publicField(this, "remainingPathEntity");
6809
- // ========== 事件 ==========
6810
- __publicField(this, "onStateChange", new Emitter());
6811
- __publicField(this, "onProgressChange", new Emitter());
6812
- this.speed = options.speed ?? 10;
6813
- this.loop = options.loop ?? true;
6814
- this.showFrustum = options.showFrustum ?? true;
6815
- this.trackHighlightOptions = {
6816
- enabled: true,
6817
- traveledWidth: 6,
6818
- remainingWidth: 4,
6819
- ...options.trackHighlight
6820
- };
6821
- this.initLayer();
6822
- this.parseWaylineData();
6823
- this.initVisualization();
6824
- console.log("[FlightPreviewController] \u521D\u59CB\u5316\u5B8C\u6210", {
6825
- waypointCount: this.waypoints.length,
6826
- totalDistance: this.totalDistance,
6827
- speed: this.speed
6828
- });
6829
- }
6830
- // ==================== 公共方法 ====================
6831
- /** 开始预览 */
6832
- start() {
6833
- if (this.destroyed) return;
6834
- if (this.state === "playing") return;
6835
- if (this.state === "stopped") {
6836
- this.currentDistance = 0;
6837
- }
6838
- this.setState("playing");
6839
- this.lastFrameTime = performance.now();
6840
- this.startAnimationLoop();
6841
- globalState.startFlightPreview();
6842
- this.pathPreview?.show();
6843
- console.log("[FlightPreviewController] \u5F00\u59CB\u64AD\u653E");
6844
- }
6845
- /** 暂停 */
6846
- pause() {
6847
- if (this.destroyed) return;
6848
- if (this.state !== "playing") return;
6849
- this.setState("paused");
6850
- this.stopAnimationLoop();
6851
- console.log("[FlightPreviewController] \u6682\u505C");
6852
- }
6853
- /** 继续 */
6854
- resume() {
6855
- if (this.destroyed) return;
6856
- if (this.state !== "paused") return;
6857
- this.setState("playing");
6858
- this.lastFrameTime = performance.now();
6859
- this.startAnimationLoop();
6860
- console.log("[FlightPreviewController] \u7EE7\u7EED\u64AD\u653E");
6861
- }
6862
- /** 停止并重置 */
6863
- stop() {
6864
- if (this.destroyed) return;
6865
- this.setState("stopped");
6866
- this.stopAnimationLoop();
6867
- this.currentDistance = 0;
6868
- globalState.stopFlightPreview();
6869
- this.updateVisualization();
6870
- this.pathPreview?.hide();
6871
- console.log("[FlightPreviewController] \u505C\u6B62");
6872
- }
6873
- /** 跳转到指定进度 (0-1) */
6874
- seek(progress) {
6875
- if (this.destroyed) return;
6876
- const clampedProgress = Math.max(0, Math.min(1, progress));
6877
- this.currentDistance = clampedProgress * this.totalDistance;
6878
- this.updateVisualization();
6879
- console.log("[FlightPreviewController] \u8DF3\u8F6C\u5230", (clampedProgress * 100).toFixed(1) + "%");
6880
- }
6881
- /** 设置速度 */
6882
- setSpeed(speed) {
6883
- this.speed = Math.max(1, speed);
6884
- console.log("[FlightPreviewController] \u8BBE\u7F6E\u901F\u5EA6:", this.speed, "m/s");
6885
- }
6886
- /** 获取当前状态 */
6887
- getState() {
6888
- return this.state;
6889
- }
6890
- /** 获取当前进度 (0-1) */
6891
- getProgress() {
6892
- if (this.totalDistance === 0) return 0;
6893
- return this.currentDistance / this.totalDistance;
6894
- }
6895
- /** 获取当前航点索引 */
6896
- getCurrentWaypointIndex() {
6897
- return this.findCurrentSegment().segmentIndex;
6898
- }
6899
- /** 销毁 */
6900
- destroy() {
6901
- if (this.destroyed) return;
6902
- this.destroyed = true;
6903
- this.stopAnimationLoop();
6904
- globalState.stopFlightPreview();
6905
- this.destroyVisualization();
6906
- this.onStateChange.clear();
6907
- this.onProgressChange.clear();
6908
- console.log("[FlightPreviewController] \u5DF2\u9500\u6BC1");
6909
- }
6910
- // ==================== 私有方法 - 初始化 ====================
6911
- /** 初始化图层 */
6912
- initLayer() {
6913
- const C = this.CesiumNS;
6914
- if (this.options.layer) {
6915
- this.layer = this.options.layer;
6916
- } else {
6917
- const newLayer = new C.CustomDataSource("FlightPreviewLayer");
6918
- this.layer = newLayer;
6919
- this.viewer.dataSources.add(newLayer);
6920
- }
6921
- }
6922
- /** 解析航线数据 */
6923
- parseWaylineData() {
6924
- const C = this.CesiumNS;
6925
- const { waylineData } = this.options;
6926
- const converted = convertSinoflyWayline(waylineData, { CesiumNS: this.CesiumNS });
6927
- const waypointData = converted.waypointData;
6928
- if (!waypointData || waypointData.length < 2) {
6929
- console.warn("[FlightPreviewController] \u822A\u70B9\u6570\u91CF\u4E0D\u8DB3");
6930
- return;
6931
- }
6932
- let cumulativeDistance = 0;
6933
- for (let i = 0; i < waypointData.length; i++) {
6934
- const wp = waypointData[i];
6935
- let distanceToNext = 0;
6936
- if (i < waypointData.length - 1) {
6937
- const nextWp = waypointData[i + 1];
6938
- distanceToNext = C.Cartesian3.distance(wp.position, nextWp.position);
6939
- }
6940
- this.waypoints.push({
6941
- position: wp.position,
6942
- heading: wp.heading ?? 0,
6943
- pitch: wp.pitch ?? -10,
6944
- roll: wp.roll ?? 0,
6945
- distanceToNext,
6946
- cumulativeDistance
6947
- });
6948
- cumulativeDistance += distanceToNext;
6949
- }
6950
- this.totalDistance = cumulativeDistance;
6951
- }
6952
- /** 初始化可视化组件 */
6953
- initVisualization() {
6954
- this.initDroneModel();
6955
- this.initFrustum();
6956
- this.initTrackHighlight();
6957
- this.initPathPreview();
6958
- this.updateVisualization();
6959
- }
6960
- /** 初始化无人机模型 */
6961
- initDroneModel() {
6962
- if (!this.layer || this.waypoints.length === 0) return;
6963
- const C = this.CesiumNS;
6964
- const layer = this.layer;
6965
- const modelHeadingOffset = 270;
6966
- const modelVerticalOffset = 35;
6967
- const modelForwardOffset = 5;
6968
- const modelRightOffset = -30;
6969
- this.droneEntity = layer.entities.add({
6970
- position: new C.CallbackProperty(() => {
6971
- const { position, heading, pitch } = this.getCurrentPose();
6972
- return this.applyModelOffset(position, heading, pitch, {
6973
- forward: modelForwardOffset,
6974
- right: modelRightOffset,
6975
- up: modelVerticalOffset
6976
- });
6977
- }, false),
6978
- orientation: new C.CallbackProperty(() => {
6979
- const { position, heading, pitch, roll } = this.getCurrentPose();
6980
- const hpr = new C.HeadingPitchRoll(
6981
- C.Math.toRadians(heading + modelHeadingOffset),
6982
- C.Math.toRadians(pitch),
6983
- C.Math.toRadians(roll)
6984
- );
6985
- return C.Transforms.headingPitchRollQuaternion(position, hpr);
6986
- }, false),
6987
- model: {
6988
- uri: wurenji_default,
6989
- scale: 0.5,
6990
- minimumPixelSize: 32,
6991
- maximumScale: 100,
6992
- silhouetteColor: C.Color.CYAN,
6993
- silhouetteSize: 1
6994
- },
6995
- properties: { _type: "flight-preview-drone" }
6996
- });
6997
- }
6998
- /** 应用模型偏移 */
6999
- applyModelOffset(position, heading, pitch, offset) {
7000
- const C = this.CesiumNS;
7001
- const headingRad = C.Math.toRadians(heading);
7002
- const pitchRad = C.Math.toRadians(pitch);
7003
- const enu = C.Transforms.eastNorthUpToFixedFrame(position);
7004
- const east = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 0, new C.Cartesian4()));
7005
- const north = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 1, new C.Cartesian4()));
7006
- const up = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 2, new C.Cartesian4()));
7007
- const horizForward = C.Cartesian3.add(
7008
- C.Cartesian3.multiplyByScalar(east, Math.sin(headingRad), new C.Cartesian3()),
7009
- C.Cartesian3.multiplyByScalar(north, Math.cos(headingRad), new C.Cartesian3()),
7010
- new C.Cartesian3()
7011
- );
7012
- const right = C.Cartesian3.add(
7013
- C.Cartesian3.multiplyByScalar(east, Math.cos(headingRad), new C.Cartesian3()),
7014
- C.Cartesian3.multiplyByScalar(north, -Math.sin(headingRad), new C.Cartesian3()),
7015
- new C.Cartesian3()
7016
- );
7017
- const forward = C.Cartesian3.add(
7018
- C.Cartesian3.multiplyByScalar(horizForward, Math.cos(pitchRad), new C.Cartesian3()),
7019
- C.Cartesian3.multiplyByScalar(up, Math.sin(pitchRad), new C.Cartesian3()),
7020
- new C.Cartesian3()
7021
- );
7022
- const upRotated = C.Cartesian3.add(
7023
- C.Cartesian3.multiplyByScalar(horizForward, -Math.sin(pitchRad), new C.Cartesian3()),
7024
- C.Cartesian3.multiplyByScalar(up, Math.cos(pitchRad), new C.Cartesian3()),
7025
- new C.Cartesian3()
7026
- );
7027
- let result = C.Cartesian3.clone(position);
7028
- result = C.Cartesian3.add(result, C.Cartesian3.multiplyByScalar(forward, offset.forward, new C.Cartesian3()), result);
7029
- result = C.Cartesian3.add(result, C.Cartesian3.multiplyByScalar(right, offset.right, new C.Cartesian3()), result);
7030
- result = C.Cartesian3.add(result, C.Cartesian3.multiplyByScalar(upRotated, offset.up, new C.Cartesian3()), result);
7031
- return result;
7032
- }
7033
- /** 初始化视锥体 */
7034
- initFrustum() {
7035
- if (!this.showFrustum || !this.layer) return;
7036
- const C = this.CesiumNS;
7037
- this.frustum = new FrustumPyramid(this.CesiumNS, this.layer, {
7038
- fov: 50,
7039
- length: 500,
7040
- color: C.Color.CYAN,
7041
- fillAlpha: 0.25,
7042
- width: 2
7043
- });
7044
- this.frustum.showAtDynamic(
7045
- () => this.getCurrentPose().position,
7046
- () => this.getCurrentPose().heading,
7047
- () => this.getCurrentPose().pitch,
7048
- () => this.getCurrentPose().roll,
7049
- () => 50,
7050
- () => 500
7051
- );
7052
- }
7053
- /** 初始化轨迹高亮 */
7054
- initTrackHighlight() {
7055
- if (!this.trackHighlightOptions.enabled || !this.layer) return;
7056
- if (this.waypoints.length < 2) return;
7057
- const C = this.CesiumNS;
7058
- const layer = this.layer;
7059
- const traveledColor = this.trackHighlightOptions.traveledColor ?? C.Color.fromCssColorString("#00FF88");
7060
- const remainingColor = this.trackHighlightOptions.remainingColor ?? C.Color.fromCssColorString("#4A90D9").withAlpha(0.7);
7061
- this.traveledPathEntity = layer.entities.add({
7062
- polyline: {
7063
- positions: new C.CallbackProperty(() => this.getTraveledPositions(), false),
7064
- width: this.trackHighlightOptions.traveledWidth ?? 6,
7065
- material: traveledColor,
7066
- clampToGround: false
7067
- },
7068
- properties: { _type: "flight-preview-traveled" }
7069
- });
7070
- this.remainingPathEntity = layer.entities.add({
7071
- polyline: {
7072
- positions: new C.CallbackProperty(() => this.getRemainingPositions(), false),
7073
- width: this.trackHighlightOptions.remainingWidth ?? 4,
7074
- material: remainingColor,
7075
- clampToGround: false
7076
- },
7077
- properties: { _type: "flight-preview-remaining" }
7078
- });
7079
- }
7080
- /** 初始化预览窗口 */
7081
- initPathPreview() {
7082
- this.pathPreview = new PathPreview(
7083
- this.CesiumNS,
7084
- this.viewer,
7085
- {
7086
- container: this.options.previewContainer,
7087
- fov: 50,
7088
- pitch: -10,
7089
- useGlobalEventBus: true
7090
- }
7091
- );
7092
- this.pathPreview.hide();
7093
- }
7094
- // ==================== 私有方法 - 动画循环 ====================
7095
- /** 启动动画循环 */
7096
- startAnimationLoop() {
7097
- if (this.animationFrameId) return;
7098
- const animate = (currentTime) => {
7099
- if (this.state !== "playing" || this.destroyed) {
7100
- this.animationFrameId = void 0;
7101
- return;
7102
- }
7103
- const deltaTime = this.lastFrameTime ? (currentTime - this.lastFrameTime) / 1e3 : 0;
7104
- this.lastFrameTime = currentTime;
7105
- this.currentDistance += this.speed * deltaTime;
7106
- if (this.currentDistance >= this.totalDistance) {
7107
- if (this.loop) {
7108
- this.currentDistance = this.currentDistance % this.totalDistance;
7109
- } else {
7110
- this.currentDistance = this.totalDistance;
7111
- this.setState("stopped");
7112
- this.stopAnimationLoop();
7113
- return;
7114
- }
7115
- }
7116
- this.updateVisualization();
7117
- this.animationFrameId = requestAnimationFrame(animate);
7118
- };
7119
- this.animationFrameId = requestAnimationFrame(animate);
7120
- }
7121
- /** 停止动画循环 */
7122
- stopAnimationLoop() {
7123
- if (this.animationFrameId) {
7124
- cancelAnimationFrame(this.animationFrameId);
7125
- this.animationFrameId = void 0;
7126
- }
7127
- this.lastFrameTime = void 0;
7128
- }
7129
- // ==================== 私有方法 - 路径插值 ====================
7130
- /** 查找当前所在航段 */
7131
- findCurrentSegment() {
7132
- if (this.waypoints.length === 0) {
7133
- return { segmentIndex: 0, progressInSegment: 0 };
7134
- }
7135
- for (let i = 0; i < this.waypoints.length - 1; i++) {
7136
- const wp = this.waypoints[i];
7137
- const nextWp = this.waypoints[i + 1];
7138
- if (this.currentDistance <= nextWp.cumulativeDistance) {
7139
- const segmentStart = wp.cumulativeDistance;
7140
- const segmentLength = wp.distanceToNext;
7141
- const distanceInSegment = this.currentDistance - segmentStart;
7142
- const progressInSegment = segmentLength > 0 ? distanceInSegment / segmentLength : 0;
7143
- return { segmentIndex: i, progressInSegment };
7144
- }
7145
- }
7146
- return { segmentIndex: this.waypoints.length - 2, progressInSegment: 1 };
7147
- }
7148
- /** 获取当前姿态 */
7149
- getCurrentPose() {
7150
- if (this.waypoints.length === 0) {
7151
- const C = this.CesiumNS;
7152
- return {
7153
- position: new C.Cartesian3(),
7154
- heading: 0,
7155
- pitch: -10,
7156
- roll: 0
7157
- };
7158
- }
7159
- const { segmentIndex, progressInSegment } = this.findCurrentSegment();
7160
- const wp = this.waypoints[segmentIndex];
7161
- const nextWp = this.waypoints[Math.min(segmentIndex + 1, this.waypoints.length - 1)];
7162
- const position = this.interpolatePosition(wp.position, nextWp.position, progressInSegment);
7163
- const heading = this.interpolateAngle(wp.heading, nextWp.heading, progressInSegment);
7164
- const pitch = this.lerp(wp.pitch, nextWp.pitch, progressInSegment);
7165
- const roll = this.lerp(wp.roll, nextWp.roll, progressInSegment);
7166
- return { position, heading, pitch, roll };
7167
- }
7168
- /** 位置插值 */
7169
- interpolatePosition(start, end, t) {
7170
- const C = this.CesiumNS;
7171
- return C.Cartesian3.lerp(start, end, t, new C.Cartesian3());
7172
- }
7173
- /** 角度插值(处理 0-360 环绕) */
7174
- interpolateAngle(start, end, t) {
7175
- let diff = end - start;
7176
- if (diff > 180) diff -= 360;
7177
- if (diff < -180) diff += 360;
7178
- let result = start + diff * t;
7179
- return (result % 360 + 360) % 360;
7180
- }
7181
- /** 线性插值 */
7182
- lerp(start, end, t) {
7183
- return start + (end - start) * t;
7184
- }
7185
- // ==================== 私有方法 - 轨迹高亮 ====================
7186
- /** 获取已飞过的位置数组 */
7187
- getTraveledPositions() {
7188
- if (this.waypoints.length === 0) return [];
7189
- const { segmentIndex, progressInSegment } = this.findCurrentSegment();
7190
- const currentPos = this.getCurrentPose().position;
7191
- const positions = [];
7192
- for (let i = 0; i <= segmentIndex; i++) {
7193
- positions.push(this.waypoints[i].position);
7194
- }
7195
- if (progressInSegment > 0) {
7196
- positions.push(currentPos);
7197
- }
7198
- return positions;
7199
- }
7200
- /** 获取未飞过的位置数组 */
7201
- getRemainingPositions() {
7202
- if (this.waypoints.length === 0) return [];
7203
- const { segmentIndex } = this.findCurrentSegment();
7204
- const currentPos = this.getCurrentPose().position;
7205
- const positions = [];
7206
- positions.push(currentPos);
7207
- for (let i = segmentIndex + 1; i < this.waypoints.length; i++) {
7208
- positions.push(this.waypoints[i].position);
7209
- }
7210
- return positions;
7211
- }
7212
- // ==================== 私有方法 - 更新 ====================
7213
- /** 更新可视化 */
7214
- updateVisualization() {
7215
- const pose = this.getCurrentPose();
7216
- if (this.pathPreview) {
7217
- this.pathPreview.setPose(
7218
- pose.position,
7219
- pose.heading,
7220
- pose.pitch,
7221
- pose.roll
7222
- );
7223
- }
7224
- this.viewer.scene?.requestRender?.();
7225
- const { segmentIndex } = this.findCurrentSegment();
7226
- this.onProgressChange.emit({
7227
- progress: this.getProgress(),
7228
- position: pose.position,
7229
- waypointIndex: segmentIndex,
7230
- pose: {
7231
- heading: pose.heading,
7232
- pitch: pose.pitch,
7233
- roll: pose.roll
7234
- }
7235
- });
7236
- }
7237
- /** 设置状态 */
7238
- setState(newState) {
7239
- if (this.state !== newState) {
7240
- this.state = newState;
7241
- this.onStateChange.emit({ state: newState });
7242
- }
7243
- }
7244
- /** 销毁可视化组件 */
7245
- destroyVisualization() {
7246
- const layer = this.layer;
7247
- if (this.droneEntity && layer) {
7248
- try {
7249
- layer.entities.remove(this.droneEntity);
7250
- } catch {
7251
- }
7252
- }
7253
- if (this.traveledPathEntity && layer) {
7254
- try {
7255
- layer.entities.remove(this.traveledPathEntity);
7256
- } catch {
7257
- }
7258
- }
7259
- if (this.remainingPathEntity && layer) {
7260
- try {
7261
- layer.entities.remove(this.remainingPathEntity);
7262
- } catch {
7263
- }
7264
- }
7265
- try {
7266
- this.frustum?.destroy();
7267
- } catch {
7268
- }
7269
- try {
7270
- this.pathPreview?.destroy();
7271
- } catch {
7272
- }
7273
- if (!this.options.layer && this.layer) {
7274
- try {
7275
- this.viewer.dataSources.remove(this.layer);
7276
- } catch {
7277
- }
7278
- }
7279
- this.droneEntity = void 0;
7280
- this.traveledPathEntity = void 0;
7281
- this.remainingPathEntity = void 0;
7282
- this.frustum = void 0;
7283
- this.pathPreview = void 0;
7284
- this.layer = void 0;
7285
- }
7286
- };
7287
-
7288
6794
  // src/core/CZMLPathManager.ts
7289
6795
  var _CZMLPathManager = class _CZMLPathManager {
7290
6796
  constructor(CesiumNS, viewer) {
@@ -7398,50 +6904,6 @@ var _CZMLPathManager = class _CZMLPathManager {
7398
6904
  renderFlightPathPreview(options) {
7399
6905
  return renderFlightPathPreview(this.CesiumNS, this.viewer, options);
7400
6906
  }
7401
- /**
7402
- * 开始飞行预览:飞机沿航线飞行动画
7403
- *
7404
- * 功能:
7405
- * - 飞机游标沿航线从起点飞到终点,循环播放
7406
- * - 预览窗口实时显示飞机第一人称视角
7407
- * - 轨迹高亮显示已飞过/未飞过的航段
7408
- * - 预览模式下禁止选择/编辑航线、航点
7409
- *
7410
- * @param options 飞行预览选项
7411
- * @returns 飞行预览控制器
7412
- *
7413
- * @example
7414
- * ```typescript
7415
- * const preview = pathManager.startFlightPreview({
7416
- * waylineData: myWayline,
7417
- * speed: 15, // 飞行速度 15m/s
7418
- * loop: true, // 循环播放
7419
- * trackHighlight: {
7420
- * enabled: true,
7421
- * traveledColor: Cesium.Color.fromCssColorString('#00FF88'),
7422
- * remainingColor: Cesium.Color.fromCssColorString('#4A90D9'),
7423
- * },
7424
- * });
7425
- *
7426
- * preview.start(); // 开始播放
7427
- * preview.pause(); // 暂停
7428
- * preview.resume(); // 继续
7429
- * preview.stop(); // 停止
7430
- * preview.seek(0.5); // 跳转到 50%
7431
- * preview.setSpeed(20); // 设置速度
7432
- *
7433
- * // 监听进度变化
7434
- * preview.onProgressChange.on(({ progress, waypointIndex }) => {
7435
- * console.log(`进度: ${(progress * 100).toFixed(1)}%`);
7436
- * });
7437
- *
7438
- * // 销毁
7439
- * preview.destroy();
7440
- * ```
7441
- */
7442
- startFlightPreview(options) {
7443
- return new FlightPreviewController(this.CesiumNS, this.viewer, options);
7444
- }
7445
6907
  resolveEntity(entityOrId) {
7446
6908
  return typeof entityOrId === "string" ? this.viewer.entities.getById(entityOrId) : entityOrId;
7447
6909
  }
@@ -7637,6 +7099,8 @@ var PolygonEditor = class {
7637
7099
  __publicField(this, "keydownHandler");
7638
7100
  // 保存编码前的原始名称,用于还原
7639
7101
  __publicField(this, "originalPolygonNames", /* @__PURE__ */ new Map());
7102
+ // 保存双击缩放是否被禁用的状态
7103
+ __publicField(this, "doubleClickZoomDisabled", false);
7640
7104
  }
7641
7105
  /**
7642
7106
  * 根据图层名称获取颜色配置
@@ -7911,12 +7375,62 @@ var PolygonEditor = class {
7911
7375
  }
7912
7376
  this.handler = void 0;
7913
7377
  }
7378
+ /**
7379
+ * 禁用 Cesium 默认的双击缩放行为
7380
+ * 在绘制/编辑过程中调用,防止双击导致相机乱飞
7381
+ */
7382
+ disableDoubleClickZoom() {
7383
+ if (this.doubleClickZoomDisabled) return;
7384
+ try {
7385
+ const C = this.CesiumNS;
7386
+ const cesiumWidget = this.viewer.cesiumWidget;
7387
+ if (cesiumWidget?.screenSpaceEventHandler) {
7388
+ cesiumWidget.screenSpaceEventHandler.removeInputAction(
7389
+ C.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
7390
+ );
7391
+ this.doubleClickZoomDisabled = true;
7392
+ }
7393
+ } catch (e) {
7394
+ console.warn("[PolygonEditor] \u7981\u7528\u53CC\u51FB\u7F29\u653E\u5931\u8D25:", e);
7395
+ }
7396
+ }
7397
+ /**
7398
+ * 恢复 Cesium 默认的双击缩放行为
7399
+ * 在绘制/编辑结束后调用
7400
+ */
7401
+ restoreDoubleClickZoom() {
7402
+ if (!this.doubleClickZoomDisabled) return;
7403
+ try {
7404
+ const C = this.CesiumNS;
7405
+ const viewer = this.viewer;
7406
+ const cesiumWidget = viewer.cesiumWidget;
7407
+ if (cesiumWidget?.screenSpaceEventHandler) {
7408
+ cesiumWidget.screenSpaceEventHandler.setInputAction(
7409
+ (movement) => {
7410
+ try {
7411
+ const pickedObject = viewer.scene.pick(movement.position);
7412
+ if (pickedObject?.id && viewer.trackedEntity !== pickedObject.id) {
7413
+ viewer.flyTo(pickedObject.id, { duration: 1.5 });
7414
+ }
7415
+ } catch {
7416
+ }
7417
+ },
7418
+ C.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
7419
+ );
7420
+ this.doubleClickZoomDisabled = false;
7421
+ }
7422
+ } catch (e) {
7423
+ console.warn("[PolygonEditor] \u6062\u590D\u53CC\u51FB\u7F29\u653E\u5931\u8D25:", e);
7424
+ this.doubleClickZoomDisabled = false;
7425
+ }
7426
+ }
7914
7427
  /**
7915
7428
  * 完成编辑/绘制的通用清理逻辑
7916
7429
  */
7917
7430
  finalizePolygonSession(layer, onComplete, entity) {
7918
7431
  this.destroyHandler();
7919
7432
  this.removeKeyboardListener();
7433
+ this.restoreDoubleClickZoom();
7920
7434
  if (entity && onComplete) {
7921
7435
  onComplete(entity);
7922
7436
  }
@@ -7959,6 +7473,7 @@ var PolygonEditor = class {
7959
7473
  }
7960
7474
  this.clearAllPolygonHighlights();
7961
7475
  this.stopInternal(layer);
7476
+ this.disableDoubleClickZoom();
7962
7477
  this.drawingPositions = [];
7963
7478
  this.drawHandles = [];
7964
7479
  this.rubberBandPoint = void 0;
@@ -8103,6 +7618,7 @@ var PolygonEditor = class {
8103
7618
  }
8104
7619
  this.destroyHandler();
8105
7620
  this.removeKeyboardListener();
7621
+ this.restoreDoubleClickZoom();
8106
7622
  }
8107
7623
  /**
8108
7624
  * 开始编辑多边形
@@ -8114,6 +7630,7 @@ var PolygonEditor = class {
8114
7630
  startEditing(polygonEntity, layer, onComplete) {
8115
7631
  const C = this.CesiumNS;
8116
7632
  this.cleanupHandles(layer);
7633
+ this.disableDoubleClickZoom();
8117
7634
  const colors = this.getColorConfig(layer.name);
8118
7635
  const line_color = colors.lineColor;
8119
7636
  const line_fina_color = colors.lineFinalColor;
@@ -9536,6 +9053,7 @@ var PolygonEditor = class {
9536
9053
  const nameStr = `circle_${(circleCount + 1).toString().padStart(2, "0")}`;
9537
9054
  this.clearAllPolygonHighlights();
9538
9055
  this.stopInternal();
9056
+ this.disableDoubleClickZoom();
9539
9057
  let centerPosition = null;
9540
9058
  let currentRadius = 0;
9541
9059
  let tempCenterPoint;
@@ -9749,6 +9267,7 @@ var PolygonEditor = class {
9749
9267
  currentRadius = 0;
9750
9268
  this.destroyHandler();
9751
9269
  this.removeKeyboardListener();
9270
+ this.restoreDoubleClickZoom();
9752
9271
  };
9753
9272
  this.handler && this.handler.setInputAction(
9754
9273
  cancelCircleDrawing,
@@ -9784,6 +9303,7 @@ var PolygonEditor = class {
9784
9303
  const line_fina_color = colors.lineFinalColor;
9785
9304
  this.clearAllPolygonHighlights();
9786
9305
  this.stopInternal();
9306
+ this.disableDoubleClickZoom();
9787
9307
  let pointCount = 0;
9788
9308
  this.handler = new C.ScreenSpaceEventHandler(this.viewer.scene.canvas);
9789
9309
  this.handler && this.handler.setInputAction(
@@ -9878,6 +9398,7 @@ var PolygonEditor = class {
9878
9398
  const stopPointDrawing = () => {
9879
9399
  this.destroyHandler();
9880
9400
  this.removeKeyboardListener();
9401
+ this.restoreDoubleClickZoom();
9881
9402
  };
9882
9403
  this.handler && this.handler.setInputAction(
9883
9404
  stopPointDrawing,
@@ -11383,6 +10904,368 @@ function convertPathToSinofly(data, options) {
11383
10904
  return sinoflyData;
11384
10905
  }
11385
10906
 
10907
+ // src/core/path-manager/PathSafetyChecker.ts
10908
+ var PathSafetyChecker = class {
10909
+ constructor(CesiumNS, viewer) {
10910
+ __publicField(this, "CesiumNS");
10911
+ __publicField(this, "viewer");
10912
+ this.CesiumNS = CesiumNS;
10913
+ this.viewer = viewer;
10914
+ }
10915
+ /**
10916
+ * 检测整条路径的安全性
10917
+ * @param positions 航点位置数组
10918
+ * @param options 检测选项
10919
+ */
10920
+ checkPath(positions, options) {
10921
+ const defaultDistance = options?.safetyDistance ?? 20;
10922
+ const terrainSafetyDistance = options?.terrainSafetyDistance ?? defaultDistance;
10923
+ const pointCloudSafetyDistance = options?.pointCloudSafetyDistance ?? defaultDistance;
10924
+ const minWaypointDistance = options?.minWaypointDistance ?? 0;
10925
+ const sampleInterval = options?.sampleInterval ?? 5;
10926
+ const results = [];
10927
+ const startSegment = 1 ;
10928
+ console.log("=".repeat(60));
10929
+ console.log("[PathSafetyChecker] \u5F00\u59CB\u5B89\u5168\u68C0\u6D4B");
10930
+ console.log(` \u5730\u5F62\u5B89\u5168\u8DDD\u79BB: ${terrainSafetyDistance}m`);
10931
+ console.log(` \u70B9\u4E91\u5B89\u5168\u8DDD\u79BB: ${pointCloudSafetyDistance}m`);
10932
+ if (minWaypointDistance > 0) {
10933
+ console.log(` \u822A\u70B9\u95F4\u6700\u5C0F\u8DDD\u79BB: ${minWaypointDistance}m`);
10934
+ }
10935
+ console.log(` \u91C7\u6837\u95F4\u9694: ${sampleInterval}m`);
10936
+ console.log(` \u822A\u70B9\u603B\u6570: ${positions.length}`);
10937
+ console.log(` \u68C0\u6D4B\u822A\u6BB5: ${startSegment} ~ ${positions.length - 2}`);
10938
+ console.log("=".repeat(60));
10939
+ for (let i = startSegment; i < positions.length - 1; i++) {
10940
+ const from = positions[i];
10941
+ const to = positions[i + 1];
10942
+ const segmentName = i === 1 ? "S" : String(i);
10943
+ console.log(`
10944
+ [\u68C0\u6D4B\u822A\u6BB5 ${segmentName}]`);
10945
+ const segmentResult = this.checkSegment(
10946
+ from,
10947
+ to,
10948
+ i,
10949
+ i + 1,
10950
+ terrainSafetyDistance,
10951
+ pointCloudSafetyDistance,
10952
+ minWaypointDistance,
10953
+ sampleInterval
10954
+ );
10955
+ results.push(segmentResult);
10956
+ const terrainStatus = segmentResult.minTerrainDistance < 0 ? "\u2753" : segmentResult.isTerrainSafe ? "\u2705" : "\u26A0\uFE0F";
10957
+ const pointCloudStatus = segmentResult.minPointCloudDistance < 0 ? "\u2705" : segmentResult.isPointCloudSafe ? "\u2705" : "\u26A0\uFE0F";
10958
+ const terrainDistStr = segmentResult.minTerrainDistance < 0 ? "\u672A\u68C0\u6D4B\u5230" : `${segmentResult.minTerrainDistance.toFixed(1)}m`;
10959
+ const pointCloudDistStr = segmentResult.minPointCloudDistance < 0 ? "\u65E0\u70B9\u4E91" : `${segmentResult.minPointCloudDistance.toFixed(1)}m`;
10960
+ console.log(` \u5730\u5F62\u8DDD\u79BB: ${terrainStatus} ${terrainDistStr} (\u8981\u6C42 >= ${terrainSafetyDistance}m)`);
10961
+ console.log(` \u70B9\u4E91\u8DDD\u79BB: ${pointCloudStatus} ${pointCloudDistStr} (\u8981\u6C42 >= ${pointCloudSafetyDistance}m)`);
10962
+ if (minWaypointDistance > 0) {
10963
+ const distanceStatus = segmentResult.isDistanceSafe ? "\u2705" : "\u26A0\uFE0F";
10964
+ console.log(` \u822A\u70B9\u95F4\u8DDD: ${distanceStatus} ${segmentResult.segmentLength.toFixed(1)}m (\u8981\u6C42 >= ${minWaypointDistance}m)`);
10965
+ }
10966
+ if (!segmentResult.isSafe) {
10967
+ console.log(` \u26A0\uFE0F \u8BE5\u822A\u6BB5\u4E0D\u5B89\u5168\uFF01\u5371\u9669\u70B9\u6570\u91CF: ${segmentResult.dangerPoints.length}`);
10968
+ }
10969
+ }
10970
+ const unsafeSegments = results.filter((r) => !r.isSafe);
10971
+ console.log("\n" + "=".repeat(60));
10972
+ console.log("[PathSafetyChecker] \u68C0\u6D4B\u5B8C\u6210");
10973
+ console.log(` \u603B\u822A\u6BB5\u6570: ${results.length}`);
10974
+ console.log(` \u5B89\u5168\u822A\u6BB5: ${results.length - unsafeSegments.length}`);
10975
+ console.log(` \u5371\u9669\u822A\u6BB5: ${unsafeSegments.length}`);
10976
+ if (unsafeSegments.length > 0) {
10977
+ console.log("\n\u26A0\uFE0F \u5371\u9669\u822A\u6BB5\u8BE6\u60C5:");
10978
+ unsafeSegments.forEach((seg) => {
10979
+ const issues = [];
10980
+ if (!seg.isTerrainSafe) {
10981
+ issues.push(`\u5730\u5F62\u8DDD\u79BB ${seg.minTerrainDistance.toFixed(1)}m < ${terrainSafetyDistance}m`);
10982
+ }
10983
+ if (!seg.isPointCloudSafe) {
10984
+ issues.push(`\u70B9\u4E91\u8DDD\u79BB ${seg.minPointCloudDistance.toFixed(1)}m < ${pointCloudSafetyDistance}m`);
10985
+ }
10986
+ if (!seg.isDistanceSafe) {
10987
+ issues.push(`\u822A\u70B9\u95F4\u8DDD ${seg.segmentLength.toFixed(1)}m < ${minWaypointDistance}m`);
10988
+ }
10989
+ const segName = seg.fromIndex === 1 ? "S" : String(seg.fromIndex);
10990
+ console.log(` \u822A\u6BB5 ${segName}: ${issues.join(", ")}`);
10991
+ });
10992
+ }
10993
+ console.log("=".repeat(60));
10994
+ return {
10995
+ allSafe: unsafeSegments.length === 0,
10996
+ segments: results,
10997
+ unsafeSegments,
10998
+ terrainSafetyDistance,
10999
+ pointCloudSafetyDistance,
11000
+ minWaypointDistance
11001
+ };
11002
+ }
11003
+ /**
11004
+ * 检测单个航段的安全性
11005
+ * 支持多方向检测(上下左右前后6方向)和按间隔采样
11006
+ */
11007
+ checkSegment(from, to, fromIndex, toIndex, terrainSafetyDistance, pointCloudSafetyDistance, minWaypointDistance, sampleInterval) {
11008
+ const C = this.CesiumNS;
11009
+ const scene = this.viewer.scene;
11010
+ const dangerPoints = [];
11011
+ let minTerrainDistance = Infinity;
11012
+ let minPointCloudDistance = Infinity;
11013
+ const segmentLength = C.Cartesian3.distance(from, to);
11014
+ const sampleCount = Math.max(2, Math.ceil(segmentLength / sampleInterval) + 1);
11015
+ for (let i = 0; i < sampleCount; i++) {
11016
+ const t = i / (sampleCount - 1);
11017
+ const samplePoint = C.Cartesian3.lerp(from, to, t, new C.Cartesian3());
11018
+ const cartographic = C.Cartographic.fromCartesian(samplePoint);
11019
+ const sampleHeight = cartographic.height;
11020
+ try {
11021
+ const terrainHeight = scene.globe.getHeight(cartographic);
11022
+ if (terrainHeight !== void 0 && terrainHeight !== null) {
11023
+ const terrainDistance = sampleHeight - terrainHeight;
11024
+ minTerrainDistance = Math.min(minTerrainDistance, terrainDistance);
11025
+ if (terrainDistance < terrainSafetyDistance) {
11026
+ dangerPoints.push(C.Cartesian3.clone(samplePoint));
11027
+ }
11028
+ }
11029
+ } catch (e) {
11030
+ }
11031
+ if (pointCloudSafetyDistance <= 0) {
11032
+ continue;
11033
+ }
11034
+ try {
11035
+ const forward = C.Cartesian3.subtract(to, from, new C.Cartesian3());
11036
+ C.Cartesian3.normalize(forward, forward);
11037
+ const localUp = C.Cartesian3.normalize(samplePoint, new C.Cartesian3());
11038
+ let right = C.Cartesian3.cross(forward, localUp, new C.Cartesian3());
11039
+ let rightMag = C.Cartesian3.magnitude(right);
11040
+ if (rightMag < 1e-3) {
11041
+ right = C.Cartesian3.cross(forward, new C.Cartesian3(1, 0, 0), new C.Cartesian3());
11042
+ rightMag = C.Cartesian3.magnitude(right);
11043
+ if (rightMag < 1e-3) {
11044
+ right = C.Cartesian3.cross(forward, new C.Cartesian3(0, 1, 0), new C.Cartesian3());
11045
+ rightMag = C.Cartesian3.magnitude(right);
11046
+ }
11047
+ }
11048
+ if (rightMag > 1e-3) {
11049
+ C.Cartesian3.normalize(right, right);
11050
+ } else {
11051
+ continue;
11052
+ }
11053
+ const up2 = C.Cartesian3.cross(right, forward, new C.Cartesian3());
11054
+ C.Cartesian3.normalize(up2, up2);
11055
+ const numDirections = 16;
11056
+ for (let d = 0; d < numDirections; d++) {
11057
+ const angle = d / numDirections * Math.PI * 2;
11058
+ const outwardDir = new C.Cartesian3();
11059
+ C.Cartesian3.multiplyByScalar(right, Math.cos(angle), outwardDir);
11060
+ const upComponent = new C.Cartesian3();
11061
+ C.Cartesian3.multiplyByScalar(up2, Math.sin(angle), upComponent);
11062
+ C.Cartesian3.add(outwardDir, upComponent, outwardDir);
11063
+ C.Cartesian3.normalize(outwardDir, outwardDir);
11064
+ const surfacePoint = new C.Cartesian3();
11065
+ C.Cartesian3.multiplyByScalar(outwardDir, pointCloudSafetyDistance, surfacePoint);
11066
+ C.Cartesian3.add(samplePoint, surfacePoint, surfacePoint);
11067
+ const inwardDir = C.Cartesian3.negate(outwardDir, new C.Cartesian3());
11068
+ const ray = new C.Ray(surfacePoint, inwardDir);
11069
+ const intersections = scene.drillPickFromRay(ray, 10);
11070
+ for (const intersection of intersections || []) {
11071
+ if (!intersection?.position) continue;
11072
+ const primitive = intersection.object?.primitive;
11073
+ const primitiveName = primitive?.constructor?.name || "";
11074
+ if (primitiveName === "Polyline" || primitiveName === "PointPrimitive" || primitiveName === "PointPrimitiveCollection" || primitiveName === "Billboard" || primitiveName === "Label" || primitiveName === "Cylinder" || primitiveName === "Ellipse" || primitiveName === "Model" || primitiveName === "ModelExperimental" || primitiveName === "Globe" || primitiveName === "GlobeSurfaceTile") {
11075
+ continue;
11076
+ }
11077
+ if (primitiveName === "Primitive" && primitive?.appearance) {
11078
+ const appearanceName = primitive.appearance?.constructor?.name || "";
11079
+ if (appearanceName === "PerInstanceColorAppearance" || appearanceName === "MaterialAppearance" || appearanceName === "EllipsoidSurfaceAppearance") {
11080
+ continue;
11081
+ }
11082
+ }
11083
+ const is3DTiles = primitive?.tileset !== void 0 || primitive?._tileset !== void 0 || intersection.object?.content?.tileset !== void 0;
11084
+ if (!is3DTiles && primitiveName !== "Cesium3DTilePointFeature") {
11085
+ continue;
11086
+ }
11087
+ if (i === 0 && d === 0) {
11088
+ console.log("[\u8C03\u8BD5] \u68C0\u6D4B\u5230\u70B9\u4E91\u5BF9\u8C61:", primitiveName);
11089
+ console.log("[\u8C03\u8BD5] primitive:", primitive);
11090
+ }
11091
+ const distFromCenter = C.Cartesian3.distance(samplePoint, intersection.position);
11092
+ minPointCloudDistance = Math.min(minPointCloudDistance, distFromCenter);
11093
+ if (distFromCenter < pointCloudSafetyDistance) {
11094
+ const alreadyAdded = dangerPoints.some(
11095
+ (p) => C.Cartesian3.distance(p, samplePoint) < 0.1
11096
+ );
11097
+ if (!alreadyAdded) {
11098
+ dangerPoints.push(C.Cartesian3.clone(samplePoint));
11099
+ console.log(`[\u5371\u9669] \u91C7\u6837\u70B9 ${i}, \u65B9\u5411 ${d}: \u70B9\u4E91(${primitiveName})\u5728\u5706\u67F1\u4F53\u5185\uFF0C\u8DDD\u4E2D\u5FC3 ${distFromCenter.toFixed(1)}m`);
11100
+ }
11101
+ }
11102
+ break;
11103
+ }
11104
+ }
11105
+ } catch (e) {
11106
+ }
11107
+ }
11108
+ const isTerrainSafe = minTerrainDistance !== Infinity && minTerrainDistance >= terrainSafetyDistance;
11109
+ const isPointCloudSafe = minPointCloudDistance === Infinity || minPointCloudDistance >= pointCloudSafetyDistance;
11110
+ const isDistanceSafe = minWaypointDistance <= 0 || segmentLength >= minWaypointDistance;
11111
+ return {
11112
+ fromIndex,
11113
+ toIndex,
11114
+ isTerrainSafe,
11115
+ isPointCloudSafe,
11116
+ isDistanceSafe,
11117
+ isSafe: isTerrainSafe && isPointCloudSafe && isDistanceSafe,
11118
+ minTerrainDistance: minTerrainDistance === Infinity ? -1 : minTerrainDistance,
11119
+ minPointCloudDistance: minPointCloudDistance === Infinity ? -1 : minPointCloudDistance,
11120
+ segmentLength,
11121
+ dangerPoints
11122
+ };
11123
+ }
11124
+ /**
11125
+ * 可视化危险点(用于调试)
11126
+ */
11127
+ visualizeDangerPoints(result, layer) {
11128
+ const C = this.CesiumNS;
11129
+ const entities = [];
11130
+ result.unsafeSegments.forEach((seg) => {
11131
+ seg.dangerPoints.forEach((point, idx) => {
11132
+ const entity = layer.entities.add({
11133
+ position: point,
11134
+ point: {
11135
+ pixelSize: 10,
11136
+ color: C.Color.RED,
11137
+ outlineColor: C.Color.WHITE,
11138
+ outlineWidth: 2,
11139
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
11140
+ },
11141
+ label: {
11142
+ text: `\u26A0\uFE0F ${seg.fromIndex}-${seg.toIndex}`,
11143
+ font: "12px sans-serif",
11144
+ fillColor: C.Color.RED,
11145
+ outlineColor: C.Color.WHITE,
11146
+ outlineWidth: 2,
11147
+ style: C.LabelStyle.FILL_AND_OUTLINE,
11148
+ pixelOffset: new C.Cartesian2(0, -20),
11149
+ disableDepthTestDistance: Number.POSITIVE_INFINITY
11150
+ },
11151
+ properties: {
11152
+ _type: "safety-danger-point",
11153
+ _segmentFrom: seg.fromIndex,
11154
+ _segmentTo: seg.toIndex
11155
+ }
11156
+ });
11157
+ entities.push(entity);
11158
+ });
11159
+ });
11160
+ return entities;
11161
+ }
11162
+ /**
11163
+ * 清除危险点可视化
11164
+ */
11165
+ clearDangerPoints(layer) {
11166
+ const C = this.CesiumNS;
11167
+ const toRemove = [];
11168
+ layer.entities.values.forEach((entity) => {
11169
+ const type = entity.properties?._type?.getValue?.(C.JulianDate.now());
11170
+ if (type === "safety-danger-point") {
11171
+ toRemove.push(entity);
11172
+ }
11173
+ });
11174
+ toRemove.forEach((entity) => {
11175
+ layer.entities.remove(entity);
11176
+ });
11177
+ }
11178
+ /**
11179
+ * 沿航线创建3D安全管道可视化(使用 Cylinder)
11180
+ * 每个航段创建一个水平圆柱体,圆柱轴心与航线重合
11181
+ * @param positions 航点位置数组
11182
+ * @param layer 实体图层
11183
+ * @param options 可视化选项
11184
+ */
11185
+ visualizeSafetyTube(positions, layer, options) {
11186
+ const C = this.CesiumNS;
11187
+ const entities = [];
11188
+ const radius = options?.radius ?? 20;
11189
+ const color = options?.color ?? C.Color.YELLOW.withAlpha(0.3);
11190
+ const skipFirst = options?.skipFirstSegment ?? true;
11191
+ const startIndex = skipFirst ? 1 : 0;
11192
+ if (positions.length < startIndex + 2) {
11193
+ console.warn("[PathSafetyChecker] \u822A\u70B9\u4E0D\u8DB3\uFF0C\u65E0\u6CD5\u521B\u5EFA\u5B89\u5168\u7BA1\u9053");
11194
+ return entities;
11195
+ }
11196
+ console.log("[PathSafetyChecker] \u521B\u5EFA\u5B89\u5168\u7BA1\u9053\uFF08Cylinder \u65B9\u5F0F\uFF09");
11197
+ console.log(` \u534A\u5F84: ${radius}m`);
11198
+ console.log(` \u8D77\u59CB\u822A\u6BB5: ${startIndex}`);
11199
+ for (let i = startIndex; i < positions.length - 1; i++) {
11200
+ const from = positions[i];
11201
+ const to = positions[i + 1];
11202
+ const midPoint = C.Cartesian3.midpoint(from, to, new C.Cartesian3());
11203
+ const length = C.Cartesian3.distance(from, to);
11204
+ const direction = C.Cartesian3.subtract(to, from, new C.Cartesian3());
11205
+ C.Cartesian3.normalize(direction, direction);
11206
+ const localUp = C.Cartesian3.normalize(midPoint, new C.Cartesian3());
11207
+ const zAxis = direction;
11208
+ let xAxis = C.Cartesian3.cross(localUp, zAxis, new C.Cartesian3());
11209
+ if (C.Cartesian3.magnitude(xAxis) < 1e-3) {
11210
+ xAxis = C.Cartesian3.cross(new C.Cartesian3(1, 0, 0), zAxis, new C.Cartesian3());
11211
+ }
11212
+ C.Cartesian3.normalize(xAxis, xAxis);
11213
+ const yAxis = C.Cartesian3.cross(zAxis, xAxis, new C.Cartesian3());
11214
+ C.Cartesian3.normalize(yAxis, yAxis);
11215
+ const rotationMatrix = new C.Matrix3(
11216
+ xAxis.x,
11217
+ yAxis.x,
11218
+ zAxis.x,
11219
+ xAxis.y,
11220
+ yAxis.y,
11221
+ zAxis.y,
11222
+ xAxis.z,
11223
+ yAxis.z,
11224
+ zAxis.z
11225
+ );
11226
+ const orientation = C.Quaternion.fromRotationMatrix(rotationMatrix);
11227
+ console.log(`[\u822A\u6BB5 ${i}] \u957F\u5EA6: ${length.toFixed(1)}m, \u65B9\u5411: (${direction.x.toFixed(2)}, ${direction.y.toFixed(2)}, ${direction.z.toFixed(2)})`);
11228
+ const entity = layer.entities.add({
11229
+ position: midPoint,
11230
+ orientation,
11231
+ cylinder: {
11232
+ length,
11233
+ topRadius: radius,
11234
+ bottomRadius: radius,
11235
+ material: color,
11236
+ outline: true,
11237
+ outlineColor: C.Color.YELLOW,
11238
+ outlineWidth: 1
11239
+ },
11240
+ properties: {
11241
+ _type: "safety-tube",
11242
+ _segmentIndex: i
11243
+ }
11244
+ });
11245
+ entities.push(entity);
11246
+ }
11247
+ console.log(`[PathSafetyChecker] \u521B\u5EFA\u4E86 ${entities.length} \u4E2A\u822A\u6BB5\u5706\u67F1`);
11248
+ return entities;
11249
+ }
11250
+ /**
11251
+ * 清除安全管道可视化
11252
+ */
11253
+ clearSafetyTube(layer) {
11254
+ const C = this.CesiumNS;
11255
+ const toRemove = [];
11256
+ layer.entities.values.forEach((entity) => {
11257
+ const type = entity.properties?._type?.getValue?.(C.JulianDate.now());
11258
+ if (type === "safety-tube") {
11259
+ toRemove.push(entity);
11260
+ }
11261
+ });
11262
+ toRemove.forEach((entity) => {
11263
+ layer.entities.remove(entity);
11264
+ });
11265
+ console.log(`[PathSafetyChecker] \u6E05\u9664\u4E86 ${toRemove.length} \u4E2A\u5B89\u5168\u7BA1\u9053`);
11266
+ }
11267
+ };
11268
+
11386
11269
  // src/index.ts
11387
11270
  var placeholder = { ready: true };
11388
11271
 
@@ -11391,9 +11274,9 @@ exports.CameraEventBus = CameraEventBus;
11391
11274
  exports.CameraFOVController = CameraFOVController;
11392
11275
  exports.CameraManager = CameraManager;
11393
11276
  exports.Emitter = Emitter;
11394
- exports.FlightPreviewController = FlightPreviewController;
11395
11277
  exports.FrustumPyramid = FrustumPyramid;
11396
11278
  exports.LayerManager = LayerManager;
11279
+ exports.PathSafetyChecker = PathSafetyChecker;
11397
11280
  exports.PolygonEditor = PolygonEditor;
11398
11281
  exports.SceneManager = SceneManager;
11399
11282
  exports.Selector = Selector;