@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.
@@ -1286,12 +1286,10 @@ var StateManager = class {
1286
1286
  __publicField(this, "editing", false);
1287
1287
  __publicField(this, "editingTarget");
1288
1288
  __publicField(this, "previousView");
1289
- __publicField(this, "flightPreviewing", false);
1290
1289
  // Emitters
1291
1290
  __publicField(this, "onSelectionChange", new Emitter());
1292
1291
  __publicField(this, "onEditingChange", new Emitter());
1293
1292
  __publicField(this, "onPreviousViewChange", new Emitter());
1294
- __publicField(this, "onFlightPreviewChange", new Emitter());
1295
1293
  }
1296
1294
  // Selection
1297
1295
  setSelected(next) {
@@ -1338,35 +1336,6 @@ var StateManager = class {
1338
1336
  getPreviousCameraView() {
1339
1337
  return this.previousView;
1340
1338
  }
1341
- // Flight preview mode
1342
- /**
1343
- * 开始飞行预览模式(锁定交互)
1344
- */
1345
- startFlightPreview() {
1346
- if (this.flightPreviewing) return;
1347
- this.flightPreviewing = true;
1348
- this.onFlightPreviewChange.emit({ previewing: true });
1349
- }
1350
- /**
1351
- * 停止飞行预览模式(恢复交互)
1352
- */
1353
- stopFlightPreview() {
1354
- if (!this.flightPreviewing) return;
1355
- this.flightPreviewing = false;
1356
- this.onFlightPreviewChange.emit({ previewing: false });
1357
- }
1358
- /**
1359
- * 检查是否处于飞行预览模式
1360
- */
1361
- isFlightPreviewing() {
1362
- return this.flightPreviewing;
1363
- }
1364
- /**
1365
- * 检查当前是否允许交互(非编辑模式且非飞行预览模式)
1366
- */
1367
- isInteractionAllowed() {
1368
- return !this.editing && !this.flightPreviewing;
1369
- }
1370
1339
  };
1371
1340
  var globalState = new StateManager();
1372
1341
 
@@ -2820,6 +2789,10 @@ var AirplaneCursor = class {
2820
2789
  __publicField(this, "pyramidEntities", []);
2821
2790
  // 临时数据源,用于存储金字塔实体(避免污染宿主图层)
2822
2791
  __publicField(this, "pyramidLayer");
2792
+ // 缓存的缩放因子,仅在相机变化时更新
2793
+ __publicField(this, "cachedScaleFactor", 1);
2794
+ // 相机变化事件监听器
2795
+ __publicField(this, "cameraChangedListener");
2823
2796
  const C = this.CesiumNS;
2824
2797
  this.opts = opts;
2825
2798
  this.pose = { position: startPosition, heading: 0, pitch: -10, roll: 0 };
@@ -2839,14 +2812,39 @@ var AirplaneCursor = class {
2839
2812
  this.viewer.dataSources.add(this.pyramidLayer);
2840
2813
  }
2841
2814
  const modelHeadingOffset = 270;
2842
- const modelVerticalOffset = 35;
2843
- const modelForwardOffset = 5;
2844
- const modelRightOffset = -30;
2815
+ const baseVerticalOffset = 10;
2816
+ const baseForwardOffset = 5;
2817
+ const baseRightOffset = -9;
2818
+ const baseScale = 0.15;
2819
+ const minScale = 0.025;
2820
+ const maxScale = 1.5;
2821
+ const referenceDistance = 800;
2822
+ const computeScaleFactor = () => {
2823
+ try {
2824
+ const cameraPos = this.viewer.camera.positionWC;
2825
+ const modelPos = this.pose.position;
2826
+ const distance = C.Cartesian3.distance(cameraPos, modelPos);
2827
+ const rawScale = baseScale * (distance / referenceDistance);
2828
+ const clampedScale = Math.max(minScale, Math.min(maxScale, rawScale));
2829
+ return clampedScale / baseScale;
2830
+ } catch {
2831
+ return 1;
2832
+ }
2833
+ };
2834
+ this.cachedScaleFactor = computeScaleFactor();
2835
+ this.cameraChangedListener = this.viewer.camera.changed.addEventListener(() => {
2836
+ this.cachedScaleFactor = computeScaleFactor();
2837
+ this.viewer.scene?.requestRender?.();
2838
+ });
2845
2839
  this.modelEntity = this.pyramidLayer.entities.add({
2846
2840
  position: new C.CallbackProperty(() => {
2847
2841
  const pos = this.pose.position;
2848
2842
  const headingRad = C.Math.toRadians(this.pose.heading);
2849
2843
  const pitchRad = C.Math.toRadians(this.pose.pitch);
2844
+ const scaleFactor = this.cachedScaleFactor;
2845
+ const modelVerticalOffset = baseVerticalOffset * scaleFactor;
2846
+ const modelForwardOffset = baseForwardOffset * scaleFactor;
2847
+ const modelRightOffset = baseRightOffset * scaleFactor;
2850
2848
  const enu = C.Transforms.eastNorthUpToFixedFrame(pos);
2851
2849
  const east = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 0, new C.Cartesian4()));
2852
2850
  const north = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 1, new C.Cartesian4()));
@@ -2887,9 +2885,10 @@ var AirplaneCursor = class {
2887
2885
  }, false),
2888
2886
  model: {
2889
2887
  uri: wurenji_default,
2890
- scale: 0.5,
2891
- minimumPixelSize: 32,
2892
- maximumScale: 100,
2888
+ scale: new C.CallbackProperty(() => {
2889
+ return baseScale * this.cachedScaleFactor;
2890
+ }, false),
2891
+ minimumPixelSize: 24,
2893
2892
  silhouetteColor: C.Color.CYAN,
2894
2893
  silhouetteSize: 1
2895
2894
  },
@@ -3251,6 +3250,11 @@ var AirplaneCursor = class {
3251
3250
  } catch {
3252
3251
  }
3253
3252
  this.pyramidLayer = void 0;
3253
+ try {
3254
+ this.cameraChangedListener?.();
3255
+ } catch {
3256
+ }
3257
+ this.cameraChangedListener = void 0;
3254
3258
  try {
3255
3259
  this.frustum?.destroy();
3256
3260
  } catch {
@@ -4569,34 +4573,6 @@ var PathEditingEventHandler = class {
4569
4573
  * 🆕 处理快速编辑模式的点击
4570
4574
  */
4571
4575
  handleQuickEditClick(movement) {
4572
- const C = this.CesiumNS;
4573
- try {
4574
- const picked = this.viewer.scene.pick?.(movement.position);
4575
- const entity = picked?.id;
4576
- if (entity?.properties) {
4577
- const props = entity.properties;
4578
- const vertexIndex = props._vertexIndex?.getValue?.() ?? props._vertexIndex;
4579
- const type = props._type?.getValue?.() ?? props._type;
4580
- if (typeof vertexIndex === "number" && (type === "path-vertex" || type === "vertex-label")) {
4581
- if (vertexIndex !== this.hiddenClimbIndex && vertexIndex !== 0) {
4582
- console.log("[QuickEdit] \u70B9\u51FB\u5230\u5DF2\u5B58\u5728\u822A\u70B9\uFF0C\u9009\u4E2D\u800C\u975E\u65B0\u589E:", vertexIndex);
4583
- if (this.callbacks.onVertexSelect) {
4584
- this.callbacks.onVertexSelect(vertexIndex);
4585
- }
4586
- if (this.callbacks.onVertexSelectDetail) {
4587
- try {
4588
- const detailInfo = this.buildVertexDetailInfo(vertexIndex);
4589
- this.callbacks.onVertexSelectDetail(detailInfo);
4590
- } catch (error) {
4591
- console.error("Error building vertex detail info:", error);
4592
- }
4593
- }
4594
- return;
4595
- }
4596
- }
4597
- }
4598
- } catch (error) {
4599
- }
4600
4576
  const position = this.getPositionFromMouse(movement);
4601
4577
  if (!position) {
4602
4578
  console.warn("[QuickEdit] \u65E0\u6CD5\u83B7\u53D6\u70B9\u51FB\u4F4D\u7F6E");
@@ -4605,6 +4581,7 @@ var PathEditingEventHandler = class {
4605
4581
  const quickEditOptions = this.callbacks.getQuickEditOptions?.();
4606
4582
  if (!quickEditOptions) return;
4607
4583
  const { climbHeight, altitudeMode } = quickEditOptions;
4584
+ const C = this.CesiumNS;
4608
4585
  const originalCarto = C.Cartographic.fromCartesian(position);
4609
4586
  const originalHeight = originalCarto.height;
4610
4587
  const newPosition = this.applyClimbHeight(position, climbHeight, altitudeMode);
@@ -5459,10 +5436,6 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
5459
5436
  );
5460
5437
  const cleanupSession = () => {
5461
5438
  setActiveIndex(void 0);
5462
- try {
5463
- preview?.destroy();
5464
- } catch {
5465
- }
5466
5439
  try {
5467
5440
  handles.forEach((handle) => {
5468
5441
  if (handle) {
@@ -5872,6 +5845,13 @@ var ArrowShape = class {
5872
5845
  __publicField(this, "baseAlpha");
5873
5846
  __publicField(this, "outline");
5874
5847
  __publicField(this, "outlineColor");
5848
+ // 动态缩放相关
5849
+ __publicField(this, "dynamicScale");
5850
+ __publicField(this, "viewer");
5851
+ __publicField(this, "referenceDistance");
5852
+ __publicField(this, "minScale");
5853
+ __publicField(this, "maxScale");
5854
+ __publicField(this, "cachedScaleFactor", 1);
5875
5855
  const C = this.CesiumNS;
5876
5856
  this.shaftHeight = opts.shaftHeight ?? 17;
5877
5857
  this.headHeight = opts.headHeight ?? 8;
@@ -5879,12 +5859,17 @@ var ArrowShape = class {
5879
5859
  this.shaftRadius = opts.shaftRadius ?? 3;
5880
5860
  this.headRadius = opts.headRadius ?? 6;
5881
5861
  this.baseRadius = opts.baseRadius ?? 8;
5882
- this.color = opts.color ? C.Color.fromCssColorString(opts.color) : C.Color.fromCssColorString("#2E77FB");
5862
+ this.color = opts.color ? C.Color.fromCssColorString(opts.color) : C.Color.fromCssColorString("#FFD700");
5883
5863
  this.shaftAlpha = opts.shaftAlpha ?? 1;
5884
5864
  this.headAlpha = opts.headAlpha ?? 1;
5885
- this.baseAlpha = opts.baseAlpha ?? 1;
5865
+ this.baseAlpha = opts.baseAlpha ?? 0.65;
5886
5866
  this.outline = opts.outline ?? false;
5887
5867
  this.outlineColor = opts.outlineColor ? C.Color.fromCssColorString(opts.outlineColor) : this.color.darken(0.3, new C.Color());
5868
+ this.dynamicScale = opts.dynamicScale ?? false;
5869
+ this.viewer = opts.viewer;
5870
+ this.referenceDistance = opts.referenceDistance ?? 500;
5871
+ this.minScale = opts.minScale ?? 0.3;
5872
+ this.maxScale = opts.maxScale ?? 3;
5888
5873
  }
5889
5874
  /**
5890
5875
  * Create arrow entities at given position
@@ -5902,22 +5887,40 @@ var ArrowShape = class {
5902
5887
  const carto = C.Cartographic.fromCartesian(position);
5903
5888
  const lon = C.Math.toDegrees(carto.longitude);
5904
5889
  const lat = C.Math.toDegrees(carto.latitude);
5905
- const shaftPosition = C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight / 2);
5906
- const headPosition = C.Cartesian3.fromDegrees(
5907
- lon,
5908
- lat,
5909
- baseHeight + this.shaftHeight + this.headHeight / 2
5910
- );
5890
+ const computeScaleFactor = () => {
5891
+ if (!this.dynamicScale || !this.viewer) return 1;
5892
+ try {
5893
+ const cameraPos = this.viewer.camera.positionWC;
5894
+ const distance = C.Cartesian3.distance(cameraPos, position);
5895
+ const rawScale = distance / this.referenceDistance;
5896
+ return Math.max(this.minScale, Math.min(this.maxScale, rawScale));
5897
+ } catch {
5898
+ return 1;
5899
+ }
5900
+ };
5901
+ if (this.dynamicScale && this.viewer) {
5902
+ this.viewer.scene.preRender.addEventListener(() => {
5903
+ this.cachedScaleFactor = computeScaleFactor();
5904
+ });
5905
+ }
5911
5906
  const hpr = new C.HeadingPitchRoll(0, 0, 0);
5912
5907
  const baseOrientation = C.Transforms.headingPitchRollQuaternion(position, hpr);
5913
5908
  const fixedOrientation = new C.ConstantProperty(baseOrientation);
5909
+ const getShaftPosition = () => {
5910
+ const scale = this.cachedScaleFactor;
5911
+ return C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight * scale / 2);
5912
+ };
5913
+ const getHeadPosition = () => {
5914
+ const scale = this.cachedScaleFactor;
5915
+ return C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight * scale + this.headHeight * scale / 2);
5916
+ };
5914
5917
  const shaftEntity = layer.entities.add({
5915
- position: new C.ConstantPositionProperty(shaftPosition),
5918
+ position: this.dynamicScale ? new C.CallbackProperty(getShaftPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight / 2)),
5916
5919
  orientation: fixedOrientation,
5917
5920
  cylinder: {
5918
- length: this.shaftHeight,
5919
- topRadius: this.shaftRadius,
5920
- bottomRadius: this.shaftRadius,
5921
+ length: this.dynamicScale ? new C.CallbackProperty(() => this.shaftHeight * this.cachedScaleFactor, false) : this.shaftHeight,
5922
+ topRadius: this.dynamicScale ? new C.CallbackProperty(() => this.shaftRadius * this.cachedScaleFactor, false) : this.shaftRadius,
5923
+ bottomRadius: this.dynamicScale ? new C.CallbackProperty(() => this.shaftRadius * this.cachedScaleFactor, false) : this.shaftRadius,
5921
5924
  material: this.color.withAlpha(this.shaftAlpha),
5922
5925
  outline: this.outline,
5923
5926
  outlineColor: this.outlineColor,
@@ -5930,13 +5933,13 @@ var ArrowShape = class {
5930
5933
  });
5931
5934
  entities.push(shaftEntity);
5932
5935
  const headEntity = layer.entities.add({
5933
- position: new C.ConstantPositionProperty(headPosition),
5936
+ position: this.dynamicScale ? new C.CallbackProperty(getHeadPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight + this.headHeight / 2)),
5934
5937
  orientation: fixedOrientation,
5935
5938
  cylinder: {
5936
- length: this.headHeight,
5939
+ length: this.dynamicScale ? new C.CallbackProperty(() => this.headHeight * this.cachedScaleFactor, false) : this.headHeight,
5937
5940
  topRadius: 0,
5938
5941
  // Creates cone shape
5939
- bottomRadius: this.headRadius,
5942
+ bottomRadius: this.dynamicScale ? new C.CallbackProperty(() => this.headRadius * this.cachedScaleFactor, false) : this.headRadius,
5940
5943
  material: this.color.withAlpha(this.headAlpha),
5941
5944
  outline: this.outline,
5942
5945
  outlineColor: this.outlineColor,
@@ -5951,8 +5954,8 @@ var ArrowShape = class {
5951
5954
  const baseEntity = layer.entities.add({
5952
5955
  position,
5953
5956
  ellipse: {
5954
- semiMajorAxis: this.baseRadius,
5955
- semiMinorAxis: this.baseRadius,
5957
+ semiMajorAxis: this.dynamicScale ? new C.CallbackProperty(() => this.baseRadius * this.cachedScaleFactor, false) : this.baseRadius,
5958
+ semiMinorAxis: this.dynamicScale ? new C.CallbackProperty(() => this.baseRadius * this.cachedScaleFactor, false) : this.baseRadius,
5956
5959
  height: baseHeight,
5957
5960
  material: this.color.withAlpha(this.baseAlpha),
5958
5961
  outline: this.outline,
@@ -6091,7 +6094,13 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
6091
6094
  color: "#FFD700",
6092
6095
  shaftAlpha: 1,
6093
6096
  headAlpha: 1,
6094
- baseAlpha: 0.5
6097
+ baseAlpha: 0.5,
6098
+ // 启用动态缩放,和无人机模型一样根据相机距离调整大小
6099
+ dynamicScale: true,
6100
+ viewer,
6101
+ referenceDistance: 300,
6102
+ minScale: 0.3,
6103
+ maxScale: 2
6095
6104
  });
6096
6105
  const arrowEntities = arrowShape.createEntities(
6097
6106
  layer,
@@ -6590,7 +6599,13 @@ function renderFlightPath(CesiumNS, viewer, options) {
6590
6599
  color: "#FFD700",
6591
6600
  shaftAlpha: 0.98,
6592
6601
  headAlpha: 0.98,
6593
- baseAlpha: 0.65
6602
+ baseAlpha: 0.65,
6603
+ // 启用动态缩放
6604
+ dynamicScale: true,
6605
+ viewer,
6606
+ referenceDistance: 300,
6607
+ minScale: 0.3,
6608
+ maxScale: 2
6594
6609
  });
6595
6610
  arrowShape.createEntities(
6596
6611
  layer,
@@ -6714,515 +6729,6 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
6714
6729
  return { entity, controller };
6715
6730
  }
6716
6731
 
6717
- // src/core/path-manager/FlightPreviewController.ts
6718
- var FlightPreviewController = class {
6719
- constructor(CesiumNS, viewer, options) {
6720
- this.CesiumNS = CesiumNS;
6721
- this.viewer = viewer;
6722
- this.options = options;
6723
- // ========== 状态 ==========
6724
- __publicField(this, "state", "stopped");
6725
- __publicField(this, "destroyed", false);
6726
- // ========== 配置 ==========
6727
- __publicField(this, "speed");
6728
- __publicField(this, "loop");
6729
- __publicField(this, "showFrustum");
6730
- __publicField(this, "trackHighlightOptions");
6731
- // ========== 路径数据 ==========
6732
- __publicField(this, "waypoints", []);
6733
- __publicField(this, "totalDistance", 0);
6734
- // ========== 动画状态 ==========
6735
- __publicField(this, "currentDistance", 0);
6736
- // 当前已飞行距离
6737
- __publicField(this, "animationFrameId");
6738
- __publicField(this, "lastFrameTime");
6739
- // ========== 可视化组件 ==========
6740
- __publicField(this, "layer");
6741
- __publicField(this, "droneEntity");
6742
- __publicField(this, "frustum");
6743
- __publicField(this, "pathPreview");
6744
- // ========== 轨迹高亮 ==========
6745
- __publicField(this, "traveledPathEntity");
6746
- __publicField(this, "remainingPathEntity");
6747
- // ========== 事件 ==========
6748
- __publicField(this, "onStateChange", new Emitter());
6749
- __publicField(this, "onProgressChange", new Emitter());
6750
- this.speed = options.speed ?? 10;
6751
- this.loop = options.loop ?? true;
6752
- this.showFrustum = options.showFrustum ?? true;
6753
- this.trackHighlightOptions = {
6754
- enabled: true,
6755
- traveledWidth: 6,
6756
- remainingWidth: 4,
6757
- ...options.trackHighlight
6758
- };
6759
- this.initLayer();
6760
- this.parseWaylineData();
6761
- this.initVisualization();
6762
- console.log("[FlightPreviewController] \u521D\u59CB\u5316\u5B8C\u6210", {
6763
- waypointCount: this.waypoints.length,
6764
- totalDistance: this.totalDistance,
6765
- speed: this.speed
6766
- });
6767
- }
6768
- // ==================== 公共方法 ====================
6769
- /** 开始预览 */
6770
- start() {
6771
- if (this.destroyed) return;
6772
- if (this.state === "playing") return;
6773
- if (this.state === "stopped") {
6774
- this.currentDistance = 0;
6775
- }
6776
- this.setState("playing");
6777
- this.lastFrameTime = performance.now();
6778
- this.startAnimationLoop();
6779
- globalState.startFlightPreview();
6780
- this.pathPreview?.show();
6781
- console.log("[FlightPreviewController] \u5F00\u59CB\u64AD\u653E");
6782
- }
6783
- /** 暂停 */
6784
- pause() {
6785
- if (this.destroyed) return;
6786
- if (this.state !== "playing") return;
6787
- this.setState("paused");
6788
- this.stopAnimationLoop();
6789
- console.log("[FlightPreviewController] \u6682\u505C");
6790
- }
6791
- /** 继续 */
6792
- resume() {
6793
- if (this.destroyed) return;
6794
- if (this.state !== "paused") return;
6795
- this.setState("playing");
6796
- this.lastFrameTime = performance.now();
6797
- this.startAnimationLoop();
6798
- console.log("[FlightPreviewController] \u7EE7\u7EED\u64AD\u653E");
6799
- }
6800
- /** 停止并重置 */
6801
- stop() {
6802
- if (this.destroyed) return;
6803
- this.setState("stopped");
6804
- this.stopAnimationLoop();
6805
- this.currentDistance = 0;
6806
- globalState.stopFlightPreview();
6807
- this.updateVisualization();
6808
- this.pathPreview?.hide();
6809
- console.log("[FlightPreviewController] \u505C\u6B62");
6810
- }
6811
- /** 跳转到指定进度 (0-1) */
6812
- seek(progress) {
6813
- if (this.destroyed) return;
6814
- const clampedProgress = Math.max(0, Math.min(1, progress));
6815
- this.currentDistance = clampedProgress * this.totalDistance;
6816
- this.updateVisualization();
6817
- console.log("[FlightPreviewController] \u8DF3\u8F6C\u5230", (clampedProgress * 100).toFixed(1) + "%");
6818
- }
6819
- /** 设置速度 */
6820
- setSpeed(speed) {
6821
- this.speed = Math.max(1, speed);
6822
- console.log("[FlightPreviewController] \u8BBE\u7F6E\u901F\u5EA6:", this.speed, "m/s");
6823
- }
6824
- /** 获取当前状态 */
6825
- getState() {
6826
- return this.state;
6827
- }
6828
- /** 获取当前进度 (0-1) */
6829
- getProgress() {
6830
- if (this.totalDistance === 0) return 0;
6831
- return this.currentDistance / this.totalDistance;
6832
- }
6833
- /** 获取当前航点索引 */
6834
- getCurrentWaypointIndex() {
6835
- return this.findCurrentSegment().segmentIndex;
6836
- }
6837
- /** 销毁 */
6838
- destroy() {
6839
- if (this.destroyed) return;
6840
- this.destroyed = true;
6841
- this.stopAnimationLoop();
6842
- globalState.stopFlightPreview();
6843
- this.destroyVisualization();
6844
- this.onStateChange.clear();
6845
- this.onProgressChange.clear();
6846
- console.log("[FlightPreviewController] \u5DF2\u9500\u6BC1");
6847
- }
6848
- // ==================== 私有方法 - 初始化 ====================
6849
- /** 初始化图层 */
6850
- initLayer() {
6851
- const C = this.CesiumNS;
6852
- if (this.options.layer) {
6853
- this.layer = this.options.layer;
6854
- } else {
6855
- const newLayer = new C.CustomDataSource("FlightPreviewLayer");
6856
- this.layer = newLayer;
6857
- this.viewer.dataSources.add(newLayer);
6858
- }
6859
- }
6860
- /** 解析航线数据 */
6861
- parseWaylineData() {
6862
- const C = this.CesiumNS;
6863
- const { waylineData } = this.options;
6864
- const converted = convertSinoflyWayline(waylineData, { CesiumNS: this.CesiumNS });
6865
- const waypointData = converted.waypointData;
6866
- if (!waypointData || waypointData.length < 2) {
6867
- console.warn("[FlightPreviewController] \u822A\u70B9\u6570\u91CF\u4E0D\u8DB3");
6868
- return;
6869
- }
6870
- let cumulativeDistance = 0;
6871
- for (let i = 0; i < waypointData.length; i++) {
6872
- const wp = waypointData[i];
6873
- let distanceToNext = 0;
6874
- if (i < waypointData.length - 1) {
6875
- const nextWp = waypointData[i + 1];
6876
- distanceToNext = C.Cartesian3.distance(wp.position, nextWp.position);
6877
- }
6878
- this.waypoints.push({
6879
- position: wp.position,
6880
- heading: wp.heading ?? 0,
6881
- pitch: wp.pitch ?? -10,
6882
- roll: wp.roll ?? 0,
6883
- distanceToNext,
6884
- cumulativeDistance
6885
- });
6886
- cumulativeDistance += distanceToNext;
6887
- }
6888
- this.totalDistance = cumulativeDistance;
6889
- }
6890
- /** 初始化可视化组件 */
6891
- initVisualization() {
6892
- this.initDroneModel();
6893
- this.initFrustum();
6894
- this.initTrackHighlight();
6895
- this.initPathPreview();
6896
- this.updateVisualization();
6897
- }
6898
- /** 初始化无人机模型 */
6899
- initDroneModel() {
6900
- if (!this.layer || this.waypoints.length === 0) return;
6901
- const C = this.CesiumNS;
6902
- const layer = this.layer;
6903
- const modelHeadingOffset = 270;
6904
- const modelVerticalOffset = 35;
6905
- const modelForwardOffset = 5;
6906
- const modelRightOffset = -30;
6907
- this.droneEntity = layer.entities.add({
6908
- position: new C.CallbackProperty(() => {
6909
- const { position, heading, pitch } = this.getCurrentPose();
6910
- return this.applyModelOffset(position, heading, pitch, {
6911
- forward: modelForwardOffset,
6912
- right: modelRightOffset,
6913
- up: modelVerticalOffset
6914
- });
6915
- }, false),
6916
- orientation: new C.CallbackProperty(() => {
6917
- const { position, heading, pitch, roll } = this.getCurrentPose();
6918
- const hpr = new C.HeadingPitchRoll(
6919
- C.Math.toRadians(heading + modelHeadingOffset),
6920
- C.Math.toRadians(pitch),
6921
- C.Math.toRadians(roll)
6922
- );
6923
- return C.Transforms.headingPitchRollQuaternion(position, hpr);
6924
- }, false),
6925
- model: {
6926
- uri: wurenji_default,
6927
- scale: 0.5,
6928
- minimumPixelSize: 32,
6929
- maximumScale: 100,
6930
- silhouetteColor: C.Color.CYAN,
6931
- silhouetteSize: 1
6932
- },
6933
- properties: { _type: "flight-preview-drone" }
6934
- });
6935
- }
6936
- /** 应用模型偏移 */
6937
- applyModelOffset(position, heading, pitch, offset) {
6938
- const C = this.CesiumNS;
6939
- const headingRad = C.Math.toRadians(heading);
6940
- const pitchRad = C.Math.toRadians(pitch);
6941
- const enu = C.Transforms.eastNorthUpToFixedFrame(position);
6942
- const east = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 0, new C.Cartesian4()));
6943
- const north = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 1, new C.Cartesian4()));
6944
- const up = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 2, new C.Cartesian4()));
6945
- const horizForward = C.Cartesian3.add(
6946
- C.Cartesian3.multiplyByScalar(east, Math.sin(headingRad), new C.Cartesian3()),
6947
- C.Cartesian3.multiplyByScalar(north, Math.cos(headingRad), new C.Cartesian3()),
6948
- new C.Cartesian3()
6949
- );
6950
- const right = C.Cartesian3.add(
6951
- C.Cartesian3.multiplyByScalar(east, Math.cos(headingRad), new C.Cartesian3()),
6952
- C.Cartesian3.multiplyByScalar(north, -Math.sin(headingRad), new C.Cartesian3()),
6953
- new C.Cartesian3()
6954
- );
6955
- const forward = C.Cartesian3.add(
6956
- C.Cartesian3.multiplyByScalar(horizForward, Math.cos(pitchRad), new C.Cartesian3()),
6957
- C.Cartesian3.multiplyByScalar(up, Math.sin(pitchRad), new C.Cartesian3()),
6958
- new C.Cartesian3()
6959
- );
6960
- const upRotated = C.Cartesian3.add(
6961
- C.Cartesian3.multiplyByScalar(horizForward, -Math.sin(pitchRad), new C.Cartesian3()),
6962
- C.Cartesian3.multiplyByScalar(up, Math.cos(pitchRad), new C.Cartesian3()),
6963
- new C.Cartesian3()
6964
- );
6965
- let result = C.Cartesian3.clone(position);
6966
- result = C.Cartesian3.add(result, C.Cartesian3.multiplyByScalar(forward, offset.forward, new C.Cartesian3()), result);
6967
- result = C.Cartesian3.add(result, C.Cartesian3.multiplyByScalar(right, offset.right, new C.Cartesian3()), result);
6968
- result = C.Cartesian3.add(result, C.Cartesian3.multiplyByScalar(upRotated, offset.up, new C.Cartesian3()), result);
6969
- return result;
6970
- }
6971
- /** 初始化视锥体 */
6972
- initFrustum() {
6973
- if (!this.showFrustum || !this.layer) return;
6974
- const C = this.CesiumNS;
6975
- this.frustum = new FrustumPyramid(this.CesiumNS, this.layer, {
6976
- fov: 50,
6977
- length: 500,
6978
- color: C.Color.CYAN,
6979
- fillAlpha: 0.25,
6980
- width: 2
6981
- });
6982
- this.frustum.showAtDynamic(
6983
- () => this.getCurrentPose().position,
6984
- () => this.getCurrentPose().heading,
6985
- () => this.getCurrentPose().pitch,
6986
- () => this.getCurrentPose().roll,
6987
- () => 50,
6988
- () => 500
6989
- );
6990
- }
6991
- /** 初始化轨迹高亮 */
6992
- initTrackHighlight() {
6993
- if (!this.trackHighlightOptions.enabled || !this.layer) return;
6994
- if (this.waypoints.length < 2) return;
6995
- const C = this.CesiumNS;
6996
- const layer = this.layer;
6997
- const traveledColor = this.trackHighlightOptions.traveledColor ?? C.Color.fromCssColorString("#00FF88");
6998
- const remainingColor = this.trackHighlightOptions.remainingColor ?? C.Color.fromCssColorString("#4A90D9").withAlpha(0.7);
6999
- this.traveledPathEntity = layer.entities.add({
7000
- polyline: {
7001
- positions: new C.CallbackProperty(() => this.getTraveledPositions(), false),
7002
- width: this.trackHighlightOptions.traveledWidth ?? 6,
7003
- material: traveledColor,
7004
- clampToGround: false
7005
- },
7006
- properties: { _type: "flight-preview-traveled" }
7007
- });
7008
- this.remainingPathEntity = layer.entities.add({
7009
- polyline: {
7010
- positions: new C.CallbackProperty(() => this.getRemainingPositions(), false),
7011
- width: this.trackHighlightOptions.remainingWidth ?? 4,
7012
- material: remainingColor,
7013
- clampToGround: false
7014
- },
7015
- properties: { _type: "flight-preview-remaining" }
7016
- });
7017
- }
7018
- /** 初始化预览窗口 */
7019
- initPathPreview() {
7020
- this.pathPreview = new PathPreview(
7021
- this.CesiumNS,
7022
- this.viewer,
7023
- {
7024
- container: this.options.previewContainer,
7025
- fov: 50,
7026
- pitch: -10,
7027
- useGlobalEventBus: true
7028
- }
7029
- );
7030
- this.pathPreview.hide();
7031
- }
7032
- // ==================== 私有方法 - 动画循环 ====================
7033
- /** 启动动画循环 */
7034
- startAnimationLoop() {
7035
- if (this.animationFrameId) return;
7036
- const animate = (currentTime) => {
7037
- if (this.state !== "playing" || this.destroyed) {
7038
- this.animationFrameId = void 0;
7039
- return;
7040
- }
7041
- const deltaTime = this.lastFrameTime ? (currentTime - this.lastFrameTime) / 1e3 : 0;
7042
- this.lastFrameTime = currentTime;
7043
- this.currentDistance += this.speed * deltaTime;
7044
- if (this.currentDistance >= this.totalDistance) {
7045
- if (this.loop) {
7046
- this.currentDistance = this.currentDistance % this.totalDistance;
7047
- } else {
7048
- this.currentDistance = this.totalDistance;
7049
- this.setState("stopped");
7050
- this.stopAnimationLoop();
7051
- return;
7052
- }
7053
- }
7054
- this.updateVisualization();
7055
- this.animationFrameId = requestAnimationFrame(animate);
7056
- };
7057
- this.animationFrameId = requestAnimationFrame(animate);
7058
- }
7059
- /** 停止动画循环 */
7060
- stopAnimationLoop() {
7061
- if (this.animationFrameId) {
7062
- cancelAnimationFrame(this.animationFrameId);
7063
- this.animationFrameId = void 0;
7064
- }
7065
- this.lastFrameTime = void 0;
7066
- }
7067
- // ==================== 私有方法 - 路径插值 ====================
7068
- /** 查找当前所在航段 */
7069
- findCurrentSegment() {
7070
- if (this.waypoints.length === 0) {
7071
- return { segmentIndex: 0, progressInSegment: 0 };
7072
- }
7073
- for (let i = 0; i < this.waypoints.length - 1; i++) {
7074
- const wp = this.waypoints[i];
7075
- const nextWp = this.waypoints[i + 1];
7076
- if (this.currentDistance <= nextWp.cumulativeDistance) {
7077
- const segmentStart = wp.cumulativeDistance;
7078
- const segmentLength = wp.distanceToNext;
7079
- const distanceInSegment = this.currentDistance - segmentStart;
7080
- const progressInSegment = segmentLength > 0 ? distanceInSegment / segmentLength : 0;
7081
- return { segmentIndex: i, progressInSegment };
7082
- }
7083
- }
7084
- return { segmentIndex: this.waypoints.length - 2, progressInSegment: 1 };
7085
- }
7086
- /** 获取当前姿态 */
7087
- getCurrentPose() {
7088
- if (this.waypoints.length === 0) {
7089
- const C = this.CesiumNS;
7090
- return {
7091
- position: new C.Cartesian3(),
7092
- heading: 0,
7093
- pitch: -10,
7094
- roll: 0
7095
- };
7096
- }
7097
- const { segmentIndex, progressInSegment } = this.findCurrentSegment();
7098
- const wp = this.waypoints[segmentIndex];
7099
- const nextWp = this.waypoints[Math.min(segmentIndex + 1, this.waypoints.length - 1)];
7100
- const position = this.interpolatePosition(wp.position, nextWp.position, progressInSegment);
7101
- const heading = this.interpolateAngle(wp.heading, nextWp.heading, progressInSegment);
7102
- const pitch = this.lerp(wp.pitch, nextWp.pitch, progressInSegment);
7103
- const roll = this.lerp(wp.roll, nextWp.roll, progressInSegment);
7104
- return { position, heading, pitch, roll };
7105
- }
7106
- /** 位置插值 */
7107
- interpolatePosition(start, end, t) {
7108
- const C = this.CesiumNS;
7109
- return C.Cartesian3.lerp(start, end, t, new C.Cartesian3());
7110
- }
7111
- /** 角度插值(处理 0-360 环绕) */
7112
- interpolateAngle(start, end, t) {
7113
- let diff = end - start;
7114
- if (diff > 180) diff -= 360;
7115
- if (diff < -180) diff += 360;
7116
- let result = start + diff * t;
7117
- return (result % 360 + 360) % 360;
7118
- }
7119
- /** 线性插值 */
7120
- lerp(start, end, t) {
7121
- return start + (end - start) * t;
7122
- }
7123
- // ==================== 私有方法 - 轨迹高亮 ====================
7124
- /** 获取已飞过的位置数组 */
7125
- getTraveledPositions() {
7126
- if (this.waypoints.length === 0) return [];
7127
- const { segmentIndex, progressInSegment } = this.findCurrentSegment();
7128
- const currentPos = this.getCurrentPose().position;
7129
- const positions = [];
7130
- for (let i = 0; i <= segmentIndex; i++) {
7131
- positions.push(this.waypoints[i].position);
7132
- }
7133
- if (progressInSegment > 0) {
7134
- positions.push(currentPos);
7135
- }
7136
- return positions;
7137
- }
7138
- /** 获取未飞过的位置数组 */
7139
- getRemainingPositions() {
7140
- if (this.waypoints.length === 0) return [];
7141
- const { segmentIndex } = this.findCurrentSegment();
7142
- const currentPos = this.getCurrentPose().position;
7143
- const positions = [];
7144
- positions.push(currentPos);
7145
- for (let i = segmentIndex + 1; i < this.waypoints.length; i++) {
7146
- positions.push(this.waypoints[i].position);
7147
- }
7148
- return positions;
7149
- }
7150
- // ==================== 私有方法 - 更新 ====================
7151
- /** 更新可视化 */
7152
- updateVisualization() {
7153
- const pose = this.getCurrentPose();
7154
- if (this.pathPreview) {
7155
- this.pathPreview.setPose(
7156
- pose.position,
7157
- pose.heading,
7158
- pose.pitch,
7159
- pose.roll
7160
- );
7161
- }
7162
- this.viewer.scene?.requestRender?.();
7163
- const { segmentIndex } = this.findCurrentSegment();
7164
- this.onProgressChange.emit({
7165
- progress: this.getProgress(),
7166
- position: pose.position,
7167
- waypointIndex: segmentIndex,
7168
- pose: {
7169
- heading: pose.heading,
7170
- pitch: pose.pitch,
7171
- roll: pose.roll
7172
- }
7173
- });
7174
- }
7175
- /** 设置状态 */
7176
- setState(newState) {
7177
- if (this.state !== newState) {
7178
- this.state = newState;
7179
- this.onStateChange.emit({ state: newState });
7180
- }
7181
- }
7182
- /** 销毁可视化组件 */
7183
- destroyVisualization() {
7184
- const layer = this.layer;
7185
- if (this.droneEntity && layer) {
7186
- try {
7187
- layer.entities.remove(this.droneEntity);
7188
- } catch {
7189
- }
7190
- }
7191
- if (this.traveledPathEntity && layer) {
7192
- try {
7193
- layer.entities.remove(this.traveledPathEntity);
7194
- } catch {
7195
- }
7196
- }
7197
- if (this.remainingPathEntity && layer) {
7198
- try {
7199
- layer.entities.remove(this.remainingPathEntity);
7200
- } catch {
7201
- }
7202
- }
7203
- try {
7204
- this.frustum?.destroy();
7205
- } catch {
7206
- }
7207
- try {
7208
- this.pathPreview?.destroy();
7209
- } catch {
7210
- }
7211
- if (!this.options.layer && this.layer) {
7212
- try {
7213
- this.viewer.dataSources.remove(this.layer);
7214
- } catch {
7215
- }
7216
- }
7217
- this.droneEntity = void 0;
7218
- this.traveledPathEntity = void 0;
7219
- this.remainingPathEntity = void 0;
7220
- this.frustum = void 0;
7221
- this.pathPreview = void 0;
7222
- this.layer = void 0;
7223
- }
7224
- };
7225
-
7226
6732
  // src/core/CZMLPathManager.ts
7227
6733
  var _CZMLPathManager = class _CZMLPathManager {
7228
6734
  constructor(CesiumNS, viewer) {
@@ -7336,50 +6842,6 @@ var _CZMLPathManager = class _CZMLPathManager {
7336
6842
  renderFlightPathPreview(options) {
7337
6843
  return renderFlightPathPreview(this.CesiumNS, this.viewer, options);
7338
6844
  }
7339
- /**
7340
- * 开始飞行预览:飞机沿航线飞行动画
7341
- *
7342
- * 功能:
7343
- * - 飞机游标沿航线从起点飞到终点,循环播放
7344
- * - 预览窗口实时显示飞机第一人称视角
7345
- * - 轨迹高亮显示已飞过/未飞过的航段
7346
- * - 预览模式下禁止选择/编辑航线、航点
7347
- *
7348
- * @param options 飞行预览选项
7349
- * @returns 飞行预览控制器
7350
- *
7351
- * @example
7352
- * ```typescript
7353
- * const preview = pathManager.startFlightPreview({
7354
- * waylineData: myWayline,
7355
- * speed: 15, // 飞行速度 15m/s
7356
- * loop: true, // 循环播放
7357
- * trackHighlight: {
7358
- * enabled: true,
7359
- * traveledColor: Cesium.Color.fromCssColorString('#00FF88'),
7360
- * remainingColor: Cesium.Color.fromCssColorString('#4A90D9'),
7361
- * },
7362
- * });
7363
- *
7364
- * preview.start(); // 开始播放
7365
- * preview.pause(); // 暂停
7366
- * preview.resume(); // 继续
7367
- * preview.stop(); // 停止
7368
- * preview.seek(0.5); // 跳转到 50%
7369
- * preview.setSpeed(20); // 设置速度
7370
- *
7371
- * // 监听进度变化
7372
- * preview.onProgressChange.on(({ progress, waypointIndex }) => {
7373
- * console.log(`进度: ${(progress * 100).toFixed(1)}%`);
7374
- * });
7375
- *
7376
- * // 销毁
7377
- * preview.destroy();
7378
- * ```
7379
- */
7380
- startFlightPreview(options) {
7381
- return new FlightPreviewController(this.CesiumNS, this.viewer, options);
7382
- }
7383
6845
  resolveEntity(entityOrId) {
7384
6846
  return typeof entityOrId === "string" ? this.viewer.entities.getById(entityOrId) : entityOrId;
7385
6847
  }
@@ -7575,6 +7037,8 @@ var PolygonEditor = class {
7575
7037
  __publicField(this, "keydownHandler");
7576
7038
  // 保存编码前的原始名称,用于还原
7577
7039
  __publicField(this, "originalPolygonNames", /* @__PURE__ */ new Map());
7040
+ // 保存双击缩放是否被禁用的状态
7041
+ __publicField(this, "doubleClickZoomDisabled", false);
7578
7042
  }
7579
7043
  /**
7580
7044
  * 根据图层名称获取颜色配置
@@ -7849,12 +7313,62 @@ var PolygonEditor = class {
7849
7313
  }
7850
7314
  this.handler = void 0;
7851
7315
  }
7316
+ /**
7317
+ * 禁用 Cesium 默认的双击缩放行为
7318
+ * 在绘制/编辑过程中调用,防止双击导致相机乱飞
7319
+ */
7320
+ disableDoubleClickZoom() {
7321
+ if (this.doubleClickZoomDisabled) return;
7322
+ try {
7323
+ const C = this.CesiumNS;
7324
+ const cesiumWidget = this.viewer.cesiumWidget;
7325
+ if (cesiumWidget?.screenSpaceEventHandler) {
7326
+ cesiumWidget.screenSpaceEventHandler.removeInputAction(
7327
+ C.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
7328
+ );
7329
+ this.doubleClickZoomDisabled = true;
7330
+ }
7331
+ } catch (e) {
7332
+ console.warn("[PolygonEditor] \u7981\u7528\u53CC\u51FB\u7F29\u653E\u5931\u8D25:", e);
7333
+ }
7334
+ }
7335
+ /**
7336
+ * 恢复 Cesium 默认的双击缩放行为
7337
+ * 在绘制/编辑结束后调用
7338
+ */
7339
+ restoreDoubleClickZoom() {
7340
+ if (!this.doubleClickZoomDisabled) return;
7341
+ try {
7342
+ const C = this.CesiumNS;
7343
+ const viewer = this.viewer;
7344
+ const cesiumWidget = viewer.cesiumWidget;
7345
+ if (cesiumWidget?.screenSpaceEventHandler) {
7346
+ cesiumWidget.screenSpaceEventHandler.setInputAction(
7347
+ (movement) => {
7348
+ try {
7349
+ const pickedObject = viewer.scene.pick(movement.position);
7350
+ if (pickedObject?.id && viewer.trackedEntity !== pickedObject.id) {
7351
+ viewer.flyTo(pickedObject.id, { duration: 1.5 });
7352
+ }
7353
+ } catch {
7354
+ }
7355
+ },
7356
+ C.ScreenSpaceEventType.LEFT_DOUBLE_CLICK
7357
+ );
7358
+ this.doubleClickZoomDisabled = false;
7359
+ }
7360
+ } catch (e) {
7361
+ console.warn("[PolygonEditor] \u6062\u590D\u53CC\u51FB\u7F29\u653E\u5931\u8D25:", e);
7362
+ this.doubleClickZoomDisabled = false;
7363
+ }
7364
+ }
7852
7365
  /**
7853
7366
  * 完成编辑/绘制的通用清理逻辑
7854
7367
  */
7855
7368
  finalizePolygonSession(layer, onComplete, entity) {
7856
7369
  this.destroyHandler();
7857
7370
  this.removeKeyboardListener();
7371
+ this.restoreDoubleClickZoom();
7858
7372
  if (entity && onComplete) {
7859
7373
  onComplete(entity);
7860
7374
  }
@@ -7897,6 +7411,7 @@ var PolygonEditor = class {
7897
7411
  }
7898
7412
  this.clearAllPolygonHighlights();
7899
7413
  this.stopInternal(layer);
7414
+ this.disableDoubleClickZoom();
7900
7415
  this.drawingPositions = [];
7901
7416
  this.drawHandles = [];
7902
7417
  this.rubberBandPoint = void 0;
@@ -8041,6 +7556,7 @@ var PolygonEditor = class {
8041
7556
  }
8042
7557
  this.destroyHandler();
8043
7558
  this.removeKeyboardListener();
7559
+ this.restoreDoubleClickZoom();
8044
7560
  }
8045
7561
  /**
8046
7562
  * 开始编辑多边形
@@ -8052,6 +7568,7 @@ var PolygonEditor = class {
8052
7568
  startEditing(polygonEntity, layer, onComplete) {
8053
7569
  const C = this.CesiumNS;
8054
7570
  this.cleanupHandles(layer);
7571
+ this.disableDoubleClickZoom();
8055
7572
  const colors = this.getColorConfig(layer.name);
8056
7573
  const line_color = colors.lineColor;
8057
7574
  const line_fina_color = colors.lineFinalColor;
@@ -9474,6 +8991,7 @@ var PolygonEditor = class {
9474
8991
  const nameStr = `circle_${(circleCount + 1).toString().padStart(2, "0")}`;
9475
8992
  this.clearAllPolygonHighlights();
9476
8993
  this.stopInternal();
8994
+ this.disableDoubleClickZoom();
9477
8995
  let centerPosition = null;
9478
8996
  let currentRadius = 0;
9479
8997
  let tempCenterPoint;
@@ -9687,6 +9205,7 @@ var PolygonEditor = class {
9687
9205
  currentRadius = 0;
9688
9206
  this.destroyHandler();
9689
9207
  this.removeKeyboardListener();
9208
+ this.restoreDoubleClickZoom();
9690
9209
  };
9691
9210
  this.handler && this.handler.setInputAction(
9692
9211
  cancelCircleDrawing,
@@ -9722,6 +9241,7 @@ var PolygonEditor = class {
9722
9241
  const line_fina_color = colors.lineFinalColor;
9723
9242
  this.clearAllPolygonHighlights();
9724
9243
  this.stopInternal();
9244
+ this.disableDoubleClickZoom();
9725
9245
  let pointCount = 0;
9726
9246
  this.handler = new C.ScreenSpaceEventHandler(this.viewer.scene.canvas);
9727
9247
  this.handler && this.handler.setInputAction(
@@ -9816,6 +9336,7 @@ var PolygonEditor = class {
9816
9336
  const stopPointDrawing = () => {
9817
9337
  this.destroyHandler();
9818
9338
  this.removeKeyboardListener();
9339
+ this.restoreDoubleClickZoom();
9819
9340
  };
9820
9341
  this.handler && this.handler.setInputAction(
9821
9342
  stopPointDrawing,