在地理信息系统(GIS)和军事模拟领域,箭头绘制是一项基础且重要的功能。本文将介绍如何使用Cesium.js结合Vue框架实现交互式攻击箭头绘制功能,支持鼠标点击采集关键点、动态更新箭头形状、右键结束绘制等核心交互,并对实现过程中的关键技术点进行深入解析。
本组件实现了以下核心功能:
// 导入地图初始化函数
import initMap from '@/config/initMap.js';
// 导入地图配置项(包含高德地图服务地址等)
import { mapConfig } from '@/config/mapConfig';
// 导入算法工具库(包含攻击箭头绘制算法)
import xp from '@/utils/algorithm.js';
// 导入标记点图片资源
import boardimg from '@/assets/images/captain-01.png';
data() {
return {
viewer: null, // Cesium地图实例对象
gatherPosition: [], // 采集的点坐标数组(Cartesian3格式)
floatingPoint: null, // 鼠标移动时的动态浮动点实体
drawHandler: null, // 屏幕空间事件处理器
layerId: 'attackArrowLayer', // 自定义图层ID
};
}
mounted() {
// 初始化Cesium地图,使用高德地图服务
this.viewer = initMap(mapConfig.gaode.url3, false);
// 延迟1秒执行箭头采集函数,确保地图资源加载完成
setTimeout(() => {
this.gatherAttackArrow();
}, 1000);
}
/**
* 开始采集攻击箭头的坐标点
* 绑定鼠标事件:左键点击采集点,鼠标移动更新浮动点,右键点击结束采集
*/
gatherAttackArrow() {
console.log('开始采集攻击箭头');
this.gatherPosition = []; // 清空历史采集点数组
this.floatingPoint = null; // 重置浮动点实体
this.clearPlot(); // 清除已有绘制内容
// 创建屏幕空间事件处理器,用于监听鼠标交互
this.drawHandler = new Cesium.ScreenSpaceEventHandler(
this.viewer.scene.canvas
);
// 左键点击事件 - 采集坐标点
this.drawHandler.setInputAction((event) => {
// 坐标点采集逻辑...
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 鼠标移动事件 - 更新浮动点位置
this.drawHandler.setInputAction((event) => {
// 浮动点更新逻辑...
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 右键点击事件 - 停止采集并处理结果
this.drawHandler.setInputAction((movement) => {
// 结束采集逻辑...
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
/**
* 创建地图上的关键点标记
* @param {Cesium.Cartesian3} cartesian - 点的笛卡尔坐标
* @param {number} oid - 点的唯一标识
* @returns {Cesium.Entity} 创建的点实体对象
*/
createPoint(cartesian, oid) {
const point = this.viewer.entities.add({
position: cartesian,
billboard: {
image: boardimg, // 使用导入的图片作为标记
eyeOffset: new Cesium.Cartesian3(0, 0, -100), // 视线偏移
heightReference: Cesium.HeightReference.NONE, // 不贴地
scale: 1.0, // 缩放比例
width: 32, // 固定宽度
height: 32, // 固定高度
},
});
// 自定义属性,用于后续识别和管理
point.oid = oid;
point.layerId = this.layerId;
point.flag = 'keypoint';
return point;
}
/**
* 在地图上动态显示攻击箭头区域
* 通过CallbackProperty实现多边形和轮廓线的动态更新
*/
showRegion2Map() {
// 定义多边形填充材质(黄色半透明)
const material = Cesium.Color.fromCssColorString('#ff0').withAlpha(0.5);
// 定义轮廓线材质(红色虚线)
const outlineMaterial = new Cesium.PolylineDashMaterialProperty({
dashLength: 16,
color: Cesium.Color.fromCssColorString('#f00').withAlpha(0.7),
});
// 动态更新多边形层级(攻击箭头形状)
const dynamicHierarchy = new Cesium.CallbackProperty(() => {
if (this.gatherPosition.length > 1) {
const lonLats = this.getLonLatArr(this.gatherPosition);
// 使用算法库生成带尾翼的攻击箭头
const doubleArrow = xp.algorithm.tailedAttackArrow(lonLats);
const positions = doubleArrow.polygonalPoint;
if (!positions || positions.length < 3) return null;
positions.splice(positions.length - 2, 1); // 移除燕尾部分
const pHierarchy = new Cesium.PolygonHierarchy(positions);
pHierarchy.keyPoints = lonLats; // 存储原始关键点
return pHierarchy;
}
return null;
}, false);
// 动态更新轮廓线坐标
const outlineDynamicPositions = new Cesium.CallbackProperty(() => {
// 轮廓线更新逻辑...
}, false);
// 添加多边形和轮廓线实体到地图
const entity = this.viewer.entities.add({
polygon: new Cesium.PolygonGraphics({
hierarchy: dynamicHierarchy,
material: material,
show: true,
}),
polyline: new Cesium.PolylineGraphics({
positions: outlineDynamicPositions,
clampToGround: true,
width: 2,
material: outlineMaterial,
show: true,
}),
});
entity.layerId = this.layerId;
entity.valueFlag = 'value';
}
/**
* 将笛卡尔坐标数组转换为经纬度数组
* @param {Array} positions - 笛卡尔坐标数组
* @returns {Array>} 经纬度数组 [[lon, lat], ...]
*/
getLonLatArr(positions) {
const arr = [];
for (let i = 0; i < positions.length; i++) {
const p = this.getLonLat(positions[i]);
if (p) {
arr.push([p.lon, p.lat]);
}
}
return arr;
}
/**
* 将笛卡尔坐标转换为经纬度坐标
* @param {Cesium.Cartesian3} cartesian - 笛卡尔坐标
* @returns {Object} 包含lon, lat, alt的经纬度对象
*/
getLonLat(cartesian) {
const cartographic = this.viewer.scene.globe.ellipsoid.cartesianToCartographic(cartesian);
cartographic.height = this.viewer.scene.globe.getHeight(cartographic);
return {
lon: Cesium.Math.toDegrees(cartographic.longitude),
lat: Cesium.Math.toDegrees(cartographic.latitude),
alt: cartographic.height,
};
}
beforeDestroy() {
// 组件销毁时释放资源,避免内存泄漏
if (this.viewer) {
this.viewer.destroy(); // 销毁地图实例
}
if (this.drawHandler) {
this.drawHandler.destroy(); // 销毁事件处理器
}
}
Cesium中的CallbackProperty
是实现动态图形的核心,它允许我们定义一个回调函数,该函数会在每一帧渲染时被调用,返回最新的属性值。在攻击箭头绘制中,我们利用这一特性实现了箭头形状的实时更新:
const dynamicHierarchy = new Cesium.CallbackProperty(() => {
// 根据当前采集的点动态生成箭头形状
// ...
}, false);
第二个参数false
表示该属性不会引发视图更新,适合用于频繁更新的场景。
Cesium的ScreenSpaceEventHandler
用于处理用户交互事件,使用时需要注意:
destroy()
方法释放资源Cesium中存在多种坐标系统,本项目中主要涉及:
通过ellipsoid.cartesianToCartographic
和Math.toDegrees
实现坐标转换。
通过自定义属性(如layerId
、flag
)对Cesium实体进行分类管理,便于批量操作和清理:
// 清除当前图层上的所有绘制内容
clearPlot() {
const entityList = this.viewer.entities.values;
if (!entityList || entityList.length < 1) return;
for (let i = 0; i < entityList.length; i++) {
const entity = entityList[i];
if (entity.layerId === this.layerId) {
this.viewer.entities.remove(entity);
i--; // 移除后索引调整
}
}
}
mapConfig.js
中配置高德或其他地图服务地址mounted
钩子自动初始化或手动调用gatherAttackArrow
方法
本文实现了一个功能完整的交互式攻击箭头绘制组件,核心亮点包括:
未来可以进一步优化的方向:
通过本文的实现,不仅掌握了Cesium的核心交互和绘图API,也学习了大型前端GIS应用中的性能优化和内存管理技巧,为构建更复杂的地理信息应用奠定了基础。
点点关注不迷路,代码中使用到的 algorithm.js与 plotUtil.js 有需要的留言。