Browse Source

增加比例尺功能

wxp
ctw 2 months ago
parent
commit
086b1946e0
  1. 194
      ruoyi-ui/src/views/cesiumMap/index.vue

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

@ -35,9 +35,18 @@
@delete="deleteEntityFromContextMenu"
@update-property="updateEntityProperty"
/>
<!-- 鼠标经纬度显示 -->
<div class="coordinates-display">
{{ coordinatesText }}
<!-- 地图右下角比例尺 + 经纬度 -->
<div class="map-info-panel">
<div class="scale-bar">
<span class="scale-bar-text">{{ scaleBarText }}</span>
<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-right"></span>
</div>
</div>
<div class="coordinates-display">
{{ coordinatesText }}
</div>
</div>
</div>
</template>
@ -85,7 +94,6 @@ export default {
data() {
return {
viewer: null,
scaleBar: null,
//
drawingMode: null, // 'point', 'line', 'polygon', 'rectangle', 'circle'
drawingHandler: null,
@ -127,7 +135,10 @@ export default {
image: { width: 150, height: 150 }
},
//
coordinatesText: '经度: --, 纬度: --'
coordinatesText: '经度: --, 纬度: --',
//
scaleBarText: '--',
scaleBarWidthPx: 80
}
},
components: {
@ -2558,7 +2569,101 @@ export default {
})
},
initScaleBar() {
// ...
const that = this
const update = () => {
that.updateScaleBar()
}
update()
this.viewer.camera.changed.addEventListener(update)
this.viewer.camera.moveEnd.addEventListener(update)
this.scaleBarCleanup = () => {
that.viewer.camera.changed.removeEventListener(update)
that.viewer.camera.moveEnd.removeEventListener(update)
}
},
/** 根据当前视口计算比例尺(兼容 2D/WebMercator,多位置尝试 pick + 视口宽度备用) */
getScaleBarInfo() {
if (!this.viewer || !this.viewer.scene.canvas) return null
const canvas = this.viewer.scene.canvas
const w = canvas.clientWidth
const h = canvas.clientHeight
if (w <= 0 || h <= 0) return null
const ellipsoid = this.viewer.scene.globe.ellipsoid
const camera = this.viewer.camera
const barPx = 80
const centerX = w / 2
// 2D pickEllipsoid
const tryPoints = [
[centerX - barPx / 2, h - 50],
[centerX + barPx / 2, h - 50]
].concat([
[centerX - barPx / 2, h / 2],
[centerX + barPx / 2, h / 2]
])
let leftCartesian = null
let rightCartesian = null
leftCartesian = camera.pickEllipsoid(new Cesium.Cartesian2(tryPoints[0][0], tryPoints[0][1]), ellipsoid)
rightCartesian = camera.pickEllipsoid(new Cesium.Cartesian2(tryPoints[1][0], tryPoints[1][1]), ellipsoid)
if (!leftCartesian || !rightCartesian) {
leftCartesian = camera.pickEllipsoid(new Cesium.Cartesian2(tryPoints[2][0], tryPoints[2][1]), ellipsoid)
rightCartesian = camera.pickEllipsoid(new Cesium.Cartesian2(tryPoints[3][0], tryPoints[3][1]), ellipsoid)
}
if (leftCartesian && rightCartesian) {
const rawMeters = Cesium.Cartesian3.distance(leftCartesian, rightCartesian)
if (rawMeters > 0) {
const niceMeters = this.niceScaleValue(rawMeters)
const widthPx = Math.round((niceMeters / rawMeters) * barPx)
const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)}`
return { text, widthPx, niceMeters }
}
}
// 2D/WebMercator pick
const leftBottom = camera.pickEllipsoid(new Cesium.Cartesian2(0, h - 20), ellipsoid)
const rightBottom = camera.pickEllipsoid(new Cesium.Cartesian2(w, h - 20), ellipsoid)
if (leftBottom && rightBottom) {
const widthMeters = Cesium.Cartesian3.distance(leftBottom, rightBottom)
if (widthMeters > 0) {
const metersPerPx = widthMeters / w
const rawMeters = metersPerPx * barPx
const niceMeters = this.niceScaleValue(rawMeters)
const widthPx = Math.round(niceMeters / metersPerPx)
const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)}`
return { text, widthPx: Math.min(120, Math.max(40, widthPx)), niceMeters }
}
}
// 2D ->
if (this.viewer.scene.mode === Cesium.SceneMode.SCENE2D && camera.frustum && camera.frustum.right !== undefined) {
const frustumWidth = camera.frustum.right - camera.frustum.left
if (frustumWidth > 0) {
const metersPerPx = frustumWidth / w
const rawMeters = metersPerPx * barPx
const niceMeters = this.niceScaleValue(rawMeters)
const widthPx = Math.round(niceMeters / metersPerPx)
const text = niceMeters >= 1000 ? `${(niceMeters / 1000).toFixed(0)} 公里` : `${Math.round(niceMeters)}`
return { text, widthPx: Math.min(120, Math.max(40, widthPx)), niceMeters }
}
}
return null
},
/** 将实际距离圆整为易读的刻度值(米) */
niceScaleValue(meters) {
const candidates = [10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000, 500000]
let best = candidates[0]
for (let i = 0; i < candidates.length; i++) {
if (candidates[i] <= meters * 1.5) best = candidates[i]
}
return best
},
updateScaleBar() {
const info = this.getScaleBarInfo()
if (info) {
this.scaleBarText = info.text
this.scaleBarWidthPx = Math.min(120, Math.max(40, info.widthPx))
} else {
this.scaleBarText = '--'
this.scaleBarWidthPx = 80
}
this.$nextTick()
},
initPointMovement() {
//
@ -2657,10 +2762,6 @@ export default {
}, Cesium.ScreenSpaceEventType.LEFT_UP)
},
updateScaleBar() {
// ...
},
//
initMouseCoordinates() {
//
@ -2697,6 +2798,11 @@ export default {
this.mouseCoordinatesHandler = null
}
if (typeof this.scaleBarCleanup === 'function') {
this.scaleBarCleanup()
this.scaleBarCleanup = null
}
if (this.viewer) {
this.viewer.destroy()
this.viewer = null
@ -2737,20 +2843,72 @@ export default {
display: none !important;
}
/* 鼠标经纬度显示样式 */
.coordinates-display {
/* 地图右下角信息面板:比例尺在上、经纬度在下,整体略下移减少遮挡 */
.map-info-panel {
position: absolute;
bottom: 20px;
right: 20px;
bottom: 10px;
right: 12px;
display: flex;
flex-direction: column;
align-items: flex-end;
gap: 6px;
z-index: 1000;
pointer-events: none;
}
/* 比例尺:高德风格,浅色底、圆角、刻度线 */
.scale-bar {
background: rgba(255, 255, 255, 0.65);
color: #333;
padding: 4px 8px 6px;
border-radius: 4px;
box-shadow: 0 1px 3px rgba(0, 0, 0, 0.12);
display: flex;
flex-direction: column;
align-items: flex-start;
min-width: 60px;
}
.scale-bar-text {
font-size: 12px;
font-family: 'Microsoft YaHei', 'PingFang SC', sans-serif;
margin-bottom: 4px;
color: #555;
}
.scale-bar-line {
height: 2px;
background: #555;
position: relative;
flex-shrink: 0;
}
.scale-bar-tick {
position: absolute;
top: 0;
width: 2px;
height: 6px;
background: #555;
}
.scale-bar-tick-left {
left: 0;
top: -4px;
}
.scale-bar-tick-right {
right: 0;
left: auto;
top: -4px;
}
/* 经纬度显示(在比例尺下方) */
.map-info-panel .coordinates-display {
position: static;
bottom: auto;
right: auto;
background-color: rgba(0, 0, 0, 0.7);
color: white;
padding: 8px 12px;
padding: 6px 10px;
border-radius: 4px;
font-size: 14px;
font-size: 13px;
font-family: Arial, sans-serif;
box-shadow: 0 2px 4px rgba(0, 0, 0, 0.3);
z-index: 1000;
pointer-events: none;
min-width: 200px;
text-align: right;
}

Loading…
Cancel
Save