Browse Source

Merge branch 'lbj' of http://124.70.32.114:3100/woka/cesium-map-object into ctw

# Conflicts:
#	ruoyi-ui/src/views/cesiumMap/ContextMenu.vue
#	ruoyi-ui/src/views/cesiumMap/index.vue
master
cuitw 2 months ago
parent
commit
c65594e84b
  1. 12
      ruoyi-ui/src/views/cesiumMap/ContextMenu.vue
  2. 95
      ruoyi-ui/src/views/cesiumMap/LocateDialog.vue
  3. 187
      ruoyi-ui/src/views/cesiumMap/index.vue
  4. 8
      ruoyi-ui/src/views/childRoom/index.vue
  5. 13
      ruoyi-ui/src/views/dialogs/RadiusDialog.vue

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

@ -126,6 +126,11 @@
<!-- 多边形特有选项 --> <!-- 多边形特有选项 -->
<div class="menu-section" v-if="entityData.type === 'polygon' || entityData.type === 'rectangle' || entityData.type === 'circle' || entityData.type === 'sector' || entityData.type === 'powerZone'"> <div class="menu-section" v-if="entityData.type === 'polygon' || entityData.type === 'rectangle' || entityData.type === 'circle' || entityData.type === 'sector' || entityData.type === 'powerZone'">
<div class="menu-title">填充属性</div> <div class="menu-title">填充属性</div>
<div class="menu-item" @click="editName" v-if="entityData.type === 'powerZone'">
<span class="menu-icon">📝</span>
<span>名称</span>
<span class="menu-value">{{ entityData.name || '' }}</span>
</div>
<div class="menu-item" @click="toggleColorPicker('color')"> <div class="menu-item" @click="toggleColorPicker('color')">
<span class="menu-icon">🎨</span> <span class="menu-icon">🎨</span>
<span>填充色</span> <span>填充色</span>
@ -384,6 +389,13 @@ export default {
this.$emit('toggle-route-label') this.$emit('toggle-route-label')
}, },
editName() {
const newName = prompt('请输入威力区名称:', this.entityData.name || '')
if (newName !== null && newName.trim() !== '') {
this.$emit('update-property', 'name', newName.trim())
}
},
handleToggleRouteLock() { handleToggleRouteLock() {
this.$emit('toggle-route-lock') this.$emit('toggle-route-lock')
}, },

95
ruoyi-ui/src/views/cesiumMap/LocateDialog.vue

@ -61,7 +61,7 @@
<el-option <el-option
v-for="item in waypointList" v-for="item in waypointList"
:key="item.id" :key="item.id"
:label="`${item.name} (${degreesToDMS(item.lng)}, ${degreesToDMS(item.lat)})`" :label="`${item.name} (${formatCoordinate(item.lng)}, ${formatCoordinate(item.lat)})`"
:value="item.id" :value="item.id"
/> />
</el-select> </el-select>
@ -70,7 +70,7 @@
<el-form-item label="经度:"> <el-form-item label="经度:">
<el-input <el-input
v-model="formData.lng" v-model="formData.lng"
placeholder="例如 116°23'48.64"" :placeholder="coordinateFormat === 'dms' ? '例如 116°23\'48.64&quot;' : '例如 116.396844'"
clearable clearable
/> />
</el-form-item> </el-form-item>
@ -78,7 +78,7 @@
<el-form-item label="纬度:"> <el-form-item label="纬度:">
<el-input <el-input
v-model="formData.lat" v-model="formData.lat"
placeholder="例如 39°54'33.48"" :placeholder="coordinateFormat === 'dms' ? '例如 39°54\'33.48&quot;' : '例如 39.909289'"
clearable clearable
/> />
</el-form-item> </el-form-item>
@ -107,6 +107,10 @@ export default {
visible: { visible: {
type: Boolean, type: Boolean,
default: false default: false
},
coordinateFormat: {
type: String,
default: 'dms' // 'decimal' 'dms'
} }
}, },
data() { data() {
@ -137,9 +141,44 @@ export default {
this.loadScenarios() this.loadScenarios()
} }
this.$emit('update:visible', newVal) this.$emit('update:visible', newVal)
},
coordinateFormat: {
handler(newVal) {
this.updateCoordinateFormat(newVal)
}
} }
}, },
methods: { methods: {
updateCoordinateFormat(newFormat) {
if (!this.formData.lng || !this.formData.lat) return
let lngDegrees, latDegrees
if (this.coordinateFormat === 'dms') {
lngDegrees = this.dmsToDegrees(this.formData.lng)
latDegrees = this.dmsToDegrees(this.formData.lat)
} else {
lngDegrees = parseFloat(this.formData.lng)
latDegrees = parseFloat(this.formData.lat)
}
if (lngDegrees !== null && latDegrees !== null && !isNaN(lngDegrees) && !isNaN(latDegrees)) {
if (newFormat === 'dms') {
this.formData.lng = this.degreesToDMS(lngDegrees)
this.formData.lat = this.degreesToDMS(latDegrees)
} else {
this.formData.lng = lngDegrees.toFixed(6)
this.formData.lat = latDegrees.toFixed(6)
}
}
},
formatCoordinate(value) {
if (this.coordinateFormat === 'dms') {
return this.degreesToDMS(value)
} else {
return value.toFixed(6)
}
},
degreesToDMS(decimalDegrees) { degreesToDMS(decimalDegrees) {
const degrees = Math.floor(decimalDegrees) const degrees = Math.floor(decimalDegrees)
const minutesDecimal = (decimalDegrees - degrees) * 60 const minutesDecimal = (decimalDegrees - degrees) * 60
@ -157,12 +196,22 @@ export default {
return sign * (Math.abs(degrees) + minutes / 60 + seconds / 3600) return sign * (Math.abs(degrees) + minutes / 60 + seconds / 3600)
}, },
resetForm() { resetForm() {
this.formData = { if (this.coordinateFormat === 'dms') {
scenarioId: null, this.formData = {
routeId: null, scenarioId: null,
waypointId: null, routeId: null,
lng: '116°23\'48.64"', waypointId: null,
lat: '39°54\'33.48"' lng: '116°23\'48.64"',
lat: '39°54\'33.48"'
}
} else {
this.formData = {
scenarioId: null,
routeId: null,
waypointId: null,
lng: '116.396844',
lat: '39.909289'
}
} }
this.routeList = [] this.routeList = []
this.waypointList = [] this.waypointList = []
@ -214,8 +263,8 @@ export default {
if (value) { if (value) {
const waypoint = this.waypointList.find(w => w.id === value) const waypoint = this.waypointList.find(w => w.id === value)
if (waypoint) { if (waypoint) {
this.formData.lng = this.degreesToDMS(waypoint.lng) this.formData.lng = this.formatCoordinate(waypoint.lng)
this.formData.lat = this.degreesToDMS(waypoint.lat) this.formData.lat = this.formatCoordinate(waypoint.lat)
} }
} }
}, },
@ -233,12 +282,24 @@ export default {
return return
} }
const lngDegrees = this.dmsToDegrees(lng) let lngDegrees, latDegrees
const latDegrees = this.dmsToDegrees(lat)
if (lngDegrees === null || latDegrees === null || isNaN(lngDegrees) || isNaN(latDegrees)) { if (this.coordinateFormat === 'dms') {
this.$message.error('请输入有效的度分秒格式!格式:116°23\'48.64"') lngDegrees = this.dmsToDegrees(lng)
return latDegrees = this.dmsToDegrees(lat)
if (lngDegrees === null || latDegrees === null || isNaN(lngDegrees) || isNaN(latDegrees)) {
this.$message.error('请输入有效的度分秒格式!格式:116°23\'48.64"')
return
}
} else {
lngDegrees = parseFloat(lng)
latDegrees = parseFloat(lat)
if (isNaN(lngDegrees) || isNaN(latDegrees)) {
this.$message.error('请输入有效的十进制格式!')
return
}
} }
if (lngDegrees < -180 || lngDegrees > 180 || latDegrees < -90 || latDegrees > 90) { if (lngDegrees < -180 || lngDegrees > 180 || latDegrees < -90 || latDegrees > 90) {

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

@ -45,6 +45,7 @@
<!-- 定位弹窗 --> <!-- 定位弹窗 -->
<locate-dialog <locate-dialog
:visible="locateDialogVisible" :visible="locateDialogVisible"
:coordinateFormat="coordinateFormat"
@update:visible="locateDialogVisible = $event" @update:visible="locateDialogVisible = $event"
@confirm="handleLocateConfirm" @confirm="handleLocateConfirm"
@cancel="handleLocateCancel" @cancel="handleLocateCancel"
@ -116,6 +117,10 @@ export default {
unit: 'km', unit: 'km',
position: 'bottom-right' position: 'bottom-right'
}) })
},
coordinateFormat: {
type: String,
default: 'dms' // 'decimal' 'dms'
} }
}, },
watch: { watch: {
@ -135,6 +140,11 @@ export default {
this.updateScaleFromConfig(newVal) this.updateScaleFromConfig(newVal)
} }
} }
},
coordinateFormat: {
handler(newVal) {
this.updateCoordinatesDisplay()
}
} }
}, },
data() { data() {
@ -189,6 +199,7 @@ export default {
}, },
// //
coordinatesText: '经度: --, 纬度: --', coordinatesText: '经度: --, 纬度: --',
currentCoordinates: null,
// //
scaleBarText: '--', scaleBarText: '--',
scaleBarWidthPx: 80, scaleBarWidthPx: 80,
@ -1689,10 +1700,12 @@ export default {
// canvas readPixels false // canvas readPixels false
contextOptions: { contextOptions: {
preserveDrawingBuffer: true, preserveDrawingBuffer: true,
antialias: true // WebGL 齿 webgl: {
}, antialias: true,
// 齿WebGL2 8 线 msaa: true,
msaaSamples: 8 msaaSamples: 4
}
}
}) })
this.viewer.cesiumWidget.creditContainer.style.display = "none" this.viewer.cesiumWidget.creditContainer.style.display = "none"
// 80% 线齿 // 80% 线齿
@ -1733,6 +1746,7 @@ export default {
console.log('Cesium离线二维地图已加载') console.log('Cesium离线二维地图已加载')
// 1. // 1.
this.viewer.scene.postProcessStages.fxaa.enabled = true
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas); this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((click) => { this.handler.setInputAction((click) => {
// //
@ -2279,10 +2293,18 @@ export default {
addCoordinateLabels() { addCoordinateLabels() {
for (let lon = -180; lon <= 180; lon += 30) { for (let lon = -180; lon <= 180; lon += 30) {
for (let lat = -90; lat <= 90; lat += 30) { for (let lat = -90; lat <= 90; lat += 30) {
let latText, lonText
if (this.coordinateFormat === 'dms') {
latText = `${this.degreesToDMS(lat)}N`
lonText = `${this.degreesToDMS(lon)}E`
} else {
latText = `${lat.toFixed(2)}°N`
lonText = `${lon.toFixed(2)}°E`
}
this.viewer.entities.add({ this.viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(lon, lat), position: Cesium.Cartesian3.fromDegrees(lon, lat),
label: { label: {
text: `${this.degreesToDMS(lat)}N\n${this.degreesToDMS(lon)}E`, text: `${latText}\n${lonText}`,
font: '12px sans-serif', font: '12px sans-serif',
fillColor: Cesium.Color.BLACK, fillColor: Cesium.Color.BLACK,
outlineColor: Cesium.Color.WHITE, outlineColor: Cesium.Color.WHITE,
@ -2540,7 +2562,10 @@ export default {
return this.drawingPoints.length > 0 ? [this.drawingPoints[this.drawingPoints.length - 1]] : []; return this.drawingPoints.length > 0 ? [this.drawingPoints[this.drawingPoints.length - 1]] : [];
}, false), }, false),
width: this.defaultStyles.line.width, width: this.defaultStyles.line.width,
material: this.getPolylineSolidMaterial(this.defaultStyles.line.color), material: new Cesium.PolylineDashMaterialProperty({
color: Cesium.Color.fromCssColorString(this.defaultStyles.line.color),
dashLength: 16
}),
clampToGround: true clampToGround: true
} }
}); });
@ -3792,113 +3817,6 @@ export default {
return entity 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.0 80% 2.5齿
let resolutionScale = 2.0
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 = 48) {
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 = 32) {
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 = 32) {
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) { getClickPosition(pixelPosition) {
const cartesian = this.viewer.camera.pickEllipsoid(pixelPosition, this.viewer.scene.globe.ellipsoid) const cartesian = this.viewer.camera.pickEllipsoid(pixelPosition, this.viewer.scene.globe.ellipsoid)
return cartesian return cartesian
@ -4160,6 +4078,9 @@ export default {
entity.polyline.material = this.getPolylineSolidMaterial(data.borderColor || data.color) entity.polyline.material = this.getPolylineSolidMaterial(data.borderColor || data.color)
entity.polyline.width = data.width entity.polyline.width = data.width
} }
if (data.name && data.centerEntity && data.centerEntity.label) {
data.centerEntity.label.text = data.name
}
break break
case 'sector': case 'sector':
if (entity.polygon) { if (entity.polygon) {
@ -4747,7 +4668,13 @@ export default {
}, },
duration: 2 duration: 2
}) })
this.$message.success(`已定位到经度 ${this.degreesToDMS(lng)},纬度 ${this.degreesToDMS(lat)}`) let coordinateText
if (this.coordinateFormat === 'dms') {
coordinateText = `${this.degreesToDMS(lng)}${this.degreesToDMS(lat)}`
} else {
coordinateText = `${lng.toFixed(6)}${lat.toFixed(6)}`
}
this.$message.success(`已定位到经度 ${coordinateText}`)
} }
}, },
@ -5018,12 +4945,26 @@ export default {
const cartographic = Cesium.Cartographic.fromCartesian(cartesian) const cartographic = Cesium.Cartographic.fromCartesian(cartesian)
const longitude = Cesium.Math.toDegrees(cartographic.longitude) const longitude = Cesium.Math.toDegrees(cartographic.longitude)
const latitude = Cesium.Math.toDegrees(cartographic.latitude) const latitude = Cesium.Math.toDegrees(cartographic.latitude)
this.coordinatesText = `经度: ${this.degreesToDMS(longitude)}, 纬度: ${this.degreesToDMS(latitude)}` this.currentCoordinates = { longitude, latitude }
this.updateCoordinatesDisplay()
} else { } else {
this.coordinatesText = '经度: --, 纬度: --' this.coordinatesText = '经度: --, 纬度: --'
} }
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE) }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
}, },
//
updateCoordinatesDisplay() {
if (!this.currentCoordinates) {
this.coordinatesText = '经度: --, 纬度: --'
return
}
const { longitude, latitude } = this.currentCoordinates
if (this.coordinateFormat === 'dms') {
this.coordinatesText = `经度: ${this.degreesToDMS(longitude)}, 纬度: ${this.degreesToDMS(latitude)}`
} else {
this.coordinatesText = `经度: ${longitude.toFixed(6)}, 纬度: ${latitude.toFixed(6)}`
}
},
destroyViewer() { destroyViewer() {
this.stopDrawing() this.stopDrawing()
this.clearAll(false) this.clearAll(false)
@ -5162,6 +5103,17 @@ export default {
outlineColor: Cesium.Color.WHITE, outlineColor: Cesium.Color.WHITE,
outlineWidth: 2, outlineWidth: 2,
disableDepthTestDistance: Number.POSITIVE_INFINITY disableDepthTestDistance: Number.POSITIVE_INFINITY
},
label: {
text: '',
font: '14px Microsoft YaHei',
fillColor: Cesium.Color.WHITE,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2,
style: Cesium.LabelStyle.FILL_AND_OUTLINE,
verticalOrigin: Cesium.VerticalOrigin.TOP,
pixelOffset: new Cesium.Cartesian2(0, -20),
disableDepthTestDistance: Number.POSITIVE_INFINITY
} }
}); });
@ -5202,12 +5154,17 @@ export default {
centerEntity: this.powerZoneCenterEntity, centerEntity: this.powerZoneCenterEntity,
center: this.powerZoneCenter, center: this.powerZoneCenter,
radius: radiusInMeters, radius: radiusInMeters,
name: radiusData.name,
color: '#FF0000', color: '#FF0000',
opacity: 0, opacity: 0,
borderColor: '#FF0000', borderColor: '#FF0000',
width: 2 width: 2
}); });
if (this.powerZoneCenterEntity && this.powerZoneCenterEntity.label) {
this.powerZoneCenterEntity.label.text = radiusData.name;
}
this.isDrawing = false; this.isDrawing = false;
this.viewer.canvas.style.cursor = 'default'; this.viewer.canvas.style.cursor = 'default';

8
ruoyi-ui/src/views/childRoom/index.vue

@ -12,6 +12,7 @@
<cesiumMap ref="cesiumMap" :drawDomClick="drawDom || airspaceDrawDom" <cesiumMap ref="cesiumMap" :drawDomClick="drawDom || airspaceDrawDom"
:tool-mode="drawDom ? 'ranging' : (airspaceDrawDom ? 'airspace' : 'airspace')" :tool-mode="drawDom ? 'ranging' : (airspaceDrawDom ? 'airspace' : 'airspace')"
:scaleConfig="scaleConfig" :scaleConfig="scaleConfig"
:coordinateFormat="coordinateFormat"
@draw-complete="handleMapDrawComplete" @draw-complete="handleMapDrawComplete"
@drawing-points-update="missionDrawingPointsCount = $event" @drawing-points-update="missionDrawingPointsCount = $event"
@open-waypoint-dialog="handleOpenWaypointEdit" @open-waypoint-dialog="handleOpenWaypointEdit"
@ -563,6 +564,9 @@ export default {
showLandmark: true, showLandmark: true,
showRoute: true, showRoute: true,
//
coordinateFormat: 'dms', // 'decimal' 'dms'
menuItems: [], menuItems: [],
// //
@ -1794,8 +1798,8 @@ export default {
}, },
coordinateConversion() { coordinateConversion() {
this.$message.success('坐标换算'); this.coordinateFormat = this.coordinateFormat === 'decimal' ? 'dms' : 'decimal'
// this.$message.success(`坐标格式已切换为:${this.coordinateFormat === 'decimal' ? '十进制' : '度分秒'}`)
}, },
// //

13
ruoyi-ui/src/views/dialogs/RadiusDialog.vue

@ -8,6 +8,14 @@
<div class="dialog-body"> <div class="dialog-body">
<el-form :model="formData" :rules="rules" ref="formRef" label-width="80px" size="small"> <el-form :model="formData" :rules="rules" ref="formRef" label-width="80px" size="small">
<el-form-item label="名称" prop="name">
<el-input
v-model="formData.name"
placeholder="请输入威力区名称"
style="width: 100%;"
></el-input>
</el-form-item>
<el-form-item label="半径" prop="radius"> <el-form-item label="半径" prop="radius">
<el-input-number <el-input-number
v-model="formData.radius" v-model="formData.radius"
@ -47,10 +55,14 @@ export default {
data() { data() {
return { return {
formData: { formData: {
name: '',
radius: 50, radius: 50,
unit: 'km' unit: 'km'
}, },
rules: { rules: {
name: [
{ required: true, message: '请输入威力区名称', trigger: 'blur' }
],
radius: [ radius: [
{ required: true, message: '请输入半径', trigger: 'blur' } { required: true, message: '请输入半径', trigger: 'blur' }
] ]
@ -65,6 +77,7 @@ export default {
this.$refs.formRef.validate((valid) => { this.$refs.formRef.validate((valid) => {
if (valid) { if (valid) {
this.$emit('confirm', { this.$emit('confirm', {
name: this.formData.name,
radius: this.formData.radius, radius: this.formData.radius,
unit: this.formData.unit unit: this.formData.unit
}) })

Loading…
Cancel
Save