Cesium 实现比例尺功能
摘要 本文介绍了在Vue 3.5.13和Cesium 1.127.0环境下实现地图比例尺功能的方法。通过计算屏幕像素与真实距离的对应关系,动态调整比例尺的显示长度和单位(米或千米)。组件采用响应式设计,在每帧渲染时更新比例尺,确保在不同缩放级别下都能显示合适的比例。实现过程包括获取地图中心点距离、动态计算最优比例长度、设置比例尺文本和样式等核心功能。组件通过TailwindCSS设置基础样式,并添
·
环境
- Vue:v3.5.13
- Cesium:v1.127.0
需求描述
项目开发中需要实现地图比例尺功能,所以使用 Cesium 相关的 API 组合开发来实现,具体效果如下:

解决方法
下面直接贴代码:
<template>
<div
class="absolute left-4 bottom-4 pt-1 pb-2 pl-2 pr-2 bg-(--container-bg-3) text-white text-xs"
>
<div ref="scaleBarRef" class="scale-bar-view relative">{{ scaleBarText }}</div>
</div>
</template>
<script setup lang="ts">
import { ref, toRaw, onMounted } from 'vue'
import * as Cesium from 'cesium'
import { useMapStore } from '@/stores/counter'
const mapStore = useMapStore()
const scaleBarRef = ref<HTMLElement | null>(null)
const scaleBarText = ref('0 km')
function updateScaleBar() {
if (!mapStore.map) {
console.error('mapStore.map 未初始化')
return
}
const viewer = toRaw(mapStore.map)
// 限制视图缩放高度
const minHeight = 10 // 最小高度
//const maxHeight = 10000000 // 最大高度
const height = viewer.camera.positionCartographic.height
if (height < minHeight) {
viewer.camera.moveBackward(minHeight - height)
}
// else if(height > maxHeight) {
// viewer.camera.moveForward(height - maxHeight)
// }
const scene = viewer.scene
const camera = scene.camera
const canvas = scene.canvas
// 屏幕中心点
const center = new Cesium.Cartesian2(canvas.clientWidth / 2, canvas.clientHeight / 2)
// 向右偏移 100px 的点
const right = new Cesium.Cartesian2(center.x + 100, center.y)
const pickCenter = camera.pickEllipsoid(center)
const pickRight = camera.pickEllipsoid(right)
if (!pickCenter || !pickRight) return
const geoCenter = Cesium.Ellipsoid.WGS84.cartographicToCartesian(
Cesium.Cartographic.fromCartesian(pickCenter),
)
const geoRight = Cesium.Ellipsoid.WGS84.cartographicToCartesian(
Cesium.Cartographic.fromCartesian(pickRight),
)
const distance = Cesium.Cartesian3.distance(geoCenter, geoRight)
// 将 100px 转换为真实距离
const meterPerPixel = distance / 100
// 动态计算适合的比例尺长度
const scaleOptions = [
1, 2, 5, 10, 20, 50, 100, 200, 500, 1000, 2000, 5000, 10000, 20000, 50000, 100000, 200000,
500000, 1000000, 2000000, 5000000,
]
// 设置最小和最大宽度
const minWidthPx = 60
const maxWidthPx = 150
// 选择在 minWidthPx ~ maxWidthPx 范围内的最佳比例
let finalDisplayDistance = scaleOptions[0]
let finalWidthPx = finalDisplayDistance / meterPerPixel
for (const opt of scaleOptions) {
const width = opt / meterPerPixel
if (width >= minWidthPx && width <= maxWidthPx) {
finalDisplayDistance = opt
finalWidthPx = width
}
}
// 如果都不在范围内,就取最接近 max/min 的一个
if (finalWidthPx < minWidthPx) {
finalDisplayDistance =
scaleOptions.find((opt) => opt / meterPerPixel > minWidthPx) || finalDisplayDistance
finalWidthPx = finalDisplayDistance / meterPerPixel
}
if (finalWidthPx > maxWidthPx) {
finalDisplayDistance =
scaleOptions.reverse().find((opt) => opt / meterPerPixel < maxWidthPx) || finalDisplayDistance
finalWidthPx = finalDisplayDistance / meterPerPixel
}
// 设置显示文本
scaleBarText.value =
finalDisplayDistance >= 1000 ? `${finalDisplayDistance / 1000} km` : `${finalDisplayDistance} m`
if (scaleBarRef.value) {
scaleBarRef.value.style.width = `${finalWidthPx}px`
scaleBarRef.value.style.borderBottom = '2px solid #fff'
scaleBarRef.value.style.textAlign = 'center'
}
}
function initScaleBar() {
if (!mapStore.map) {
console.error('mapStore.map 未初始化')
return
}
const viewer = toRaw(mapStore.map)
// 每帧更新
viewer.scene.postRender.addEventListener(updateScaleBar)
}
onMounted(() => {
setTimeout(() => {
initScaleBar()
}, 300)
})
</script>
<style scoped lang="scss">
.scale-bar-view {
&::before {
content: ' ';
position: absolute;
left: -2px;
bottom: -2px;
height: 7px;
width: 2px;
background-color: #fff;
}
&::after {
content: ' ';
position: absolute;
right: -2px;
bottom: -2px;
height: 7px;
width: 2px;
background-color: #fff;
}
}
</style>
以上代码是一个使用 Cesium 实现的地图比例尺的 Vue 组件,其中样式是通过 tailwindcss 来设置的,viewer 对象是存储到全局状态中的,其余就没什么特殊的了。
更多推荐


所有评论(0)