Browse Source

航线上锁

master
cuitw 2 months ago
parent
commit
dc5a44c045
  1. 23
      ruoyi-ui/src/views/cesiumMap/ContextMenu.vue
  2. 8
      ruoyi-ui/src/views/cesiumMap/DrawingToolbar.vue
  3. 475
      ruoyi-ui/src/views/cesiumMap/index.vue

23
ruoyi-ui/src/views/cesiumMap/ContextMenu.vue

@ -1,12 +1,21 @@
<template>
<div class="context-menu" v-if="visible" :style="positionStyle">
<div class="menu-section" v-if="!entityData || entityData.type !== 'routePlatform'">
<div class="menu-section" v-if="!entityData || (entityData.type !== 'routePlatform' && entityData.type !== 'route')">
<div class="menu-item" @click="handleDelete">
<span class="menu-icon">🗑</span>
<span>删除</span>
</div>
</div>
<!-- 航线上锁/解锁上锁后不可编辑 -->
<div class="menu-section" v-if="entityData && entityData.type === 'route'">
<div class="menu-title">航线编辑</div>
<div class="menu-item" @click="handleToggleRouteLock">
<span class="menu-icon">{{ isRouteLocked ? '🔓' : '🔒' }}</span>
<span>{{ isRouteLocked ? '解锁' : '上锁' }}</span>
</div>
</div>
<!-- 航线上飞机显示/隐藏标牌 -->
<div class="menu-section" v-if="entityData && entityData.type === 'routePlatform'">
<div class="menu-title">飞机标牌</div>
@ -317,6 +326,10 @@ export default {
entityData: {
type: Object,
default: null
},
routeLocked: {
type: Object,
default: () => ({})
}
},
data() {
@ -344,6 +357,10 @@ export default {
left: this.position.x + 'px',
top: this.position.y + 'px'
}
},
isRouteLocked() {
if (!this.entityData || this.entityData.type !== 'route' || this.entityData.routeId == null) return false
return !!this.routeLocked[this.entityData.routeId]
}
},
methods: {
@ -367,6 +384,10 @@ export default {
this.$emit('toggle-route-label')
},
handleToggleRouteLock() {
this.$emit('toggle-route-lock')
},
toggleColorPicker(property) {
if (this.showColorPickerFor === property) {
this.showColorPickerFor = null

8
ruoyi-ui/src/views/cesiumMap/DrawingToolbar.vue

@ -42,10 +42,10 @@ export default {
// 线
allToolbarItems: [
{ id: 'mouse', name: '鼠标', icon: 'el-icon-position' },
{ id: 'polygon', name: '', icon: 'el-icon-house' },
{ id: 'rectangle', name: '矩形', icon: 'jx' },
{ id: 'circle', name: '圆形', icon: 'circle' },
{ id: 'sector', name: '扇形', icon: 'sx' },
{ id: 'polygon', name: '多边形空域', icon: 'el-icon-house' },
{ id: 'rectangle', name: '矩形空域', icon: 'jx' },
{ id: 'circle', name: '圆形空域', icon: 'circle' },
{ id: 'sector', name: '扇形空域', icon: 'sx' },
{ id: 'arrow', name: '箭头', icon: 'el-icon-right' },
{ id: 'text', name: '文本', icon: 'el-icon-document' },
{ id: 'image', name: '图片', icon: 'el-icon-picture-outline' },

475
ruoyi-ui/src/views/cesiumMap/index.vue

@ -32,12 +32,14 @@
:visible="contextMenu.visible"
:position="contextMenu.position"
:entity-data="contextMenu.entityData"
:route-locked="routeLocked"
@delete="deleteEntityFromContextMenu"
@update-property="updateEntityProperty"
@edit-platform-position="openPlatformIconPositionDialog"
@edit-platform-heading="openPlatformIconHeadingDialog"
@show-transform-box="showPlatformIconTransformBox"
@toggle-route-label="toggleRouteLabelVisibility"
@toggle-route-lock="toggleRouteLock"
/>
<!-- 定位弹窗 -->
@ -171,6 +173,8 @@ export default {
},
// 线routeId -> true / false
routeLabelVisible: {},
// 线routeId -> true / false
routeLocked: {},
//
defaultStyles: {
point: { color: '#FF0000', size: 12 },
@ -491,8 +495,8 @@ export default {
return positions;
}, false),
width: 3,
material: Cesium.Color.fromCssColorString('#800080'),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial('#800080'),
clampToGround: true
}
});
}
@ -699,8 +703,8 @@ export default {
polyline: {
positions: linePositions,
width: 3,
material: Cesium.Color.fromCssColorString('#008aff'),
arcType: Cesium.ArcType.NONE,
material: this.getPolylineSolidMaterial('#008aff'),
clampToGround: true,
disableDepthTestDistance: Number.POSITIVE_INFINITY
}
});
@ -935,19 +939,10 @@ export default {
const wpColor = wpStyle.color || '#ffffff';
const wpOutline = wpStyle.outlineColor || '#0078FF';
const wpOutlineW = wpStyle.outlineWidth != null ? wpStyle.outlineWidth : 2;
// 线线 3线线线
// 线线 3线使齿
const lineWidth = lineStyle.width != null ? lineStyle.width : 3;
const lineColor = lineStyle.color || '#800080';
const gapColor = lineStyle.gapColor != null ? lineStyle.gapColor : '#000000';
const dashLen = lineStyle.dashLength != null ? lineStyle.dashLength : 20;
const useDash = (lineStyle.style || 'solid') === 'dash';
const lineMaterial = useDash
? new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString(lineColor),
gapColor: Cesium.Color.fromCssColorString(gapColor),
dashLength: dashLen
})
: Cesium.Color.fromCssColorString(lineColor);
const lineMaterial = this.getPolylineSolidMaterial(lineColor);
//
const originalPositions = [];
waypoints.forEach((wp) => {
@ -1110,7 +1105,7 @@ export default {
finalPathPositions.push(exit);
this.viewer.entities.add({
id: `hold-line-${routeId}-${i}`,
polyline: { positions: [entry, ...arcPoints.slice(1), exit], width: 8, material: Cesium.Color.ORANGE, arcType: Cesium.ArcType.NONE, zIndex: 20 },
polyline: { positions: [entry, ...arcPoints.slice(1), exit], width: 8, material: this.getPolylineSolidMaterial('#FFA500'), clampToGround: true, zIndex: 20 },
properties: { routeId: routeId }
});
lastPos = exit;
@ -1127,7 +1122,7 @@ export default {
const arcPoints = this.computeArcPositions(lastPos, currPos, nextLogical, radius);
this.viewer.entities.add({
id: `arc-line-${routeId}-${i}`,
polyline: { positions: arcPoints, width: lineWidth, material: lineMaterial, arcType: Cesium.ArcType.NONE, zIndex: 20 },
polyline: { positions: arcPoints, width: lineWidth, material: lineMaterial, clampToGround: true, zIndex: 20 },
properties: { routeId: routeId }
});
// 线dbId wp.id
@ -1178,13 +1173,13 @@ export default {
positions: finalPathPositions,
width: lineWidth,
material: lineMaterial,
arcType: Cesium.ArcType.NONE,
clampToGround: true,
zIndex: 1
},
properties: {isMissionRouteLine: true, routeId: routeId}
});
if (this.allEntities) {
this.allEntities.push({id: lineId, entity: routeEntity, type: 'route'});
this.allEntities.push({ id: lineId, entity: routeEntity, type: 'route', routeId });
}
}
},
@ -1688,18 +1683,27 @@ export default {
imageryProvider: false,
terrainProvider: new Cesium.EllipsoidTerrainProvider(),
baseLayer: false,
// CPU/GPU viewer.scene.requestRender()
requestRenderMode: true,
maximumRenderTimeChange: Infinity,
// canvas readPixels false
contextOptions: {
preserveDrawingBuffer: true,
webgl: {
antialias: true,
msaa: true,
msaaSamples: 8
}
}
antialias: true // WebGL 齿
},
// 齿WebGL2 8 线
msaaSamples: 8
})
this.viewer.cesiumWidget.creditContainer.style.display = "none"
// FXAA 线齿
try {
if (this.viewer.scene.postProcessStages && this.viewer.scene.postProcessStages.fxaa) {
this.viewer.scene.postProcessStages.fxaa.enabled = true
}
} catch (e) { /* 部分 Cesium 版本可能无此 API */ }
// 80% 线齿
this.applyResolutionScale()
this._resolutionScaleCleanup = this.setupResolutionScaleListener()
//
if (this.viewer.scene.requestRenderMode) {
this.viewer.entities.collectionChanged.addEventListener(() => {
@ -1735,7 +1739,6 @@ export default {
console.log('Cesium离线二维地图已加载')
// 1.
this.viewer.scene.postProcessStages.fxaa.enabled = true
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((click) => {
//
@ -1779,6 +1782,11 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
this.entityClickDebounceTimer = setTimeout(() => {
this.entityClickDebounceTimer = null;
this.lastEntityClickTime = 0;
// 线
if (this.routeLocked[routeId]) {
this.$message && this.$message.info('该航线已上锁,请右键选择“解锁”后再编辑');
return;
}
if (isWaypoint) {
console.log(`>>> [地图触发] 点击了点 ${dbId}, 属于航线 ${routeId}`);
this.$emit('open-waypoint-dialog', dbId, routeId);
@ -1869,8 +1877,26 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
}
}
}
// 线 routeId线/
if (!entityData && pickedEntity.properties) {
const now = Cesium.JulianDate.now();
const props = pickedEntity.properties.getValue ? pickedEntity.properties.getValue(now) : null;
if (props) {
const isWp = props.isMissionWaypoint && props.isMissionWaypoint.getValue ? props.isMissionWaypoint.getValue() : props.isMissionWaypoint;
const isLine = props.isMissionRouteLine && props.isMissionRouteLine.getValue ? props.isMissionRouteLine.getValue() : props.isMissionRouteLine;
if (isWp || isLine) {
let rId = props.routeId;
if (rId && rId.getValue) rId = rId.getValue();
if (rId) entityData = { type: 'route', routeId: rId };
}
}
}
}
// 线 allEntities routeId id
if (entityData && entityData.type === 'route' && entityData.id && !entityData.routeId) {
entityData = { ...entityData, routeId: entityData.id.replace('route-line-', '') };
}
if (entityData && entityData.type !== 'route') {
if (entityData) {
this.contextMenu = {
visible: true,
position: {
@ -2503,28 +2529,25 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
}
this.tempEntity = this.viewer.entities.add({
polyline: {
positions: this.drawingPoints,
positions: this.getPolylinePositionsStraightIn2D([...this.drawingPoints], false),
width: this.defaultStyles.line.width,
material: Cesium.Color.fromCssColorString(this.defaultStyles.line.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.line.color),
clampToGround: true
}
});
}
// 线使 CallbackProperty
// 线线2D 线
this.tempPreviewEntity = this.viewer.entities.add({
polyline: {
positions: new Cesium.CallbackProperty(() => {
if (this.activeCursorPosition) {
return [this.drawingPoints[this.drawingPoints.length - 1], this.activeCursorPosition];
if (this.activeCursorPosition && this.drawingPoints.length > 0) {
return this.getPolylinePositionsStraightIn2D([this.drawingPoints[this.drawingPoints.length - 1], this.activeCursorPosition], false);
}
return [this.drawingPoints[this.drawingPoints.length - 1]];
return this.drawingPoints.length > 0 ? [this.drawingPoints[this.drawingPoints.length - 1]] : [];
}, false),
width: this.defaultStyles.line.width,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString(this.defaultStyles.line.color),
dashLength: 16
}),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.line.color),
clampToGround: true
}
});
}
@ -2630,16 +2653,15 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
polyline: {
// positions 使 CallbackProperty 线
positions: new Cesium.CallbackProperty(() => {
if (this.activeCursorPosition) {
// [, , ]
return [...this.drawingPoints, this.activeCursorPosition, this.drawingPoints[0]];
if (this.activeCursorPosition && this.drawingPoints.length >= 2) {
return this.getPolylinePositionsStraightIn2D([...this.drawingPoints, this.activeCursorPosition], true);
}
return this.drawingPoints;
}, false),
width: this.defaultStyles.polygon.width,
// 使线
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color),
arcType: Cesium.ArcType.NONE
// 线2D 线
material: this.getPolylineSolidMaterial(this.defaultStyles.polygon.color),
clampToGround: true
}
});
}
@ -2659,12 +2681,6 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
finishPolygonDrawing() {
const positions = [...this.drawingPoints]
const entity = this.addPolygonEntity(positions)
//
const area = this.calculatePolygonArea(positions)
this.measurementResult = {
area: area,
type: 'polygon'
}
//
this.drawingPoints = []
if (this.tempEntity) {
@ -2707,34 +2723,23 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
}
return Cesium.Rectangle.fromDegrees(0, 0, 0, 0);
}, false),
material: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color).withAlpha(this.defaultStyles.rectangle.opacity)
//
material: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color).withAlpha(this.defaultStyles.rectangle.opacity),
clampToGround: true //
},
//
polyline: {
// 使 CallbackProperty 线
positions: new Cesium.CallbackProperty(() => {
if (this.drawingPoints.length > 0 && this.activeCursorPosition) {
//
const rect = Cesium.Rectangle.fromCartesianArray([this.drawingPoints[0], this.activeCursorPosition]);
const west = rect.west;
const south = rect.south;
const east = rect.east;
const north = rect.north;
//
const southwest = Cesium.Cartesian3.fromRadians(west, south);
const southeast = Cesium.Cartesian3.fromRadians(east, south);
const northeast = Cesium.Cartesian3.fromRadians(east, north);
const northwest = Cesium.Cartesian3.fromRadians(west, north);
// 线
return [southwest, southeast, northeast, northwest, southwest];
return this.getRectangleBorderPositions(rect);
}
return [];
}, false),
width: this.defaultStyles.rectangle.width,
material: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.rectangle.color),
clampToGround: true //
}
});
}
@ -2765,20 +2770,8 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
this.entityCounter++;
const id = `rectangle_${this.entityCounter}`;
//
const west = rect.west;
const south = rect.south;
const east = rect.east;
const north = rect.north;
//
const southwest = Cesium.Cartesian3.fromRadians(west, south);
const southeast = Cesium.Cartesian3.fromRadians(east, south);
const northeast = Cesium.Cartesian3.fromRadians(east, north);
const northwest = Cesium.Cartesian3.fromRadians(west, north);
// 线
const borderPositions = [southwest, southeast, northeast, northwest, southwest];
// 线/2D 线
const borderPositions = this.getRectangleBorderPositions(rect);
//
const finalEntity = this.viewer.entities.add({
@ -2786,14 +2779,15 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
//
rectangle: {
coordinates: rect,
material: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color).withAlpha(this.defaultStyles.rectangle.opacity)
material: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color).withAlpha(this.defaultStyles.rectangle.opacity),
clampToGround: true
},
//
polyline: {
positions: borderPositions,
width: this.defaultStyles.rectangle.width,
material: Cesium.Color.fromCssColorString(this.defaultStyles.rectangle.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.rectangle.color),
clampToGround: true
}
});
// 4.
@ -2807,16 +2801,10 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
borderColor: this.defaultStyles.rectangle.color, //
opacity: this.defaultStyles.rectangle.opacity,
width: this.defaultStyles.rectangle.width,
label: `矩形 ${this.entityCounter}`
label: `矩形空域 ${this.entityCounter}`
};
this.allEntities.push(entityData);
// 5.
const area = this.calculateRectangleArea(rect);
this.measurementResult = {
area: area,
type: 'rectangle'
};
// 6.
// 5.
this.drawingPoints = [];
},
//
@ -2940,9 +2928,10 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
}, false),
//
material: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color).withAlpha(this.defaultStyles.circle.opacity),
arcType: Cesium.ArcType.NONE
// outline polyline
clampToGround: true
},
// - 使 polyline
//
polyline: {
// 使 CallbackProperty 线
positions: new Cesium.CallbackProperty(() => {
@ -2962,8 +2951,8 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
}
}, false),
width: this.defaultStyles.circle.width,
material: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.circle.color),
clampToGround: true
}
});
}
@ -3006,21 +2995,24 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
// 线
const circlePositions = this.generateCirclePositions(centerPoint, radius);
//
const finalEntity = this.viewer.entities.add({
id: id,
position: centerPoint,
//
ellipse: {
semiMajorAxis: radius,
semiMinorAxis: radius,
//
material: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color).withAlpha(this.defaultStyles.circle.opacity),
arcType: Cesium.ArcType.NONE
clampToGround: true
},
//
polyline: {
positions: circlePositions,
width: this.defaultStyles.circle.width,
material: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.circle.color),
clampToGround: true
}
});
// 4.
@ -3035,18 +3027,10 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
opacity: this.defaultStyles.circle.opacity,
width: this.defaultStyles.circle.width,
radius: radius,
label: `圆形 ${this.entityCounter}`
label: `圆形空域 ${this.entityCounter}`
};
this.allEntities.push(entityData);
// 5. (π * r²)
//
const area = Math.PI * Math.pow(radius, 2);
this.measurementResult = {
radius: radius, //
area: area,
type: 'circle'
};
// 6.
// 5.
this.drawingPoints = [];
this.activeCursorPosition = null;
},
@ -3073,7 +3057,8 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
},
//
startSectorDrawing() {
this.drawingPoints = [];
//
this.drawingPoints = []; //
// 1.
if (this.tempEntity) this.viewer.entities.remove(this.tempEntity);
if (this.tempPreviewEntity) this.viewer.entities.remove(this.tempPreviewEntity);
@ -3104,18 +3089,15 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
return [];
}, false),
width: this.defaultStyles.sector.width,
material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString(this.defaultStyles.sector.color),
dashLength: 16
}),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.sector.color),
clampToGround: true
}
});
}
// --- B ---
else if (this.drawingPoints.length === 1) {
this.drawingPoints.push(position);
this.activeCursorPosition = position;
this.activeCursorPosition = position; // activeCursorPosition
const centerPoint = this.drawingPoints[0];
const radiusPoint = this.drawingPoints[1];
const fixedRadius = Cesium.Cartesian3.distance(centerPoint, radiusPoint);
@ -3142,7 +3124,9 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
}
return new Cesium.PolygonHierarchy([]);
}, false),
material: Cesium.Color.fromCssColorString(this.defaultStyles.sector.color).withAlpha(this.defaultStyles.sector.opacity)
//
material: Cesium.Color.fromCssColorString(this.defaultStyles.sector.color).withAlpha(this.defaultStyles.sector.opacity),
clampToGround: true
},
//
polyline: {
@ -3161,15 +3145,15 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
return [];
}, false),
width: this.defaultStyles.sector.width,
material: Cesium.Color.fromCssColorString(this.defaultStyles.sector.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.sector.color),
clampToGround: true
}
});
}
// --- C ---
else if (this.drawingPoints.length === 2) {
this.drawingPoints.push(position);
this.activeCursorPosition = null;
this.activeCursorPosition = null; //
//
this.finishSectorDrawing(this.drawingPoints[0], this.drawingPoints[1], this.drawingPoints[2]);
}
@ -3195,18 +3179,20 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
// 3.
const finalEntity = this.viewer.entities.add({
id: 'sector-' + new Date().getTime(),
name: `扇形 ${this.entityCounter}`,
name: `扇形空域 ${this.entityCounter}`,
//
polygon: {
hierarchy: new Cesium.PolygonHierarchy(positions),
material: Cesium.Color.fromCssColorString(this.defaultStyles.sector.color).withAlpha(this.defaultStyles.sector.opacity)
//
material: Cesium.Color.fromCssColorString(this.defaultStyles.sector.color).withAlpha(this.defaultStyles.sector.opacity),
clampToGround: true
},
//
polyline: {
positions: positions,
width: this.defaultStyles.sector.width,
material: Cesium.Color.fromCssColorString(this.defaultStyles.sector.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.sector.color),
clampToGround: true
}
});
// 4.
@ -3220,18 +3206,18 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
endAngle: endAngle,
positions: positions,
entity: finalEntity,
color: this.defaultStyles.sector.color,
borderColor: this.defaultStyles.sector.color,
color: this.defaultStyles.sector.color, //
borderColor: this.defaultStyles.sector.color, //
opacity: 0.5,
width: this.defaultStyles.sector.width,
label: `扇形 ${this.entityCounter}`
label: `扇形空域 ${this.entityCounter}`
};
this.allEntities.push(entityData);
// 5.
this.drawingPoints = [];
},
// 线
generateCirclePositions(center, radius, numPoints = 1024) {
// 线齿
generateCirclePositions(center, radius, numPoints = 256) {
const positions = [];
const ellipsoid = this.viewer.scene.globe.ellipsoid;
@ -3307,8 +3293,8 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
}
//
angleDiff = Math.max(0.01, angleDiff);
//
const numPoints = Math.max(200, Math.ceil(angleDiff * 180 / Math.PI));
// 齿
const numPoints = Math.max(64, Math.ceil(angleDiff * 180 / Math.PI / 2));
const angleStep = angleDiff / (numPoints - 1);
//
for (let i = 0; i < numPoints; i++) {
@ -3323,14 +3309,6 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
positions.push(center);
return positions;
},
//
generateSectorEdgePoint(center, radius, angle) {
const centerLL = Cesium.Cartographic.fromCartesian(center);
const distance = radius / 6378137;
const lat = centerLL.latitude + Math.sin(angle) * distance;
const lng = centerLL.longitude + Math.cos(angle) * distance / Math.cos(centerLL.latitude);
return Cesium.Cartesian3.fromRadians(lng, lat);
},
//
calculateDistance(point1, point2) {
return Cesium.Cartesian3.distance(point1, point2);
@ -3372,12 +3350,12 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
}
return [];
}, false),
width: 12, //
// 使
width: 8, //
// 使
material: new Cesium.PolylineArrowMaterialProperty(
Cesium.Color.fromCssColorString(this.defaultStyles.arrow.color)
),
arcType: Cesium.ArcType.NONE,
clampToGround: true, //
widthInMeters: false // 使
}
});
@ -3425,11 +3403,11 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
name: `箭头 ${this.entityCounter}`,
polyline: {
positions: positions,
width: 12, //
width: 8, //
material: new Cesium.PolylineArrowMaterialProperty(
Cesium.Color.fromCssColorString(this.defaultStyles.arrow.color)
),
arcType: Cesium.ArcType.NONE,
clampToGround: true,
// 使
widthInMeters: false
}
@ -3707,14 +3685,15 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
addLineEntity(positions, pointEntities = []) {
this.entityCounter++
const id = `line_${this.entityCounter}`
const straightPositions = this.getPolylinePositionsStraightIn2D(positions, false)
const entity = this.viewer.entities.add({
id: id,
name: `线 ${this.entityCounter}`,
polyline: {
positions: positions,
positions: straightPositions,
width: this.defaultStyles.line.width,
material: Cesium.Color.fromCssColorString(this.defaultStyles.line.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.line.color),
clampToGround: true
}
})
const entityData = {
@ -3739,23 +3718,21 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
addPolygonEntity(positions) {
this.entityCounter++
const id = `polygon_${this.entityCounter}`
//
const polygonPositions = [...positions, positions[0]]
//
const straightBorder = this.getPolylinePositionsStraightIn2D(polygonPositions, false)
const entity = this.viewer.entities.add({
id: id,
name: `${this.entityCounter}`,
//
polygon: {
hierarchy: new Cesium.PolygonHierarchy(polygonPositions),
material: Cesium.Color.TRANSPARENT
material: Cesium.Color.TRANSPARENT,
clampToGround: true
},
//
polyline: {
positions: polygonPositions,
positions: straightBorder,
width: this.defaultStyles.polygon.width,
material: Cesium.Color.fromCssColorString(this.defaultStyles.polygon.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(this.defaultStyles.polygon.color),
clampToGround: true
}
})
const entityData = {
@ -3768,7 +3745,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
borderColor: this.defaultStyles.polygon.color,
opacity: 0,
width: this.defaultStyles.polygon.width,
label: ` ${this.entityCounter}`
label: `多边形空域 ${this.entityCounter}`
}
this.allEntities.push(entityData)
entity.clickHandler = (e) => {
@ -3782,7 +3759,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
const id = `rectangle_${this.entityCounter}`
const entity = this.viewer.entities.add({
id: id,
name: `矩形 ${this.entityCounter}`,
name: `矩形空域 ${this.entityCounter}`,
rectangle: {
coordinates: coordinates,
//
@ -3803,17 +3780,17 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
const id = `circle_${this.entityCounter}`
const entity = this.viewer.entities.add({
id: id,
name: `圆形 ${this.entityCounter}`,
name: `圆形空域 ${this.entityCounter}`,
position: center, //
// 使 ellipse ()
ellipse: {
semiMinorAxis: validRadius,
semiMajorAxis: validRadius,
semiMinorAxis: validRadius, // =
semiMajorAxis: validRadius, // =
//
material: Cesium.Color.TRANSPARENT,
outline: true,
outlineColor: Cesium.Color.fromCssColorString(this.defaultStyles.circle.color),
outlineWidth: this.defaultStyles.circle.width,
arcType: Cesium.ArcType.NONE
outlineWidth: this.defaultStyles.circle.width
}
})
// entity
@ -3821,6 +3798,113 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
return entity
},
// ================== ==================
/** 生成普通纯色折线材质(无轮廓/发光) */
getPolylineSolidMaterial(cssColor) {
const color = Cesium.Color.fromCssColorString(cssColor)
return new Cesium.ColorMaterialProperty(color)
},
/** 根据浏览器缩放比例设置渲染分辨率,减轻 80% 等缩放下线条锯齿 */
applyResolutionScale() {
if (!this.viewer || !this.viewer.cesiumWidget) return
const vv = window.visualViewport
const scale = (vv && typeof vv.scale === 'number') ? vv.scale : 1
// 使 2.2 80% 2.5线齿
let resolutionScale = 2.2
if (scale > 0 && scale < 1) {
resolutionScale = Math.min(2.5, 1 / scale)
if (resolutionScale < 2.0) resolutionScale = 2.0
}
this.viewer.resolutionScale = resolutionScale
try {
if (typeof this.viewer.cesiumWidget.resize === 'function') {
this.viewer.cesiumWidget.resize()
}
} catch (e) { /* 忽略 resize 异常 */ }
if (this.viewer.scene.requestRenderMode) this.viewer.scene.requestRender()
},
/** 监听浏览器缩放/窗口变化,更新 resolutionScale;返回取消监听的函数 */
setupResolutionScaleListener() {
const onResize = () => {
this.applyResolutionScale()
}
if (window.visualViewport) {
window.visualViewport.addEventListener('resize', onResize)
window.visualViewport.addEventListener('scroll', onResize)
}
window.addEventListener('resize', onResize)
return () => {
if (window.visualViewport) {
window.visualViewport.removeEventListener('resize', onResize)
window.visualViewport.removeEventListener('scroll', onResize)
}
window.removeEventListener('resize', onResize)
}
},
/** 按等经度/等纬度采样矩形边框,使在 2D 地图缩小时仍显示为直线(避免测地线弯弧);分段数提高以减轻锯齿 */
getRectangleBorderPositions(rect, segmentsPerEdge = 64) {
const west = rect.west
const south = rect.south
const east = rect.east
const north = rect.north
const positions = []
const n = Math.max(2, segmentsPerEdge)
for (let i = 0; i <= n; i++) {
const lon = west + (east - west) * (i / n)
positions.push(Cesium.Cartesian3.fromRadians(lon, south))
}
for (let i = 1; i <= n; i++) {
const lat = south + (north - south) * (i / n)
positions.push(Cesium.Cartesian3.fromRadians(east, lat))
}
for (let i = 1; i <= n; i++) {
const lon = east - (east - west) * (i / n)
positions.push(Cesium.Cartesian3.fromRadians(lon, north))
}
for (let i = 1; i < n; i++) {
const lat = north - (north - south) * (i / n)
positions.push(Cesium.Cartesian3.fromRadians(west, lat))
}
return positions
},
/** 在 2D 墨卡托下将一段线采样为“直线”(避免测地线弯弧);返回 Cartesian3 数组含起点到终点;分段数提高以减轻锯齿 */
sampleSegmentStraightIn2D(cartesianA, cartesianB, numSegments = 64) {
const cgA = Cesium.Cartographic.fromCartesian(cartesianA)
const cgB = Cesium.Cartographic.fromCartesian(cartesianB)
const lonA = cgA.longitude
const latA = cgA.latitude
const lonB = cgB.longitude
const latB = cgB.latitude
const yA = Math.log(Math.tan(Math.PI / 4 + latA / 2))
const yB = Math.log(Math.tan(Math.PI / 4 + latB / 2))
const n = Math.max(1, numSegments)
const positions = []
for (let i = 0; i <= n; i++) {
const t = i / n
const x = lonA + t * (lonB - lonA)
const y = yA + t * (yB - yA)
const lat = 2 * Math.atan(Math.exp(y)) - Math.PI / 2
positions.push(Cesium.Cartesian3.fromRadians(x, lat))
}
return positions
},
/** 将折线/多边形顶点按 2D 墨卡托“直线”采样,使缩放小时仍显示为直;closed 为 true 表示多边形闭合;分段数提高以减轻锯齿 */
getPolylinePositionsStraightIn2D(positions, closed = false, segmentsPerEdge = 64) {
if (!positions || positions.length < 2) return positions
const n = positions.length
const result = []
const segs = closed ? n : n - 1
for (let seg = 0; seg < segs; seg++) {
const i = seg
const j = closed ? (seg + 1) % n : seg + 1
const sampled = this.sampleSegmentStraightIn2D(positions[i], positions[j], segmentsPerEdge)
if (seg === 0) {
result.push(...sampled)
} else {
for (let k = 1; k < sampled.length; k++) result.push(sampled[k])
}
}
return result
},
getClickPosition(pixelPosition) {
const cartesian = this.viewer.camera.pickEllipsoid(pixelPosition, this.viewer.scene.globe.ellipsoid)
return cartesian
@ -4028,7 +4112,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
break
case 'line':
if (entity.polyline) {
entity.polyline.material = Cesium.Color.fromCssColorString(data.color)
entity.polyline.material = this.getPolylineSolidMaterial(data.color)
entity.polyline.width = data.width
}
break
@ -4037,7 +4121,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
entity.polygon.material = Cesium.Color.fromCssColorString(data.color).withAlpha(data.opacity)
}
if (entity.polyline) {
entity.polyline.material = Cesium.Color.fromCssColorString(data.borderColor || data.color)
entity.polyline.material = this.getPolylineSolidMaterial(data.borderColor || data.color)
entity.polyline.width = data.width
}
break
@ -4046,7 +4130,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
entity.rectangle.material = Cesium.Color.fromCssColorString(data.color).withAlpha(data.opacity)
}
if (entity.polyline) {
entity.polyline.material = Cesium.Color.fromCssColorString(data.borderColor || data.color)
entity.polyline.material = this.getPolylineSolidMaterial(data.borderColor || data.color)
entity.polyline.width = data.width
}
break
@ -4055,7 +4139,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
entity.ellipse.material = Cesium.Color.fromCssColorString(data.color).withAlpha(data.opacity)
}
if (entity.polyline) {
entity.polyline.material = Cesium.Color.fromCssColorString(data.borderColor || data.color)
entity.polyline.material = this.getPolylineSolidMaterial(data.borderColor || data.color)
entity.polyline.width = data.width
}
break
@ -4079,7 +4163,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
entity.ellipse.material = Cesium.Color.fromCssColorString(data.color).withAlpha(data.opacity)
}
if (entity.polyline) {
entity.polyline.material = Cesium.Color.fromCssColorString(data.borderColor || data.color)
entity.polyline.material = this.getPolylineSolidMaterial(data.borderColor || data.color)
entity.polyline.width = data.width
}
break
@ -4088,7 +4172,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
entity.polygon.material = Cesium.Color.fromCssColorString(data.color).withAlpha(data.opacity)
}
if (entity.polyline) {
entity.polyline.material = Cesium.Color.fromCssColorString(data.borderColor || data.color)
entity.polyline.material = this.getPolylineSolidMaterial(data.borderColor || data.color)
entity.polyline.width = data.width
}
break
@ -4121,6 +4205,21 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
this.selectedEntity = null
}
},
/** 右键航线:上锁/解锁,上锁后该航线不可编辑 */
toggleRouteLock() {
const ed = this.contextMenu.entityData;
if (!ed || ed.type !== 'route' || ed.routeId == null) {
this.contextMenu.visible = false;
return;
}
const routeId = ed.routeId;
const nextLocked = !this.routeLocked[routeId];
this.$set(this.routeLocked, routeId, nextLocked);
this.contextMenu.visible = false;
this.$message && this.$message.success(nextLocked ? '航线已上锁,无法修改' : '航线已解锁,可以编辑');
this.$emit('route-lock-changed', { routeId, locked: nextLocked });
},
/** 右键飞机:切换该航线飞机标牌的显示/隐藏 */
toggleRouteLabelVisibility() {
const ed = this.contextMenu.entityData
@ -4365,13 +4464,13 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
const names = {
point: '点',
line: '线',
polygon: '',
rectangle: '矩形',
circle: '圆形',
polygon: '多边形空域',
rectangle: '矩形空域',
circle: '圆形空域',
ellipse: '椭圆',
hold_circle: '圆形盘旋',
hold_ellipse: '椭圆盘旋',
sector: '扇形',
sector: '扇形空域',
arrow: '箭头',
text: '文本',
image: '图片'
@ -4473,12 +4572,13 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
break
case 'line':
const linePositions = entityData.data.points.map(p => Cesium.Cartesian3.fromDegrees(p.lng, p.lat))
const straightLinePositions = this.getPolylinePositionsStraightIn2D(linePositions, false)
entity = this.viewer.entities.add({
polyline: {
positions: linePositions,
positions: straightLinePositions,
width: 3,
material: Cesium.Color.fromCssColorString(color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(color),
clampToGround: true
},
label: {
text: entityData.label || '线',
@ -4502,7 +4602,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
outlineWidth: 2
},
label: {
text: entityData.label || '',
text: entityData.label || '多边形空域',
font: '14px sans-serif',
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
@ -4523,7 +4623,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
outlineWidth: 2
},
label: {
text: entityData.label || '矩形',
text: entityData.label || '矩形空域',
font: '14px sans-serif',
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
@ -4551,7 +4651,7 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
outlineWidth: 2
},
label: {
text: entityData.label || '圆形',
text: entityData.label || '圆形空域',
font: '14px sans-serif',
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
@ -4878,13 +4978,12 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
id: selectedLineEntity.id,
name: selectedLineEntity.label,
polyline: {
positions: newPositions,
positions: this.getPolylinePositionsStraightIn2D(newPositions, false),
width: selectedLineEntity.width,
material: Cesium.Color.fromCssColorString(selectedLineEntity.color),
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial(selectedLineEntity.color),
clampToGround: true
}
})
// 线
selectedLineEntity.entity = newEntity
selectedLineEntity.positions = newPositions
//
@ -4973,6 +5072,10 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
this.scaleBarCleanup()
this.scaleBarCleanup = null
}
if (typeof this._resolutionScaleCleanup === 'function') {
this._resolutionScaleCleanup()
this._resolutionScaleCleanup = null
}
if (this.viewer) {
this.viewer.destroy()
@ -5088,13 +5191,13 @@ this.viewer.scene.postProcessStages.fxaa.enabled = true
semiMinorAxis: radiusInMeters,
semiMajorAxis: radiusInMeters,
material: Cesium.Color.RED.withAlpha(0),
arcType: Cesium.ArcType.NONE
clampToGround: true
},
polyline: {
positions: circlePositions,
width: 2,
material: Cesium.Color.RED,
arcType: Cesium.ArcType.NONE
material: this.getPolylineSolidMaterial('#FF0000'),
clampToGround: true
}
});

Loading…
Cancel
Save