294 lines
7.9 KiB
JavaScript
Raw Normal View History

/**
* 路线可视化 Composable
* 负责在 Cesium 地图上绘制路线和标签
*/
import { ref } from 'vue'
import * as Cesium from 'cesium'
import { ROUTE_STYLES, LABEL_STYLE } from '../constants/routeStyles'
import { formatDistance, formatDuration } from '../utils/geoUtils'
export function useRouteVisualization() {
// 路线实体集合
const routeEntities = ref([])
const labelEntities = ref([])
/**
* polyline 字符串转换为 Cartesian3 数组
* @param {string} polylineString - 高德 polyline 格式
* @returns {Array<Cesium.Cartesian3>}
*/
const transformToCartesian3 = (polylineString) => {
if (!polylineString) return []
try {
return polylineString
.split(';')
.map(pair => {
const [lon, lat] = pair.split(',').map(Number)
if (isNaN(lon) || isNaN(lat)) return null
return Cesium.Cartesian3.fromDegrees(lon, lat, 0)
})
.filter(Boolean)
} catch (error) {
console.error('[useRouteVisualization] polyline 转换失败:', error)
return []
}
}
/**
* 绘制单条路线
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
* @param {Object} routeData - 路线数据
* @param {Object} styleOptions - 样式选项
* @returns {Cesium.Entity} 路线实体
*/
const drawRoute = (viewer, routeData, styleOptions = {}) => {
if (!viewer || !routeData || !routeData.polyline) {
console.warn('[useRouteVisualization] 参数无效')
return null
}
const positions = transformToCartesian3(routeData.polyline)
if (positions.length < 2) {
console.warn('[useRouteVisualization] 路线点数不足')
return null
}
// 获取样式
const type = routeData.isFallback ? 'fallback' : (routeData.type || 'personnel')
const style = ROUTE_STYLES[type] || ROUTE_STYLES.personnel
// 创建路线实体
const entity = viewer.entities.add({
polyline: {
positions: positions,
width: styleOptions.width || style.width,
material: styleOptions.material || style.material(viewer),
clampToGround: true,
classificationType: Cesium.ClassificationType.TERRAIN
},
properties: {
type: 'route',
routeType: type,
routeId: routeData.id,
routeName: routeData.name,
distance: routeData.distance,
duration: routeData.duration,
isFallback: routeData.isFallback || false
}
})
routeEntities.value.push(entity)
console.log(`[useRouteVisualization] 绘制路线: ${routeData.name} (${type})`)
return entity
}
/**
* 绘制多条路线
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
* @param {Array} routesArray - 路线数组
* @returns {Array<Cesium.Entity>} 路线实体数组
*/
const drawMultipleRoutes = (viewer, routesArray) => {
if (!viewer || !routesArray || routesArray.length === 0) {
console.warn('[useRouteVisualization] 参数无效')
return []
}
console.log(`[useRouteVisualization] 绘制 ${routesArray.length} 条路线`)
const entities = []
routesArray.forEach((route, index) => {
try {
// 绘制路线
const routeEntity = drawRoute(viewer, route)
if (routeEntity) {
entities.push(routeEntity)
// 添加标签
const labelEntity = addRouteLabel(viewer, route)
if (labelEntity) {
entities.push(labelEntity)
}
}
} catch (error) {
console.error(`[useRouteVisualization] 绘制路线 ${route.name} 失败:`, error)
}
})
console.log(`[useRouteVisualization] 成功绘制 ${entities.length / 2} 条路线`)
return entities
}
/**
* 添加路线标签显示距离和时间
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
* @param {Object} routeData - 路线数据
* @returns {Cesium.Entity} 标签实体
*/
const addRouteLabel = (viewer, routeData) => {
if (!viewer || !routeData || !routeData.polyline) {
return null
}
const positions = transformToCartesian3(routeData.polyline)
if (positions.length < 2) {
return null
}
// 计算中点位置
const midIndex = Math.floor(positions.length / 2)
const labelPosition = positions[midIndex]
// 格式化文本
const distanceText = formatDistance(routeData.distance)
const durationText = formatDuration(routeData.duration)
const labelText = `${distanceText} | ${durationText}`
// 添加降级标识
const finalText = routeData.isFallback ? `${labelText} (直线)` : labelText
// 获取标签颜色
const type = routeData.isFallback ? 'fallback' : (routeData.type || 'personnel')
const style = ROUTE_STYLES[type] || ROUTE_STYLES.personnel
// 创建标签实体
const entity = viewer.entities.add({
position: labelPosition,
label: {
text: finalText,
font: LABEL_STYLE.font,
fillColor: LABEL_STYLE.fillColor,
outlineColor: LABEL_STYLE.outlineColor,
outlineWidth: LABEL_STYLE.outlineWidth,
style: LABEL_STYLE.style,
pixelOffset: LABEL_STYLE.pixelOffset,
disableDepthTestDistance: LABEL_STYLE.disableDepthTestDistance,
heightReference: LABEL_STYLE.heightReference,
verticalOrigin: LABEL_STYLE.verticalOrigin,
// 添加背景
showBackground: true,
backgroundColor: Cesium.Color.BLACK.withAlpha(0.5),
backgroundPadding: new Cesium.Cartesian2(7, 5)
},
properties: {
type: 'routeLabel',
routeId: routeData.id,
routeName: routeData.name
}
})
labelEntities.value.push(entity)
return entity
}
/**
* 清除所有路线实体
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
*/
const clearRoutes = (viewer) => {
if (!viewer) return
// 清除路线实体
routeEntities.value.forEach(entity => {
if (entity) {
viewer.entities.remove(entity)
}
})
// 清除标签实体
labelEntities.value.forEach(entity => {
if (entity) {
viewer.entities.remove(entity)
}
})
routeEntities.value = []
labelEntities.value = []
console.log('[useRouteVisualization] 已清除所有路线')
}
/**
* 清除指定路线
* @param {Cesium.Viewer} viewer - Cesium viewer 实例
* @param {string} routeId - 路线 ID
*/
const clearRoute = (viewer, routeId) => {
if (!viewer || !routeId) return
// 清除路线实体
routeEntities.value = routeEntities.value.filter(entity => {
if (entity && entity.properties && entity.properties.routeId === routeId) {
viewer.entities.remove(entity)
return false
}
return true
})
// 清除标签实体
labelEntities.value = labelEntities.value.filter(entity => {
if (entity && entity.properties && entity.properties.routeId === routeId) {
viewer.entities.remove(entity)
return false
}
return true
})
console.log(`[useRouteVisualization] 已清除路线: ${routeId}`)
}
/**
* 获取所有路线的边界框
* @param {Array} routesArray - 路线数组
* @returns {Array} 边界点数组 [{ lon, lat }]
*/
const getRoutesBounds = (routesArray) => {
const allPoints = []
routesArray.forEach(route => {
if (route.polyline) {
const positions = transformToCartesian3(route.polyline)
positions.forEach(pos => {
const carto = Cesium.Cartographic.fromCartesian(pos)
allPoints.push({
lon: Cesium.Math.toDegrees(carto.longitude),
lat: Cesium.Math.toDegrees(carto.latitude)
})
})
}
})
return allPoints
}
return {
// 状态
routeEntities,
labelEntities,
// 方法
drawRoute,
drawMultipleRoutes,
addRouteLabel,
clearRoutes,
clearRoute,
getRoutesBounds,
transformToCartesian3,
// 样式常量
ROUTE_STYLES
}
}
export default useRouteVisualization