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
|
|||
|
|
}
|