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