From 086b1946e0c19dfa4d384ad50dc7a85a0d0bc907 Mon Sep 17 00:00:00 2001 From: ctw <1051735452@qq.com> Date: Mon, 2 Feb 2026 11:04:57 +0800 Subject: [PATCH] =?UTF-8?q?=E5=A2=9E=E5=8A=A0=E6=AF=94=E4=BE=8B=E5=B0=BA?= =?UTF-8?q?=E5=8A=9F=E8=83=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- ruoyi-ui/src/views/cesiumMap/index.vue | 194 ++++++++++++++++++++++++++++++--- 1 file changed, 176 insertions(+), 18 deletions(-) diff --git a/ruoyi-ui/src/views/cesiumMap/index.vue b/ruoyi-ui/src/views/cesiumMap/index.vue index 3ffe8d4..aa41e97 100644 --- a/ruoyi-ui/src/views/cesiumMap/index.vue +++ b/ruoyi-ui/src/views/cesiumMap/index.vue @@ -35,9 +35,18 @@ @delete="deleteEntityFromContextMenu" @update-property="updateEntityProperty" /> - -
- {{ coordinatesText }} + +
+
+ {{ scaleBarText }} +
+ + +
+
+
+ {{ coordinatesText }} +
@@ -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; }