使用Cesium添加全面的3D地图功能,包括: - 地图视口和控件组件 - 图层管理,含底图切换器和目录控制 - 相机、实体和查询服务 - 罗盘和场景模式切换UI组件 - 支持工具、存储和数据配置 更新构建配置以支持Cesium集成和SVG图标。
111 lines
3.2 KiB
JavaScript
111 lines
3.2 KiB
JavaScript
/**
|
||
* 地图点击位置拾取工具
|
||
* 解决标记位置偏移问题
|
||
*/
|
||
import * as Cesium from 'cesium'
|
||
|
||
/**
|
||
* 更准确的地图位置拾取方法
|
||
* @param {Cesium.Viewer} viewer - Cesium viewer实例
|
||
* @param {Cesium.Cartesian2} clickPosition - 屏幕点击位置
|
||
* @returns {Object|null} 返回 {cartesian3: Cesium.Cartesian3, cartographic: Object} 或 null
|
||
*/
|
||
export function pickMapPosition(viewer, clickPosition) {
|
||
if (!viewer || !clickPosition) {
|
||
return null
|
||
}
|
||
|
||
let pickedPosition = null
|
||
|
||
try {
|
||
// 方法1: 尝试使用场景拾取(最准确,考虑地形)
|
||
const ray = viewer.camera.getPickRay(clickPosition)
|
||
if (ray) {
|
||
// 先尝试拾取地形表面
|
||
pickedPosition = viewer.scene.globe.pick(ray, viewer.scene)
|
||
|
||
if (!pickedPosition) {
|
||
// 如果地形拾取失败,使用椭球面拾取作为fallback
|
||
pickedPosition = viewer.camera.pickEllipsoid(clickPosition, viewer.scene.globe.ellipsoid)
|
||
}
|
||
}
|
||
|
||
// 方法2: 如果上述方法都失败,使用椭球面拾取
|
||
if (!pickedPosition) {
|
||
pickedPosition = viewer.camera.pickEllipsoid(clickPosition, viewer.scene.globe.ellipsoid)
|
||
}
|
||
|
||
if (pickedPosition) {
|
||
// 转换为地理坐标
|
||
const cartographic = Cesium.Cartographic.fromCartesian(pickedPosition)
|
||
const longitude = Cesium.Math.toDegrees(cartographic.longitude)
|
||
const latitude = Cesium.Math.toDegrees(cartographic.latitude)
|
||
const height = cartographic.height
|
||
|
||
return {
|
||
cartesian3: pickedPosition,
|
||
cartographic: {
|
||
longitude,
|
||
latitude,
|
||
height,
|
||
lon: longitude, // 兼容现有代码
|
||
lat: latitude // 兼容现有代码
|
||
}
|
||
}
|
||
}
|
||
} catch (error) {
|
||
console.warn('Position picking failed:', error)
|
||
}
|
||
|
||
return null
|
||
}
|
||
|
||
/**
|
||
* 创建标记实体的统一配置
|
||
* @param {Object} coordinates - 地理坐标 {lat, lng}
|
||
* @param {Object} options - 标记选项
|
||
* @returns {Object} Cesium实体配置
|
||
*/
|
||
export function createMarkerEntityConfig(coordinates, options = {}) {
|
||
const {
|
||
id = 'map-marker',
|
||
imageUrl = '/src/assets/images/marker-red.svg',
|
||
width = 32,
|
||
height = 32,
|
||
showLabel = true,
|
||
labelOffset = 40,
|
||
fontSize = '12pt'
|
||
} = options
|
||
|
||
const config = {
|
||
id,
|
||
billboard: {
|
||
image: imageUrl,
|
||
width,
|
||
height,
|
||
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
|
||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||
// 添加像素偏移补偿
|
||
pixelOffset: new Cesium.Cartesian2(0, 0),
|
||
// 禁用深度测试以确保标记总是可见
|
||
disableDepthTestDistance: Number.POSITIVE_INFINITY
|
||
}
|
||
}
|
||
|
||
if (showLabel) {
|
||
config.label = {
|
||
text: `${coordinates.lat.toFixed(6)}, ${coordinates.lng.toFixed(6)}`,
|
||
font: `${fontSize} sans-serif`,
|
||
fillColor: Cesium.Color.WHITE,
|
||
outlineColor: Cesium.Color.BLACK,
|
||
outlineWidth: 2,
|
||
verticalOrigin: Cesium.VerticalOrigin.TOP,
|
||
pixelOffset: new Cesium.Cartesian2(0, labelOffset),
|
||
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
|
||
// 确保标签在标记上方
|
||
eyeOffset: new Cesium.Cartesian3(0, 0, -100)
|
||
}
|
||
}
|
||
|
||
return config
|
||
} |