@jorgmoritz/gis-manager 0.1.31 → 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/README.md +76 -76
- package/dist/index.cjs +527 -657
- package/dist/index.cjs.map +1 -1
- package/dist/index.d.cts +120 -173
- package/dist/index.d.ts +120 -173
- package/dist/index.js +527 -657
- package/dist/index.js.map +1 -1
- package/dist/vue/index.cjs +163 -655
- package/dist/vue/index.cjs.map +1 -1
- package/dist/vue/index.js +163 -655
- package/dist/vue/index.js.map +1 -1
- package/package.json +1 -1
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.
|
|
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
|
|
2885
|
-
const
|
|
2886
|
-
const
|
|
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:
|
|
2933
|
-
|
|
2934
|
-
|
|
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
|
},
|
|
@@ -3065,20 +3064,7 @@ var AirplaneCursor = class {
|
|
|
3065
3064
|
moved = true;
|
|
3066
3065
|
}
|
|
3067
3066
|
if (this.keysPressed.has("z")) {
|
|
3068
|
-
|
|
3069
|
-
const cartographic = C.Cartographic.fromCartesian(newPos);
|
|
3070
|
-
if (cartographic) {
|
|
3071
|
-
const terrainHeight = this.viewer.scene.globe.getHeight(cartographic) ?? 0;
|
|
3072
|
-
const minAltitude = terrainHeight + 1;
|
|
3073
|
-
if (cartographic.height < minAltitude) {
|
|
3074
|
-
cartographic.height = minAltitude;
|
|
3075
|
-
setPos(C.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, cartographic.height));
|
|
3076
|
-
} else {
|
|
3077
|
-
setPos(newPos);
|
|
3078
|
-
}
|
|
3079
|
-
} else {
|
|
3080
|
-
setPos(newPos);
|
|
3081
|
-
}
|
|
3067
|
+
setPos(addVec(pose.position, C.Cartesian3.multiplyByScalar(u3, -step, new C.Cartesian3())));
|
|
3082
3068
|
moved = true;
|
|
3083
3069
|
}
|
|
3084
3070
|
if (this.keysPressed.has("q")) {
|
|
@@ -3306,6 +3292,11 @@ var AirplaneCursor = class {
|
|
|
3306
3292
|
} catch {
|
|
3307
3293
|
}
|
|
3308
3294
|
this.pyramidLayer = void 0;
|
|
3295
|
+
try {
|
|
3296
|
+
this.cameraChangedListener?.();
|
|
3297
|
+
} catch {
|
|
3298
|
+
}
|
|
3299
|
+
this.cameraChangedListener = void 0;
|
|
3309
3300
|
try {
|
|
3310
3301
|
this.frustum?.destroy();
|
|
3311
3302
|
} catch {
|
|
@@ -4624,34 +4615,6 @@ var PathEditingEventHandler = class {
|
|
|
4624
4615
|
* 🆕 处理快速编辑模式的点击
|
|
4625
4616
|
*/
|
|
4626
4617
|
handleQuickEditClick(movement) {
|
|
4627
|
-
const C = this.CesiumNS;
|
|
4628
|
-
try {
|
|
4629
|
-
const picked = this.viewer.scene.pick?.(movement.position);
|
|
4630
|
-
const entity = picked?.id;
|
|
4631
|
-
if (entity?.properties) {
|
|
4632
|
-
const props = entity.properties;
|
|
4633
|
-
const vertexIndex = props._vertexIndex?.getValue?.() ?? props._vertexIndex;
|
|
4634
|
-
const type = props._type?.getValue?.() ?? props._type;
|
|
4635
|
-
if (typeof vertexIndex === "number" && (type === "path-vertex" || type === "vertex-label")) {
|
|
4636
|
-
if (vertexIndex !== this.hiddenClimbIndex && vertexIndex !== 0) {
|
|
4637
|
-
console.log("[QuickEdit] \u70B9\u51FB\u5230\u5DF2\u5B58\u5728\u822A\u70B9\uFF0C\u9009\u4E2D\u800C\u975E\u65B0\u589E:", vertexIndex);
|
|
4638
|
-
if (this.callbacks.onVertexSelect) {
|
|
4639
|
-
this.callbacks.onVertexSelect(vertexIndex);
|
|
4640
|
-
}
|
|
4641
|
-
if (this.callbacks.onVertexSelectDetail) {
|
|
4642
|
-
try {
|
|
4643
|
-
const detailInfo = this.buildVertexDetailInfo(vertexIndex);
|
|
4644
|
-
this.callbacks.onVertexSelectDetail(detailInfo);
|
|
4645
|
-
} catch (error) {
|
|
4646
|
-
console.error("Error building vertex detail info:", error);
|
|
4647
|
-
}
|
|
4648
|
-
}
|
|
4649
|
-
return;
|
|
4650
|
-
}
|
|
4651
|
-
}
|
|
4652
|
-
}
|
|
4653
|
-
} catch (error) {
|
|
4654
|
-
}
|
|
4655
4618
|
const position = this.getPositionFromMouse(movement);
|
|
4656
4619
|
if (!position) {
|
|
4657
4620
|
console.warn("[QuickEdit] \u65E0\u6CD5\u83B7\u53D6\u70B9\u51FB\u4F4D\u7F6E");
|
|
@@ -4660,6 +4623,7 @@ var PathEditingEventHandler = class {
|
|
|
4660
4623
|
const quickEditOptions = this.callbacks.getQuickEditOptions?.();
|
|
4661
4624
|
if (!quickEditOptions) return;
|
|
4662
4625
|
const { climbHeight, altitudeMode } = quickEditOptions;
|
|
4626
|
+
const C = this.CesiumNS;
|
|
4663
4627
|
const originalCarto = C.Cartographic.fromCartesian(position);
|
|
4664
4628
|
const originalHeight = originalCarto.height;
|
|
4665
4629
|
const newPosition = this.applyClimbHeight(position, climbHeight, altitudeMode);
|
|
@@ -5514,10 +5478,6 @@ function startPathEditing(CesiumNS, viewer, entityOrId, options) {
|
|
|
5514
5478
|
);
|
|
5515
5479
|
const cleanupSession = () => {
|
|
5516
5480
|
setActiveIndex(void 0);
|
|
5517
|
-
try {
|
|
5518
|
-
preview?.destroy();
|
|
5519
|
-
} catch {
|
|
5520
|
-
}
|
|
5521
5481
|
try {
|
|
5522
5482
|
handles.forEach((handle) => {
|
|
5523
5483
|
if (handle) {
|
|
@@ -5927,6 +5887,13 @@ var ArrowShape = class {
|
|
|
5927
5887
|
__publicField(this, "baseAlpha");
|
|
5928
5888
|
__publicField(this, "outline");
|
|
5929
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);
|
|
5930
5897
|
const C = this.CesiumNS;
|
|
5931
5898
|
this.shaftHeight = opts.shaftHeight ?? 17;
|
|
5932
5899
|
this.headHeight = opts.headHeight ?? 8;
|
|
@@ -5934,12 +5901,17 @@ var ArrowShape = class {
|
|
|
5934
5901
|
this.shaftRadius = opts.shaftRadius ?? 3;
|
|
5935
5902
|
this.headRadius = opts.headRadius ?? 6;
|
|
5936
5903
|
this.baseRadius = opts.baseRadius ?? 8;
|
|
5937
|
-
this.color = opts.color ? C.Color.fromCssColorString(opts.color) : C.Color.fromCssColorString("#
|
|
5904
|
+
this.color = opts.color ? C.Color.fromCssColorString(opts.color) : C.Color.fromCssColorString("#FFD700");
|
|
5938
5905
|
this.shaftAlpha = opts.shaftAlpha ?? 1;
|
|
5939
5906
|
this.headAlpha = opts.headAlpha ?? 1;
|
|
5940
|
-
this.baseAlpha = opts.baseAlpha ??
|
|
5907
|
+
this.baseAlpha = opts.baseAlpha ?? 0.65;
|
|
5941
5908
|
this.outline = opts.outline ?? false;
|
|
5942
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;
|
|
5943
5915
|
}
|
|
5944
5916
|
/**
|
|
5945
5917
|
* Create arrow entities at given position
|
|
@@ -5957,22 +5929,40 @@ var ArrowShape = class {
|
|
|
5957
5929
|
const carto = C.Cartographic.fromCartesian(position);
|
|
5958
5930
|
const lon = C.Math.toDegrees(carto.longitude);
|
|
5959
5931
|
const lat = C.Math.toDegrees(carto.latitude);
|
|
5960
|
-
const
|
|
5961
|
-
|
|
5962
|
-
|
|
5963
|
-
|
|
5964
|
-
|
|
5965
|
-
|
|
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
|
+
}
|
|
5966
5948
|
const hpr = new C.HeadingPitchRoll(0, 0, 0);
|
|
5967
5949
|
const baseOrientation = C.Transforms.headingPitchRollQuaternion(position, hpr);
|
|
5968
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
|
+
};
|
|
5969
5959
|
const shaftEntity = layer.entities.add({
|
|
5970
|
-
position: new C.ConstantPositionProperty(
|
|
5960
|
+
position: this.dynamicScale ? new C.CallbackProperty(getShaftPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight / 2)),
|
|
5971
5961
|
orientation: fixedOrientation,
|
|
5972
5962
|
cylinder: {
|
|
5973
|
-
length: this.shaftHeight,
|
|
5974
|
-
topRadius: this.shaftRadius,
|
|
5975
|
-
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,
|
|
5976
5966
|
material: this.color.withAlpha(this.shaftAlpha),
|
|
5977
5967
|
outline: this.outline,
|
|
5978
5968
|
outlineColor: this.outlineColor,
|
|
@@ -5985,13 +5975,13 @@ var ArrowShape = class {
|
|
|
5985
5975
|
});
|
|
5986
5976
|
entities.push(shaftEntity);
|
|
5987
5977
|
const headEntity = layer.entities.add({
|
|
5988
|
-
position: new C.ConstantPositionProperty(
|
|
5978
|
+
position: this.dynamicScale ? new C.CallbackProperty(getHeadPosition, false) : new C.ConstantPositionProperty(C.Cartesian3.fromDegrees(lon, lat, baseHeight + this.shaftHeight + this.headHeight / 2)),
|
|
5989
5979
|
orientation: fixedOrientation,
|
|
5990
5980
|
cylinder: {
|
|
5991
|
-
length: this.headHeight,
|
|
5981
|
+
length: this.dynamicScale ? new C.CallbackProperty(() => this.headHeight * this.cachedScaleFactor, false) : this.headHeight,
|
|
5992
5982
|
topRadius: 0,
|
|
5993
5983
|
// Creates cone shape
|
|
5994
|
-
bottomRadius: this.headRadius,
|
|
5984
|
+
bottomRadius: this.dynamicScale ? new C.CallbackProperty(() => this.headRadius * this.cachedScaleFactor, false) : this.headRadius,
|
|
5995
5985
|
material: this.color.withAlpha(this.headAlpha),
|
|
5996
5986
|
outline: this.outline,
|
|
5997
5987
|
outlineColor: this.outlineColor,
|
|
@@ -6006,8 +5996,8 @@ var ArrowShape = class {
|
|
|
6006
5996
|
const baseEntity = layer.entities.add({
|
|
6007
5997
|
position,
|
|
6008
5998
|
ellipse: {
|
|
6009
|
-
semiMajorAxis: this.baseRadius,
|
|
6010
|
-
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,
|
|
6011
6001
|
height: baseHeight,
|
|
6012
6002
|
material: this.color.withAlpha(this.baseAlpha),
|
|
6013
6003
|
outline: this.outline,
|
|
@@ -6146,7 +6136,13 @@ function startPathDrawing(CesiumNS, viewer, options, onComplete) {
|
|
|
6146
6136
|
color: "#FFD700",
|
|
6147
6137
|
shaftAlpha: 1,
|
|
6148
6138
|
headAlpha: 1,
|
|
6149
|
-
baseAlpha: 0.5
|
|
6139
|
+
baseAlpha: 0.5,
|
|
6140
|
+
// 启用动态缩放,和无人机模型一样根据相机距离调整大小
|
|
6141
|
+
dynamicScale: true,
|
|
6142
|
+
viewer,
|
|
6143
|
+
referenceDistance: 300,
|
|
6144
|
+
minScale: 0.3,
|
|
6145
|
+
maxScale: 2
|
|
6150
6146
|
});
|
|
6151
6147
|
const arrowEntities = arrowShape.createEntities(
|
|
6152
6148
|
layer,
|
|
@@ -6663,7 +6659,13 @@ function renderFlightPath(CesiumNS, viewer, options) {
|
|
|
6663
6659
|
color: "#FFD700",
|
|
6664
6660
|
shaftAlpha: 0.98,
|
|
6665
6661
|
headAlpha: 0.98,
|
|
6666
|
-
baseAlpha: 0.65
|
|
6662
|
+
baseAlpha: 0.65,
|
|
6663
|
+
// 启用动态缩放
|
|
6664
|
+
dynamicScale: true,
|
|
6665
|
+
viewer,
|
|
6666
|
+
referenceDistance: 300,
|
|
6667
|
+
minScale: 0.3,
|
|
6668
|
+
maxScale: 2
|
|
6667
6669
|
});
|
|
6668
6670
|
arrowShape.createEntities(
|
|
6669
6671
|
layer,
|
|
@@ -6787,515 +6789,6 @@ function renderFlightPathPreview(CesiumNS, viewer, options) {
|
|
|
6787
6789
|
return { entity, controller };
|
|
6788
6790
|
}
|
|
6789
6791
|
|
|
6790
|
-
// src/core/path-manager/FlightPreviewController.ts
|
|
6791
|
-
var FlightPreviewController = class {
|
|
6792
|
-
constructor(CesiumNS, viewer, options) {
|
|
6793
|
-
this.CesiumNS = CesiumNS;
|
|
6794
|
-
this.viewer = viewer;
|
|
6795
|
-
this.options = options;
|
|
6796
|
-
// ========== 状态 ==========
|
|
6797
|
-
__publicField(this, "state", "stopped");
|
|
6798
|
-
__publicField(this, "destroyed", false);
|
|
6799
|
-
// ========== 配置 ==========
|
|
6800
|
-
__publicField(this, "speed");
|
|
6801
|
-
__publicField(this, "loop");
|
|
6802
|
-
__publicField(this, "showFrustum");
|
|
6803
|
-
__publicField(this, "trackHighlightOptions");
|
|
6804
|
-
// ========== 路径数据 ==========
|
|
6805
|
-
__publicField(this, "waypoints", []);
|
|
6806
|
-
__publicField(this, "totalDistance", 0);
|
|
6807
|
-
// ========== 动画状态 ==========
|
|
6808
|
-
__publicField(this, "currentDistance", 0);
|
|
6809
|
-
// 当前已飞行距离
|
|
6810
|
-
__publicField(this, "animationFrameId");
|
|
6811
|
-
__publicField(this, "lastFrameTime");
|
|
6812
|
-
// ========== 可视化组件 ==========
|
|
6813
|
-
__publicField(this, "layer");
|
|
6814
|
-
__publicField(this, "droneEntity");
|
|
6815
|
-
__publicField(this, "frustum");
|
|
6816
|
-
__publicField(this, "pathPreview");
|
|
6817
|
-
// ========== 轨迹高亮 ==========
|
|
6818
|
-
__publicField(this, "traveledPathEntity");
|
|
6819
|
-
__publicField(this, "remainingPathEntity");
|
|
6820
|
-
// ========== 事件 ==========
|
|
6821
|
-
__publicField(this, "onStateChange", new Emitter());
|
|
6822
|
-
__publicField(this, "onProgressChange", new Emitter());
|
|
6823
|
-
this.speed = options.speed ?? 10;
|
|
6824
|
-
this.loop = options.loop ?? true;
|
|
6825
|
-
this.showFrustum = options.showFrustum ?? true;
|
|
6826
|
-
this.trackHighlightOptions = {
|
|
6827
|
-
enabled: true,
|
|
6828
|
-
traveledWidth: 6,
|
|
6829
|
-
remainingWidth: 4,
|
|
6830
|
-
...options.trackHighlight
|
|
6831
|
-
};
|
|
6832
|
-
this.initLayer();
|
|
6833
|
-
this.parseWaylineData();
|
|
6834
|
-
this.initVisualization();
|
|
6835
|
-
console.log("[FlightPreviewController] \u521D\u59CB\u5316\u5B8C\u6210", {
|
|
6836
|
-
waypointCount: this.waypoints.length,
|
|
6837
|
-
totalDistance: this.totalDistance,
|
|
6838
|
-
speed: this.speed
|
|
6839
|
-
});
|
|
6840
|
-
}
|
|
6841
|
-
// ==================== 公共方法 ====================
|
|
6842
|
-
/** 开始预览 */
|
|
6843
|
-
start() {
|
|
6844
|
-
if (this.destroyed) return;
|
|
6845
|
-
if (this.state === "playing") return;
|
|
6846
|
-
if (this.state === "stopped") {
|
|
6847
|
-
this.currentDistance = 0;
|
|
6848
|
-
}
|
|
6849
|
-
this.setState("playing");
|
|
6850
|
-
this.lastFrameTime = performance.now();
|
|
6851
|
-
this.startAnimationLoop();
|
|
6852
|
-
globalState.startFlightPreview();
|
|
6853
|
-
this.pathPreview?.show();
|
|
6854
|
-
console.log("[FlightPreviewController] \u5F00\u59CB\u64AD\u653E");
|
|
6855
|
-
}
|
|
6856
|
-
/** 暂停 */
|
|
6857
|
-
pause() {
|
|
6858
|
-
if (this.destroyed) return;
|
|
6859
|
-
if (this.state !== "playing") return;
|
|
6860
|
-
this.setState("paused");
|
|
6861
|
-
this.stopAnimationLoop();
|
|
6862
|
-
console.log("[FlightPreviewController] \u6682\u505C");
|
|
6863
|
-
}
|
|
6864
|
-
/** 继续 */
|
|
6865
|
-
resume() {
|
|
6866
|
-
if (this.destroyed) return;
|
|
6867
|
-
if (this.state !== "paused") return;
|
|
6868
|
-
this.setState("playing");
|
|
6869
|
-
this.lastFrameTime = performance.now();
|
|
6870
|
-
this.startAnimationLoop();
|
|
6871
|
-
console.log("[FlightPreviewController] \u7EE7\u7EED\u64AD\u653E");
|
|
6872
|
-
}
|
|
6873
|
-
/** 停止并重置 */
|
|
6874
|
-
stop() {
|
|
6875
|
-
if (this.destroyed) return;
|
|
6876
|
-
this.setState("stopped");
|
|
6877
|
-
this.stopAnimationLoop();
|
|
6878
|
-
this.currentDistance = 0;
|
|
6879
|
-
globalState.stopFlightPreview();
|
|
6880
|
-
this.updateVisualization();
|
|
6881
|
-
this.pathPreview?.hide();
|
|
6882
|
-
console.log("[FlightPreviewController] \u505C\u6B62");
|
|
6883
|
-
}
|
|
6884
|
-
/** 跳转到指定进度 (0-1) */
|
|
6885
|
-
seek(progress) {
|
|
6886
|
-
if (this.destroyed) return;
|
|
6887
|
-
const clampedProgress = Math.max(0, Math.min(1, progress));
|
|
6888
|
-
this.currentDistance = clampedProgress * this.totalDistance;
|
|
6889
|
-
this.updateVisualization();
|
|
6890
|
-
console.log("[FlightPreviewController] \u8DF3\u8F6C\u5230", (clampedProgress * 100).toFixed(1) + "%");
|
|
6891
|
-
}
|
|
6892
|
-
/** 设置速度 */
|
|
6893
|
-
setSpeed(speed) {
|
|
6894
|
-
this.speed = Math.max(1, speed);
|
|
6895
|
-
console.log("[FlightPreviewController] \u8BBE\u7F6E\u901F\u5EA6:", this.speed, "m/s");
|
|
6896
|
-
}
|
|
6897
|
-
/** 获取当前状态 */
|
|
6898
|
-
getState() {
|
|
6899
|
-
return this.state;
|
|
6900
|
-
}
|
|
6901
|
-
/** 获取当前进度 (0-1) */
|
|
6902
|
-
getProgress() {
|
|
6903
|
-
if (this.totalDistance === 0) return 0;
|
|
6904
|
-
return this.currentDistance / this.totalDistance;
|
|
6905
|
-
}
|
|
6906
|
-
/** 获取当前航点索引 */
|
|
6907
|
-
getCurrentWaypointIndex() {
|
|
6908
|
-
return this.findCurrentSegment().segmentIndex;
|
|
6909
|
-
}
|
|
6910
|
-
/** 销毁 */
|
|
6911
|
-
destroy() {
|
|
6912
|
-
if (this.destroyed) return;
|
|
6913
|
-
this.destroyed = true;
|
|
6914
|
-
this.stopAnimationLoop();
|
|
6915
|
-
globalState.stopFlightPreview();
|
|
6916
|
-
this.destroyVisualization();
|
|
6917
|
-
this.onStateChange.clear();
|
|
6918
|
-
this.onProgressChange.clear();
|
|
6919
|
-
console.log("[FlightPreviewController] \u5DF2\u9500\u6BC1");
|
|
6920
|
-
}
|
|
6921
|
-
// ==================== 私有方法 - 初始化 ====================
|
|
6922
|
-
/** 初始化图层 */
|
|
6923
|
-
initLayer() {
|
|
6924
|
-
const C = this.CesiumNS;
|
|
6925
|
-
if (this.options.layer) {
|
|
6926
|
-
this.layer = this.options.layer;
|
|
6927
|
-
} else {
|
|
6928
|
-
const newLayer = new C.CustomDataSource("FlightPreviewLayer");
|
|
6929
|
-
this.layer = newLayer;
|
|
6930
|
-
this.viewer.dataSources.add(newLayer);
|
|
6931
|
-
}
|
|
6932
|
-
}
|
|
6933
|
-
/** 解析航线数据 */
|
|
6934
|
-
parseWaylineData() {
|
|
6935
|
-
const C = this.CesiumNS;
|
|
6936
|
-
const { waylineData } = this.options;
|
|
6937
|
-
const converted = convertSinoflyWayline(waylineData, { CesiumNS: this.CesiumNS });
|
|
6938
|
-
const waypointData = converted.waypointData;
|
|
6939
|
-
if (!waypointData || waypointData.length < 2) {
|
|
6940
|
-
console.warn("[FlightPreviewController] \u822A\u70B9\u6570\u91CF\u4E0D\u8DB3");
|
|
6941
|
-
return;
|
|
6942
|
-
}
|
|
6943
|
-
let cumulativeDistance = 0;
|
|
6944
|
-
for (let i = 0; i < waypointData.length; i++) {
|
|
6945
|
-
const wp = waypointData[i];
|
|
6946
|
-
let distanceToNext = 0;
|
|
6947
|
-
if (i < waypointData.length - 1) {
|
|
6948
|
-
const nextWp = waypointData[i + 1];
|
|
6949
|
-
distanceToNext = C.Cartesian3.distance(wp.position, nextWp.position);
|
|
6950
|
-
}
|
|
6951
|
-
this.waypoints.push({
|
|
6952
|
-
position: wp.position,
|
|
6953
|
-
heading: wp.heading ?? 0,
|
|
6954
|
-
pitch: wp.pitch ?? -10,
|
|
6955
|
-
roll: wp.roll ?? 0,
|
|
6956
|
-
distanceToNext,
|
|
6957
|
-
cumulativeDistance
|
|
6958
|
-
});
|
|
6959
|
-
cumulativeDistance += distanceToNext;
|
|
6960
|
-
}
|
|
6961
|
-
this.totalDistance = cumulativeDistance;
|
|
6962
|
-
}
|
|
6963
|
-
/** 初始化可视化组件 */
|
|
6964
|
-
initVisualization() {
|
|
6965
|
-
this.initDroneModel();
|
|
6966
|
-
this.initFrustum();
|
|
6967
|
-
this.initTrackHighlight();
|
|
6968
|
-
this.initPathPreview();
|
|
6969
|
-
this.updateVisualization();
|
|
6970
|
-
}
|
|
6971
|
-
/** 初始化无人机模型 */
|
|
6972
|
-
initDroneModel() {
|
|
6973
|
-
if (!this.layer || this.waypoints.length === 0) return;
|
|
6974
|
-
const C = this.CesiumNS;
|
|
6975
|
-
const layer = this.layer;
|
|
6976
|
-
const modelHeadingOffset = 270;
|
|
6977
|
-
const modelVerticalOffset = 35;
|
|
6978
|
-
const modelForwardOffset = 5;
|
|
6979
|
-
const modelRightOffset = -30;
|
|
6980
|
-
this.droneEntity = layer.entities.add({
|
|
6981
|
-
position: new C.CallbackProperty(() => {
|
|
6982
|
-
const { position, heading, pitch } = this.getCurrentPose();
|
|
6983
|
-
return this.applyModelOffset(position, heading, pitch, {
|
|
6984
|
-
forward: modelForwardOffset,
|
|
6985
|
-
right: modelRightOffset,
|
|
6986
|
-
up: modelVerticalOffset
|
|
6987
|
-
});
|
|
6988
|
-
}, false),
|
|
6989
|
-
orientation: new C.CallbackProperty(() => {
|
|
6990
|
-
const { position, heading, pitch, roll } = this.getCurrentPose();
|
|
6991
|
-
const hpr = new C.HeadingPitchRoll(
|
|
6992
|
-
C.Math.toRadians(heading + modelHeadingOffset),
|
|
6993
|
-
C.Math.toRadians(pitch),
|
|
6994
|
-
C.Math.toRadians(roll)
|
|
6995
|
-
);
|
|
6996
|
-
return C.Transforms.headingPitchRollQuaternion(position, hpr);
|
|
6997
|
-
}, false),
|
|
6998
|
-
model: {
|
|
6999
|
-
uri: wurenji_default,
|
|
7000
|
-
scale: 0.5,
|
|
7001
|
-
minimumPixelSize: 32,
|
|
7002
|
-
maximumScale: 100,
|
|
7003
|
-
silhouetteColor: C.Color.CYAN,
|
|
7004
|
-
silhouetteSize: 1
|
|
7005
|
-
},
|
|
7006
|
-
properties: { _type: "flight-preview-drone" }
|
|
7007
|
-
});
|
|
7008
|
-
}
|
|
7009
|
-
/** 应用模型偏移 */
|
|
7010
|
-
applyModelOffset(position, heading, pitch, offset) {
|
|
7011
|
-
const C = this.CesiumNS;
|
|
7012
|
-
const headingRad = C.Math.toRadians(heading);
|
|
7013
|
-
const pitchRad = C.Math.toRadians(pitch);
|
|
7014
|
-
const enu = C.Transforms.eastNorthUpToFixedFrame(position);
|
|
7015
|
-
const east = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 0, new C.Cartesian4()));
|
|
7016
|
-
const north = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 1, new C.Cartesian4()));
|
|
7017
|
-
const up = C.Cartesian3.fromCartesian4(C.Matrix4.getColumn(enu, 2, new C.Cartesian4()));
|
|
7018
|
-
const horizForward = C.Cartesian3.add(
|
|
7019
|
-
C.Cartesian3.multiplyByScalar(east, Math.sin(headingRad), new C.Cartesian3()),
|
|
7020
|
-
C.Cartesian3.multiplyByScalar(north, Math.cos(headingRad), new C.Cartesian3()),
|
|
7021
|
-
new C.Cartesian3()
|
|
7022
|
-
);
|
|
7023
|
-
const right = C.Cartesian3.add(
|
|
7024
|
-
C.Cartesian3.multiplyByScalar(east, Math.cos(headingRad), new C.Cartesian3()),
|
|
7025
|
-
C.Cartesian3.multiplyByScalar(north, -Math.sin(headingRad), new C.Cartesian3()),
|
|
7026
|
-
new C.Cartesian3()
|
|
7027
|
-
);
|
|
7028
|
-
const forward = C.Cartesian3.add(
|
|
7029
|
-
C.Cartesian3.multiplyByScalar(horizForward, Math.cos(pitchRad), new C.Cartesian3()),
|
|
7030
|
-
C.Cartesian3.multiplyByScalar(up, Math.sin(pitchRad), new C.Cartesian3()),
|
|
7031
|
-
new C.Cartesian3()
|
|
7032
|
-
);
|
|
7033
|
-
const upRotated = C.Cartesian3.add(
|
|
7034
|
-
C.Cartesian3.multiplyByScalar(horizForward, -Math.sin(pitchRad), new C.Cartesian3()),
|
|
7035
|
-
C.Cartesian3.multiplyByScalar(up, Math.cos(pitchRad), new C.Cartesian3()),
|
|
7036
|
-
new C.Cartesian3()
|
|
7037
|
-
);
|
|
7038
|
-
let result = C.Cartesian3.clone(position);
|
|
7039
|
-
result = C.Cartesian3.add(result, C.Cartesian3.multiplyByScalar(forward, offset.forward, new C.Cartesian3()), result);
|
|
7040
|
-
result = C.Cartesian3.add(result, C.Cartesian3.multiplyByScalar(right, offset.right, new C.Cartesian3()), result);
|
|
7041
|
-
result = C.Cartesian3.add(result, C.Cartesian3.multiplyByScalar(upRotated, offset.up, new C.Cartesian3()), result);
|
|
7042
|
-
return result;
|
|
7043
|
-
}
|
|
7044
|
-
/** 初始化视锥体 */
|
|
7045
|
-
initFrustum() {
|
|
7046
|
-
if (!this.showFrustum || !this.layer) return;
|
|
7047
|
-
const C = this.CesiumNS;
|
|
7048
|
-
this.frustum = new FrustumPyramid(this.CesiumNS, this.layer, {
|
|
7049
|
-
fov: 50,
|
|
7050
|
-
length: 500,
|
|
7051
|
-
color: C.Color.CYAN,
|
|
7052
|
-
fillAlpha: 0.25,
|
|
7053
|
-
width: 2
|
|
7054
|
-
});
|
|
7055
|
-
this.frustum.showAtDynamic(
|
|
7056
|
-
() => this.getCurrentPose().position,
|
|
7057
|
-
() => this.getCurrentPose().heading,
|
|
7058
|
-
() => this.getCurrentPose().pitch,
|
|
7059
|
-
() => this.getCurrentPose().roll,
|
|
7060
|
-
() => 50,
|
|
7061
|
-
() => 500
|
|
7062
|
-
);
|
|
7063
|
-
}
|
|
7064
|
-
/** 初始化轨迹高亮 */
|
|
7065
|
-
initTrackHighlight() {
|
|
7066
|
-
if (!this.trackHighlightOptions.enabled || !this.layer) return;
|
|
7067
|
-
if (this.waypoints.length < 2) return;
|
|
7068
|
-
const C = this.CesiumNS;
|
|
7069
|
-
const layer = this.layer;
|
|
7070
|
-
const traveledColor = this.trackHighlightOptions.traveledColor ?? C.Color.fromCssColorString("#00FF88");
|
|
7071
|
-
const remainingColor = this.trackHighlightOptions.remainingColor ?? C.Color.fromCssColorString("#4A90D9").withAlpha(0.7);
|
|
7072
|
-
this.traveledPathEntity = layer.entities.add({
|
|
7073
|
-
polyline: {
|
|
7074
|
-
positions: new C.CallbackProperty(() => this.getTraveledPositions(), false),
|
|
7075
|
-
width: this.trackHighlightOptions.traveledWidth ?? 6,
|
|
7076
|
-
material: traveledColor,
|
|
7077
|
-
clampToGround: false
|
|
7078
|
-
},
|
|
7079
|
-
properties: { _type: "flight-preview-traveled" }
|
|
7080
|
-
});
|
|
7081
|
-
this.remainingPathEntity = layer.entities.add({
|
|
7082
|
-
polyline: {
|
|
7083
|
-
positions: new C.CallbackProperty(() => this.getRemainingPositions(), false),
|
|
7084
|
-
width: this.trackHighlightOptions.remainingWidth ?? 4,
|
|
7085
|
-
material: remainingColor,
|
|
7086
|
-
clampToGround: false
|
|
7087
|
-
},
|
|
7088
|
-
properties: { _type: "flight-preview-remaining" }
|
|
7089
|
-
});
|
|
7090
|
-
}
|
|
7091
|
-
/** 初始化预览窗口 */
|
|
7092
|
-
initPathPreview() {
|
|
7093
|
-
this.pathPreview = new PathPreview(
|
|
7094
|
-
this.CesiumNS,
|
|
7095
|
-
this.viewer,
|
|
7096
|
-
{
|
|
7097
|
-
container: this.options.previewContainer,
|
|
7098
|
-
fov: 50,
|
|
7099
|
-
pitch: -10,
|
|
7100
|
-
useGlobalEventBus: true
|
|
7101
|
-
}
|
|
7102
|
-
);
|
|
7103
|
-
this.pathPreview.hide();
|
|
7104
|
-
}
|
|
7105
|
-
// ==================== 私有方法 - 动画循环 ====================
|
|
7106
|
-
/** 启动动画循环 */
|
|
7107
|
-
startAnimationLoop() {
|
|
7108
|
-
if (this.animationFrameId) return;
|
|
7109
|
-
const animate = (currentTime) => {
|
|
7110
|
-
if (this.state !== "playing" || this.destroyed) {
|
|
7111
|
-
this.animationFrameId = void 0;
|
|
7112
|
-
return;
|
|
7113
|
-
}
|
|
7114
|
-
const deltaTime = this.lastFrameTime ? (currentTime - this.lastFrameTime) / 1e3 : 0;
|
|
7115
|
-
this.lastFrameTime = currentTime;
|
|
7116
|
-
this.currentDistance += this.speed * deltaTime;
|
|
7117
|
-
if (this.currentDistance >= this.totalDistance) {
|
|
7118
|
-
if (this.loop) {
|
|
7119
|
-
this.currentDistance = this.currentDistance % this.totalDistance;
|
|
7120
|
-
} else {
|
|
7121
|
-
this.currentDistance = this.totalDistance;
|
|
7122
|
-
this.setState("stopped");
|
|
7123
|
-
this.stopAnimationLoop();
|
|
7124
|
-
return;
|
|
7125
|
-
}
|
|
7126
|
-
}
|
|
7127
|
-
this.updateVisualization();
|
|
7128
|
-
this.animationFrameId = requestAnimationFrame(animate);
|
|
7129
|
-
};
|
|
7130
|
-
this.animationFrameId = requestAnimationFrame(animate);
|
|
7131
|
-
}
|
|
7132
|
-
/** 停止动画循环 */
|
|
7133
|
-
stopAnimationLoop() {
|
|
7134
|
-
if (this.animationFrameId) {
|
|
7135
|
-
cancelAnimationFrame(this.animationFrameId);
|
|
7136
|
-
this.animationFrameId = void 0;
|
|
7137
|
-
}
|
|
7138
|
-
this.lastFrameTime = void 0;
|
|
7139
|
-
}
|
|
7140
|
-
// ==================== 私有方法 - 路径插值 ====================
|
|
7141
|
-
/** 查找当前所在航段 */
|
|
7142
|
-
findCurrentSegment() {
|
|
7143
|
-
if (this.waypoints.length === 0) {
|
|
7144
|
-
return { segmentIndex: 0, progressInSegment: 0 };
|
|
7145
|
-
}
|
|
7146
|
-
for (let i = 0; i < this.waypoints.length - 1; i++) {
|
|
7147
|
-
const wp = this.waypoints[i];
|
|
7148
|
-
const nextWp = this.waypoints[i + 1];
|
|
7149
|
-
if (this.currentDistance <= nextWp.cumulativeDistance) {
|
|
7150
|
-
const segmentStart = wp.cumulativeDistance;
|
|
7151
|
-
const segmentLength = wp.distanceToNext;
|
|
7152
|
-
const distanceInSegment = this.currentDistance - segmentStart;
|
|
7153
|
-
const progressInSegment = segmentLength > 0 ? distanceInSegment / segmentLength : 0;
|
|
7154
|
-
return { segmentIndex: i, progressInSegment };
|
|
7155
|
-
}
|
|
7156
|
-
}
|
|
7157
|
-
return { segmentIndex: this.waypoints.length - 2, progressInSegment: 1 };
|
|
7158
|
-
}
|
|
7159
|
-
/** 获取当前姿态 */
|
|
7160
|
-
getCurrentPose() {
|
|
7161
|
-
if (this.waypoints.length === 0) {
|
|
7162
|
-
const C = this.CesiumNS;
|
|
7163
|
-
return {
|
|
7164
|
-
position: new C.Cartesian3(),
|
|
7165
|
-
heading: 0,
|
|
7166
|
-
pitch: -10,
|
|
7167
|
-
roll: 0
|
|
7168
|
-
};
|
|
7169
|
-
}
|
|
7170
|
-
const { segmentIndex, progressInSegment } = this.findCurrentSegment();
|
|
7171
|
-
const wp = this.waypoints[segmentIndex];
|
|
7172
|
-
const nextWp = this.waypoints[Math.min(segmentIndex + 1, this.waypoints.length - 1)];
|
|
7173
|
-
const position = this.interpolatePosition(wp.position, nextWp.position, progressInSegment);
|
|
7174
|
-
const heading = this.interpolateAngle(wp.heading, nextWp.heading, progressInSegment);
|
|
7175
|
-
const pitch = this.lerp(wp.pitch, nextWp.pitch, progressInSegment);
|
|
7176
|
-
const roll = this.lerp(wp.roll, nextWp.roll, progressInSegment);
|
|
7177
|
-
return { position, heading, pitch, roll };
|
|
7178
|
-
}
|
|
7179
|
-
/** 位置插值 */
|
|
7180
|
-
interpolatePosition(start, end, t) {
|
|
7181
|
-
const C = this.CesiumNS;
|
|
7182
|
-
return C.Cartesian3.lerp(start, end, t, new C.Cartesian3());
|
|
7183
|
-
}
|
|
7184
|
-
/** 角度插值(处理 0-360 环绕) */
|
|
7185
|
-
interpolateAngle(start, end, t) {
|
|
7186
|
-
let diff = end - start;
|
|
7187
|
-
if (diff > 180) diff -= 360;
|
|
7188
|
-
if (diff < -180) diff += 360;
|
|
7189
|
-
let result = start + diff * t;
|
|
7190
|
-
return (result % 360 + 360) % 360;
|
|
7191
|
-
}
|
|
7192
|
-
/** 线性插值 */
|
|
7193
|
-
lerp(start, end, t) {
|
|
7194
|
-
return start + (end - start) * t;
|
|
7195
|
-
}
|
|
7196
|
-
// ==================== 私有方法 - 轨迹高亮 ====================
|
|
7197
|
-
/** 获取已飞过的位置数组 */
|
|
7198
|
-
getTraveledPositions() {
|
|
7199
|
-
if (this.waypoints.length === 0) return [];
|
|
7200
|
-
const { segmentIndex, progressInSegment } = this.findCurrentSegment();
|
|
7201
|
-
const currentPos = this.getCurrentPose().position;
|
|
7202
|
-
const positions = [];
|
|
7203
|
-
for (let i = 0; i <= segmentIndex; i++) {
|
|
7204
|
-
positions.push(this.waypoints[i].position);
|
|
7205
|
-
}
|
|
7206
|
-
if (progressInSegment > 0) {
|
|
7207
|
-
positions.push(currentPos);
|
|
7208
|
-
}
|
|
7209
|
-
return positions;
|
|
7210
|
-
}
|
|
7211
|
-
/** 获取未飞过的位置数组 */
|
|
7212
|
-
getRemainingPositions() {
|
|
7213
|
-
if (this.waypoints.length === 0) return [];
|
|
7214
|
-
const { segmentIndex } = this.findCurrentSegment();
|
|
7215
|
-
const currentPos = this.getCurrentPose().position;
|
|
7216
|
-
const positions = [];
|
|
7217
|
-
positions.push(currentPos);
|
|
7218
|
-
for (let i = segmentIndex + 1; i < this.waypoints.length; i++) {
|
|
7219
|
-
positions.push(this.waypoints[i].position);
|
|
7220
|
-
}
|
|
7221
|
-
return positions;
|
|
7222
|
-
}
|
|
7223
|
-
// ==================== 私有方法 - 更新 ====================
|
|
7224
|
-
/** 更新可视化 */
|
|
7225
|
-
updateVisualization() {
|
|
7226
|
-
const pose = this.getCurrentPose();
|
|
7227
|
-
if (this.pathPreview) {
|
|
7228
|
-
this.pathPreview.setPose(
|
|
7229
|
-
pose.position,
|
|
7230
|
-
pose.heading,
|
|
7231
|
-
pose.pitch,
|
|
7232
|
-
pose.roll
|
|
7233
|
-
);
|
|
7234
|
-
}
|
|
7235
|
-
this.viewer.scene?.requestRender?.();
|
|
7236
|
-
const { segmentIndex } = this.findCurrentSegment();
|
|
7237
|
-
this.onProgressChange.emit({
|
|
7238
|
-
progress: this.getProgress(),
|
|
7239
|
-
position: pose.position,
|
|
7240
|
-
waypointIndex: segmentIndex,
|
|
7241
|
-
pose: {
|
|
7242
|
-
heading: pose.heading,
|
|
7243
|
-
pitch: pose.pitch,
|
|
7244
|
-
roll: pose.roll
|
|
7245
|
-
}
|
|
7246
|
-
});
|
|
7247
|
-
}
|
|
7248
|
-
/** 设置状态 */
|
|
7249
|
-
setState(newState) {
|
|
7250
|
-
if (this.state !== newState) {
|
|
7251
|
-
this.state = newState;
|
|
7252
|
-
this.onStateChange.emit({ state: newState });
|
|
7253
|
-
}
|
|
7254
|
-
}
|
|
7255
|
-
/** 销毁可视化组件 */
|
|
7256
|
-
destroyVisualization() {
|
|
7257
|
-
const layer = this.layer;
|
|
7258
|
-
if (this.droneEntity && layer) {
|
|
7259
|
-
try {
|
|
7260
|
-
layer.entities.remove(this.droneEntity);
|
|
7261
|
-
} catch {
|
|
7262
|
-
}
|
|
7263
|
-
}
|
|
7264
|
-
if (this.traveledPathEntity && layer) {
|
|
7265
|
-
try {
|
|
7266
|
-
layer.entities.remove(this.traveledPathEntity);
|
|
7267
|
-
} catch {
|
|
7268
|
-
}
|
|
7269
|
-
}
|
|
7270
|
-
if (this.remainingPathEntity && layer) {
|
|
7271
|
-
try {
|
|
7272
|
-
layer.entities.remove(this.remainingPathEntity);
|
|
7273
|
-
} catch {
|
|
7274
|
-
}
|
|
7275
|
-
}
|
|
7276
|
-
try {
|
|
7277
|
-
this.frustum?.destroy();
|
|
7278
|
-
} catch {
|
|
7279
|
-
}
|
|
7280
|
-
try {
|
|
7281
|
-
this.pathPreview?.destroy();
|
|
7282
|
-
} catch {
|
|
7283
|
-
}
|
|
7284
|
-
if (!this.options.layer && this.layer) {
|
|
7285
|
-
try {
|
|
7286
|
-
this.viewer.dataSources.remove(this.layer);
|
|
7287
|
-
} catch {
|
|
7288
|
-
}
|
|
7289
|
-
}
|
|
7290
|
-
this.droneEntity = void 0;
|
|
7291
|
-
this.traveledPathEntity = void 0;
|
|
7292
|
-
this.remainingPathEntity = void 0;
|
|
7293
|
-
this.frustum = void 0;
|
|
7294
|
-
this.pathPreview = void 0;
|
|
7295
|
-
this.layer = void 0;
|
|
7296
|
-
}
|
|
7297
|
-
};
|
|
7298
|
-
|
|
7299
6792
|
// src/core/CZMLPathManager.ts
|
|
7300
6793
|
var _CZMLPathManager = class _CZMLPathManager {
|
|
7301
6794
|
constructor(CesiumNS, viewer) {
|
|
@@ -7409,50 +6902,6 @@ var _CZMLPathManager = class _CZMLPathManager {
|
|
|
7409
6902
|
renderFlightPathPreview(options) {
|
|
7410
6903
|
return renderFlightPathPreview(this.CesiumNS, this.viewer, options);
|
|
7411
6904
|
}
|
|
7412
|
-
/**
|
|
7413
|
-
* 开始飞行预览:飞机沿航线飞行动画
|
|
7414
|
-
*
|
|
7415
|
-
* 功能:
|
|
7416
|
-
* - 飞机游标沿航线从起点飞到终点,循环播放
|
|
7417
|
-
* - 预览窗口实时显示飞机第一人称视角
|
|
7418
|
-
* - 轨迹高亮显示已飞过/未飞过的航段
|
|
7419
|
-
* - 预览模式下禁止选择/编辑航线、航点
|
|
7420
|
-
*
|
|
7421
|
-
* @param options 飞行预览选项
|
|
7422
|
-
* @returns 飞行预览控制器
|
|
7423
|
-
*
|
|
7424
|
-
* @example
|
|
7425
|
-
* ```typescript
|
|
7426
|
-
* const preview = pathManager.startFlightPreview({
|
|
7427
|
-
* waylineData: myWayline,
|
|
7428
|
-
* speed: 15, // 飞行速度 15m/s
|
|
7429
|
-
* loop: true, // 循环播放
|
|
7430
|
-
* trackHighlight: {
|
|
7431
|
-
* enabled: true,
|
|
7432
|
-
* traveledColor: Cesium.Color.fromCssColorString('#00FF88'),
|
|
7433
|
-
* remainingColor: Cesium.Color.fromCssColorString('#4A90D9'),
|
|
7434
|
-
* },
|
|
7435
|
-
* });
|
|
7436
|
-
*
|
|
7437
|
-
* preview.start(); // 开始播放
|
|
7438
|
-
* preview.pause(); // 暂停
|
|
7439
|
-
* preview.resume(); // 继续
|
|
7440
|
-
* preview.stop(); // 停止
|
|
7441
|
-
* preview.seek(0.5); // 跳转到 50%
|
|
7442
|
-
* preview.setSpeed(20); // 设置速度
|
|
7443
|
-
*
|
|
7444
|
-
* // 监听进度变化
|
|
7445
|
-
* preview.onProgressChange.on(({ progress, waypointIndex }) => {
|
|
7446
|
-
* console.log(`进度: ${(progress * 100).toFixed(1)}%`);
|
|
7447
|
-
* });
|
|
7448
|
-
*
|
|
7449
|
-
* // 销毁
|
|
7450
|
-
* preview.destroy();
|
|
7451
|
-
* ```
|
|
7452
|
-
*/
|
|
7453
|
-
startFlightPreview(options) {
|
|
7454
|
-
return new FlightPreviewController(this.CesiumNS, this.viewer, options);
|
|
7455
|
-
}
|
|
7456
6905
|
resolveEntity(entityOrId) {
|
|
7457
6906
|
return typeof entityOrId === "string" ? this.viewer.entities.getById(entityOrId) : entityOrId;
|
|
7458
6907
|
}
|
|
@@ -7648,6 +7097,8 @@ var PolygonEditor = class {
|
|
|
7648
7097
|
__publicField(this, "keydownHandler");
|
|
7649
7098
|
// 保存编码前的原始名称,用于还原
|
|
7650
7099
|
__publicField(this, "originalPolygonNames", /* @__PURE__ */ new Map());
|
|
7100
|
+
// 保存双击缩放是否被禁用的状态
|
|
7101
|
+
__publicField(this, "doubleClickZoomDisabled", false);
|
|
7651
7102
|
}
|
|
7652
7103
|
/**
|
|
7653
7104
|
* 根据图层名称获取颜色配置
|
|
@@ -7922,12 +7373,62 @@ var PolygonEditor = class {
|
|
|
7922
7373
|
}
|
|
7923
7374
|
this.handler = void 0;
|
|
7924
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
|
+
}
|
|
7925
7425
|
/**
|
|
7926
7426
|
* 完成编辑/绘制的通用清理逻辑
|
|
7927
7427
|
*/
|
|
7928
7428
|
finalizePolygonSession(layer, onComplete, entity) {
|
|
7929
7429
|
this.destroyHandler();
|
|
7930
7430
|
this.removeKeyboardListener();
|
|
7431
|
+
this.restoreDoubleClickZoom();
|
|
7931
7432
|
if (entity && onComplete) {
|
|
7932
7433
|
onComplete(entity);
|
|
7933
7434
|
}
|
|
@@ -7970,6 +7471,7 @@ var PolygonEditor = class {
|
|
|
7970
7471
|
}
|
|
7971
7472
|
this.clearAllPolygonHighlights();
|
|
7972
7473
|
this.stopInternal(layer);
|
|
7474
|
+
this.disableDoubleClickZoom();
|
|
7973
7475
|
this.drawingPositions = [];
|
|
7974
7476
|
this.drawHandles = [];
|
|
7975
7477
|
this.rubberBandPoint = void 0;
|
|
@@ -8114,6 +7616,7 @@ var PolygonEditor = class {
|
|
|
8114
7616
|
}
|
|
8115
7617
|
this.destroyHandler();
|
|
8116
7618
|
this.removeKeyboardListener();
|
|
7619
|
+
this.restoreDoubleClickZoom();
|
|
8117
7620
|
}
|
|
8118
7621
|
/**
|
|
8119
7622
|
* 开始编辑多边形
|
|
@@ -8125,6 +7628,7 @@ var PolygonEditor = class {
|
|
|
8125
7628
|
startEditing(polygonEntity, layer, onComplete) {
|
|
8126
7629
|
const C = this.CesiumNS;
|
|
8127
7630
|
this.cleanupHandles(layer);
|
|
7631
|
+
this.disableDoubleClickZoom();
|
|
8128
7632
|
const colors = this.getColorConfig(layer.name);
|
|
8129
7633
|
const line_color = colors.lineColor;
|
|
8130
7634
|
const line_fina_color = colors.lineFinalColor;
|
|
@@ -9547,6 +9051,7 @@ var PolygonEditor = class {
|
|
|
9547
9051
|
const nameStr = `circle_${(circleCount + 1).toString().padStart(2, "0")}`;
|
|
9548
9052
|
this.clearAllPolygonHighlights();
|
|
9549
9053
|
this.stopInternal();
|
|
9054
|
+
this.disableDoubleClickZoom();
|
|
9550
9055
|
let centerPosition = null;
|
|
9551
9056
|
let currentRadius = 0;
|
|
9552
9057
|
let tempCenterPoint;
|
|
@@ -9760,6 +9265,7 @@ var PolygonEditor = class {
|
|
|
9760
9265
|
currentRadius = 0;
|
|
9761
9266
|
this.destroyHandler();
|
|
9762
9267
|
this.removeKeyboardListener();
|
|
9268
|
+
this.restoreDoubleClickZoom();
|
|
9763
9269
|
};
|
|
9764
9270
|
this.handler && this.handler.setInputAction(
|
|
9765
9271
|
cancelCircleDrawing,
|
|
@@ -9795,6 +9301,7 @@ var PolygonEditor = class {
|
|
|
9795
9301
|
const line_fina_color = colors.lineFinalColor;
|
|
9796
9302
|
this.clearAllPolygonHighlights();
|
|
9797
9303
|
this.stopInternal();
|
|
9304
|
+
this.disableDoubleClickZoom();
|
|
9798
9305
|
let pointCount = 0;
|
|
9799
9306
|
this.handler = new C.ScreenSpaceEventHandler(this.viewer.scene.canvas);
|
|
9800
9307
|
this.handler && this.handler.setInputAction(
|
|
@@ -9889,6 +9396,7 @@ var PolygonEditor = class {
|
|
|
9889
9396
|
const stopPointDrawing = () => {
|
|
9890
9397
|
this.destroyHandler();
|
|
9891
9398
|
this.removeKeyboardListener();
|
|
9399
|
+
this.restoreDoubleClickZoom();
|
|
9892
9400
|
};
|
|
9893
9401
|
this.handler && this.handler.setInputAction(
|
|
9894
9402
|
stopPointDrawing,
|
|
@@ -11394,9 +10902,371 @@ function convertPathToSinofly(data, options) {
|
|
|
11394
10902
|
return sinoflyData;
|
|
11395
10903
|
}
|
|
11396
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
|
+
|
|
11397
11267
|
// src/index.ts
|
|
11398
11268
|
var placeholder = { ready: true };
|
|
11399
11269
|
|
|
11400
|
-
export { CZMLManager, CameraEventBus, CameraFOVController, CameraManager, Emitter,
|
|
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 };
|
|
11401
11271
|
//# sourceMappingURL=index.js.map
|
|
11402
11272
|
//# sourceMappingURL=index.js.map
|