Browse Source

比例尺

lbj
sd 2 months ago
parent
commit
28d36ca0a1
  1. 190
      ruoyi-ui/src/views/cesiumMap/index.vue
  2. 18
      ruoyi-ui/src/views/childRoom/TopHeader.vue
  3. 43
      ruoyi-ui/src/views/childRoom/index.vue
  4. 41
      ruoyi-ui/src/views/dialogs/ScaleDialog.vue

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

@ -46,7 +46,7 @@
<!-- 地图右下角比例尺 + 经纬度 --> <!-- 地图右下角比例尺 + 经纬度 -->
<div class="map-info-panel"> <div class="map-info-panel">
<div class="scale-bar"> <div class="scale-bar" @click="handleScaleClick">
<span class="scale-bar-text">{{ scaleBarText }}</span> <span class="scale-bar-text">{{ scaleBarText }}</span>
<div class="scale-bar-line" :style="{ width: scaleBarWidthPx + 'px' }"> <div class="scale-bar-line" :style="{ width: scaleBarWidthPx + 'px' }">
<span class="scale-bar-tick scale-bar-tick-left"></span> <span class="scale-bar-tick scale-bar-tick-left"></span>
@ -89,6 +89,15 @@ export default {
type: String, type: String,
default: 'airspace' // 'airspace' or 'ranging' default: 'airspace' // 'airspace' or 'ranging'
}, },
scaleConfig: {
type: Object,
default: () => ({
scaleNumerator: 1,
scaleDenominator: 1000,
unit: 'km',
position: 'bottom-right'
})
}
}, },
watch: { watch: {
drawDomClick: { drawDomClick: {
@ -99,6 +108,14 @@ export default {
// this.initMap() // this.initMap()
} }
} }
},
scaleConfig: {
deep: true,
handler(newVal) {
if (newVal) {
this.updateScaleFromConfig(newVal)
}
}
} }
}, },
data() { data() {
@ -149,6 +166,10 @@ export default {
// //
scaleBarText: '--', scaleBarText: '--',
scaleBarWidthPx: 80, scaleBarWidthPx: 80,
useCustomScale: false,
customScaleText: '',
currentScaleUnit: 'm',
isApplyingScale: false,
// //
locateDialogVisible: false locateDialogVisible: false
} }
@ -174,6 +195,102 @@ export default {
this.destroyViewer() this.destroyViewer()
}, },
methods: { methods: {
updateScaleFromConfig(config) {
if (!config || !this.viewer) return
const { scaleNumerator, scaleDenominator, unit } = config
if (!scaleDenominator || scaleDenominator <= 0) return
let displayValue = ''
let metersPerPixel = 0
const standardDPI = 96
const pixelsPerCm = standardDPI * 0.393701
const scaleFactor = scaleDenominator / scaleNumerator
switch (unit) {
case 'm':
displayValue = `1:${scaleDenominator}`
metersPerPixel = scaleFactor / pixelsPerCm
break
case 'km':
displayValue = `1:${scaleDenominator}`
metersPerPixel = (scaleFactor * 1000) / pixelsPerCm
break
default:
displayValue = `1:${scaleDenominator}`
metersPerPixel = scaleFactor / pixelsPerCm
}
console.log('比例尺设置:', displayValue, '每像素米数:', metersPerPixel.toFixed(4))
this.isApplyingScale = true
this.useCustomScale = true
this.customScaleText = displayValue
this.currentScaleUnit = unit
this.applyScaleToCamera(metersPerPixel)
this.updateScaleBar()
this.$forceUpdate()
setTimeout(() => {
this.isApplyingScale = false
}, 1000)
},
applyScaleToCamera(metersPerPixel) {
if (!this.viewer || !this.viewer.camera) return
const canvas = this.viewer.scene.canvas
const width = canvas.clientWidth
const height = canvas.clientHeight
if (width <= 0 || height <= 0) return
const camera = this.viewer.camera
const scene = this.viewer.scene
if (scene.mode === Cesium.SceneMode.SCENE2D) {
const frustumWidth = width * metersPerPixel
camera.frustum.right = frustumWidth / 2
camera.frustum.left = -frustumWidth / 2
const frustumHeight = height * metersPerPixel
camera.frustum.top = frustumHeight / 2
camera.frustum.bottom = -frustumHeight / 2
} else {
const cameraPosition = camera.positionCartographic
const groundWidth = width * metersPerPixel
const fov = camera.frustum.fov || Cesium.Math.PI_OVER_THREE
const aspectRatio = width / height
const verticalFov = fov / aspectRatio
const targetHeight = (groundWidth / 2) / Math.tan(fov / 2)
const destination = Cesium.Cartesian3.fromRadians(
cameraPosition.longitude,
cameraPosition.latitude,
targetHeight
)
camera.flyTo({
destination: destination,
duration: 0.5,
orientation: {
heading: camera.heading,
pitch: camera.pitch,
roll: camera.roll
}
})
}
},
handleScaleClick() {
this.$emit('scale-click', {
useCustomScale: this.useCustomScale,
customScaleText: this.customScaleText
})
},
preventContextMenu(e) { preventContextMenu(e) {
e.preventDefault(); e.preventDefault();
}, },
@ -2766,6 +2883,10 @@ export default {
initScaleBar() { initScaleBar() {
const that = this const that = this
const update = () => { const update = () => {
if (!that.isApplyingScale) {
that.useCustomScale = false
that.customScaleText = ''
}
that.updateScaleBar() that.updateScaleBar()
} }
update() update()
@ -2806,10 +2927,12 @@ export default {
if (leftCartesian && rightCartesian) { if (leftCartesian && rightCartesian) {
const rawMeters = Cesium.Cartesian3.distance(leftCartesian, rightCartesian) const rawMeters = Cesium.Cartesian3.distance(leftCartesian, rightCartesian)
if (rawMeters > 0) { if (rawMeters > 0) {
const niceMeters = this.niceScaleValue(rawMeters) const metersPerPx = rawMeters / barPx
const widthPx = Math.round((niceMeters / rawMeters) * barPx) const scaleRatio = this.calculateScaleRatio(metersPerPx)
const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)}` const pixelsPerCm = this.getPixelsPerCm()
return { text, widthPx, niceMeters } const widthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
const text = `1:${scaleRatio}${this.currentScaleUnit}`
return { text, widthPx, niceMeters: rawMeters }
} }
} }
// 2D/WebMercator pick // 2D/WebMercator pick
@ -2819,11 +2942,11 @@ export default {
const widthMeters = Cesium.Cartesian3.distance(leftBottom, rightBottom) const widthMeters = Cesium.Cartesian3.distance(leftBottom, rightBottom)
if (widthMeters > 0) { if (widthMeters > 0) {
const metersPerPx = widthMeters / w const metersPerPx = widthMeters / w
const rawMeters = metersPerPx * barPx const scaleRatio = this.calculateScaleRatio(metersPerPx)
const niceMeters = this.niceScaleValue(rawMeters) const pixelsPerCm = this.getPixelsPerCm()
const widthPx = Math.round(niceMeters / metersPerPx) const widthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)}` const text = `1:${scaleRatio}${this.currentScaleUnit}`
return { text, widthPx: Math.min(120, Math.max(40, widthPx)), niceMeters } return { text, widthPx, niceMeters: widthMeters }
} }
} }
// 2D -> // 2D ->
@ -2831,15 +2954,34 @@ export default {
const frustumWidth = camera.frustum.right - camera.frustum.left const frustumWidth = camera.frustum.right - camera.frustum.left
if (frustumWidth > 0) { if (frustumWidth > 0) {
const metersPerPx = frustumWidth / w const metersPerPx = frustumWidth / w
const rawMeters = metersPerPx * barPx const scaleRatio = this.calculateScaleRatio(metersPerPx)
const niceMeters = this.niceScaleValue(rawMeters) const pixelsPerCm = this.getPixelsPerCm()
const widthPx = Math.round(niceMeters / metersPerPx) const widthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)}` const text = `1:${scaleRatio}${this.currentScaleUnit}`
return { text, widthPx: Math.min(120, Math.max(40, widthPx)), niceMeters } return { text, widthPx, niceMeters: frustumWidth }
} }
} }
return null return null
}, },
/** 计算比例尺比例 */
calculateScaleRatio(metersPerPx) {
const standardDPI = 96
const pixelsPerCm = standardDPI * 0.393701
const metersPerCm = metersPerPx * pixelsPerCm
let scaleRatio = 0
if (this.currentScaleUnit === 'km') {
scaleRatio = Math.round(metersPerCm / 1000)
} else {
scaleRatio = Math.round(metersPerCm)
}
return scaleRatio
},
/** 获取1厘米对应的像素数 */
getPixelsPerCm() {
const standardDPI = 96
const pixelsPerCm = standardDPI * 0.393701
return pixelsPerCm
},
/** 将实际距离圆整为易读的刻度值(米) */ /** 将实际距离圆整为易读的刻度值(米) */
niceScaleValue(meters) { niceScaleValue(meters) {
const candidates = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000] const candidates = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000]
@ -2850,13 +2992,19 @@ export default {
return best return best
}, },
updateScaleBar() { updateScaleBar() {
const info = this.getScaleBarInfo() if (this.useCustomScale && this.customScaleText) {
if (info) { this.scaleBarText = `${this.customScaleText}${this.currentScaleUnit}`
this.scaleBarText = info.text const pixelsPerCm = this.getPixelsPerCm()
this.scaleBarWidthPx = Math.min(120, Math.max(40, info.widthPx)) this.scaleBarWidthPx = Math.min(120, Math.max(40, Math.round(pixelsPerCm)))
} else { } else {
this.scaleBarText = '--' const info = this.getScaleBarInfo()
this.scaleBarWidthPx = 80 if (info) {
this.scaleBarText = info.text
this.scaleBarWidthPx = Math.min(120, Math.max(40, info.widthPx))
} else {
this.scaleBarText = '--'
this.scaleBarWidthPx = 80
}
} }
this.$nextTick() this.$nextTick()
}, },

18
ruoyi-ui/src/views/childRoom/TopHeader.vue

@ -319,6 +319,14 @@ export default {
isIconEditMode: { isIconEditMode: {
type: Boolean, type: Boolean,
default: false default: false
},
currentScaleConfig: {
type: Object,
default: () => ({
scaleNumerator: 1,
scaleDenominator: 1000,
unit: 'm'
})
} }
}, },
data() { data() {
@ -351,6 +359,16 @@ export default {
] ]
} }
}, },
watch: {
currentScaleConfig: {
deep: true,
handler(newVal) {
if (newVal) {
this.currentScale = { ...newVal }
}
}
}
},
methods: { methods: {
selectTopNav(item) { selectTopNav(item) {
this.$emit('select-nav', item) this.$emit('select-nav', item)

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

@ -6,8 +6,10 @@
<!-- cesiummap组件 --> <!-- cesiummap组件 -->
<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"
@draw-complete="handleMapDrawComplete" @draw-complete="handleMapDrawComplete"
@open-waypoint-dialog="handleOpenWaypointEdit" /> @open-waypoint-dialog="handleOpenWaypointEdit"
@scale-click="handleScaleClick" />
<div class="map-overlay-text"> <div class="map-overlay-text">
<i class="el-icon-location-outline text-3xl mb-2 block"></i> <i class="el-icon-location-outline text-3xl mb-2 block"></i>
<p>二维GIS地图区域</p> <p>二维GIS地图区域</p>
@ -44,6 +46,7 @@
:astro-time="astroTime" :astro-time="astroTime"
:user-avatar="userAvatar" :user-avatar="userAvatar"
:is-icon-edit-mode="isIconEditMode" :is-icon-edit-mode="isIconEditMode"
:current-scale-config="scaleConfig"
@select-nav="selectTopNav" @select-nav="selectTopNav"
@save-plan="savePlan" @save-plan="savePlan"
@import-plan-file="importPlanFile" @import-plan-file="importPlanFile"
@ -238,6 +241,7 @@
v-model="showScaleDialog" v-model="showScaleDialog"
:scale="currentScale" :scale="currentScale"
@save="saveScale" @save="saveScale"
@unit-change="handleScaleUnitChange"
/> />
<!-- 外部参数弹窗 --> <!-- 外部参数弹窗 -->
@ -335,7 +339,16 @@ export default {
showPowerZoneDialog: false, showPowerZoneDialog: false,
currentPowerZone: {}, currentPowerZone: {},
showScaleDialog: false, showScaleDialog: false,
currentScale: {}, currentScale: {
scaleNumerator: 1,
scaleDenominator: 1000,
unit: 'm'
},
scaleConfig: {
scaleNumerator: 1,
scaleDenominator: 1000,
unit: 'm'
},
showExternalParamsDialog: false, showExternalParamsDialog: false,
currentExternalParams: {}, currentExternalParams: {},
showPageLayoutDialog: false, showPageLayoutDialog: false,
@ -1111,10 +1124,36 @@ export default {
saveScale(scale) { saveScale(scale) {
console.log('保存比例尺:', scale) console.log('保存比例尺:', scale)
this.scaleConfig = {
scaleNumerator: scale.scaleNumerator,
scaleDenominator: scale.scaleDenominator,
unit: scale.unit
}
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.updateScaleFromConfig(this.scaleConfig)
}
const scaleText = `${scale.scaleNumerator}:${scale.scaleDenominator}` const scaleText = `${scale.scaleNumerator}:${scale.scaleDenominator}`
this.$message.success(`比例尺 "${scaleText}" 保存成功`); this.$message.success(`比例尺 "${scaleText}" 保存成功`);
}, },
handleScaleClick(scaleInfo) {
this.currentScale = {
scaleNumerator: this.scaleConfig.scaleNumerator,
scaleDenominator: this.scaleConfig.scaleDenominator,
unit: this.scaleConfig.unit
}
this.showScaleDialog = true
},
handleScaleUnitChange(unit) {
this.scaleConfig.unit = unit
if (this.$refs.cesiumMap) {
this.$refs.cesiumMap.currentScaleUnit = unit
}
},
// //
loadTerrain() { loadTerrain() {
this.$message.success('加载/切换地形'); this.$message.success('加载/切换地形');

41
ruoyi-ui/src/views/dialogs/ScaleDialog.vue

@ -30,17 +30,6 @@
<el-select v-model="formData.unit" placeholder="请选择单位" style="width: 100%;"> <el-select v-model="formData.unit" placeholder="请选择单位" style="width: 100%;">
<el-option label="米" value="m"></el-option> <el-option label="米" value="m"></el-option>
<el-option label="千米" value="km"></el-option> <el-option label="千米" value="km"></el-option>
<el-option label="英尺" value="ft"></el-option>
<el-option label="英里" value="mi"></el-option>
</el-select>
</el-form-item>
<el-form-item label="显示位置" prop="position">
<el-select v-model="formData.position" placeholder="请选择显示位置" style="width: 100%;">
<el-option label="左下角" value="bottom-left"></el-option>
<el-option label="右下角" value="bottom-right"></el-option>
<el-option label="左上角" value="top-left"></el-option>
<el-option label="右上角" value="top-right"></el-option>
</el-select> </el-select>
</el-form-item> </el-form-item>
</el-form> </el-form>
@ -72,8 +61,7 @@ export default {
formData: { formData: {
scaleNumerator: 1, scaleNumerator: 1,
scaleDenominator: 1000, scaleDenominator: 1000,
unit: 'km', unit: 'm'
position: 'bottom-right'
}, },
rules: { rules: {
scaleNumerator: [ scaleNumerator: [
@ -84,9 +72,6 @@ export default {
], ],
unit: [ unit: [
{ required: true, message: '请选择单位', trigger: 'change' } { required: true, message: '请选择单位', trigger: 'change' }
],
position: [
{ required: true, message: '请选择显示位置', trigger: 'change' }
] ]
} }
}; };
@ -101,6 +86,11 @@ export default {
if (this.value && newVal) { if (this.value && newVal) {
this.initFormData(); this.initFormData();
} }
},
'formData.unit'(newVal) {
if (this.value) {
this.$emit('unit-change', newVal);
}
} }
}, },
methods: { methods: {
@ -108,8 +98,7 @@ export default {
this.formData = { this.formData = {
scaleNumerator: this.scale.scaleNumerator || 1, scaleNumerator: this.scale.scaleNumerator || 1,
scaleDenominator: this.scale.scaleDenominator || 1000, scaleDenominator: this.scale.scaleDenominator || 1000,
unit: this.scale.unit || 'km', unit: this.scale.unit || 'm'
position: this.scale.position || 'bottom-right'
}; };
}, },
closeDialog() { closeDialog() {
@ -151,7 +140,7 @@ export default {
border-radius: 8px; border-radius: 8px;
box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15); box-shadow: 0 4px 20px rgba(0, 0, 0, 0.15);
width: 90%; width: 90%;
max-width: 500px; max-width: 400px;
max-height: 90vh; max-height: 90vh;
overflow-y: auto; overflow-y: auto;
animation: dialog-fade-in 0.3s ease; animation: dialog-fade-in 0.3s ease;
@ -173,7 +162,7 @@ export default {
display: flex; display: flex;
align-items: center; align-items: center;
justify-content: space-between; justify-content: space-between;
padding: 16px 20px; padding: 12px 16px;
border-bottom: 1px solid #e8e8e8; border-bottom: 1px solid #e8e8e8;
} }
@ -196,7 +185,17 @@ export default {
} }
.dialog-body { .dialog-body {
padding: 20px; padding: 16px;
}
.quick-scale-buttons {
display: flex;
flex-wrap: wrap;
gap: 8px;
}
.quick-scale-buttons .el-button {
margin: 0;
} }
.scale-inputs { .scale-inputs {

Loading…
Cancel
Save