Cesium 飞行路线编辑
效果
实现的主要功能
- 拖动点位图标修改位置
- 点击线,在线上新增点位
- 右击点位图标,删除点位
这些功能配合父页面使用,所以并没有从头开始绘制路线,父页面是具有增删改功能的飞行路线点位列表,包含时间点、经纬度、高度等字段信息
页面代码(vue2)
<template>
<el-dialog :title="title" :visible.sync="open" width="90vw" append-to-body>
<div style="height: 80vh;">
<three ref="three"></three>
</div>
<div slot="footer" class="dialog-footer">
<el-button type="primary" @click="ok">确 定</el-button>
<el-button @click="cancel">取 消</el-button>
</div>
</el-dialog>
</template>
<script>
/**
* 选择位置集合
*/
import Three from "@/views/three/index.vue";
import { getMap } from "@/views/three/js/index.js";
import { getHeightByLngLat } from "@/views/three/js/utils.js";
let map;
let graphicLayer;
let graphic;
let hander;
let picked;
let pickedIndex;
let move;
export default {
name: "SelectPositions",
data() {
return {
title: "选择位置集合", // 弹出层标题
open: false, // 是否显示弹出层
waitHandle: undefined,
waitHandle2: undefined,
tag: undefined,
index: undefined,
list: [
{
time: undefined,
lng: 117.12,
lat: 31.86,
height: undefined,
createUser: undefined,
createTime: undefined,
updateUser: undefined,
updateTime: undefined,
},
{
time: undefined,
lng: undefined,
lat: undefined,
height: undefined,
createUser: undefined,
createTime: undefined,
updateUser: undefined,
updateTime: undefined,
},
{
time: undefined,
lng: 117.13,
lat: 31.87,
height: undefined,
createUser: undefined,
createTime: undefined,
updateUser: undefined,
updateTime: undefined,
},
],
};
},
components: {
Three,
},
created() {},
async mounted() {
await this.waitThreeReady();
this.$refs.three.containerId = "mars3dContainer2";
await this.$refs.three.initMap({
scene: {
center: {
lng: 117.119112,
lat: 31.862912,
alt: 10000,
heading: 0,
pitch: -89,
},
},
control: {
clockAnimate: false,
timeline: false,
},
}); // 子组件调用父组件的方法
map = getMap();
graphicLayer = new mars3d.layer.GraphicLayer();
map.addLayer(graphicLayer);
this.bindMapClick(); // 绑定地图点击事件
map.unbindContextMenu(); // 屏蔽右键菜单
},
beforeDestroy() {
this.waitHandle && clearInterval(this.waitHandle);
this.waitHandle2 && clearInterval(this.waitHandle2);
this.unbindMapClick();
},
methods: {
async openDialog(data) {
this.open = true;
this.index = data.index;
this.tag = data.tag;
if (data.list) this.list = data.list;
await this.waitTerrainReady();
this.showPoints();
},
// 显示点位集合
async showPoints() {
graphicLayer.clear();
let positions = [];
for (let i = 0; i < this.list.length; i++) {
let item = this.list[i];
let height = undefined;
if (item.lng && item.lat) {
height = await getHeightByLngLat(map, item.lng, item.lat);
let entity = await this.createBillboardEntity(
this.index == i ? i + 1 + "(当前点位)" : i + 1,
item.lng,
item.lat,
height
);
entity.options.index = i; // 把index记下来
graphicLayer.addGraphic(entity);
if (this.index == i) {
graphic = entity;
}
positions.push([item.lng, item.lat, height]);
}
}
let graphicPolyline = new mars3d.graphic.PolylineEntity({
positions: positions,
style: {
color: "rgba(255,255,0,1)",
width: 5,
},
});
graphicLayer.addGraphic(graphicPolyline);
},
async createBillboardEntity(text, lng, lat, height) {
return new mars3d.graphic.BillboardEntity({
position: [lng, lat, height],
style: {
image: "img/marker/mark-blue.png",
scale: 1,
horizontalOrigin: Cesium.HorizontalOrigin.CENTER,
verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
label: {
text: text,
font_size: 18,
color: "#ffffff",
pixelOffsetY: -50,
},
},
});
},
// 绑定地图点击事件
bindMapClick() {
hander = new Cesium.ScreenSpaceEventHandler(map.viewer.scene.canvas);
// 鼠标单击
hander.setInputAction(async (e) => {
let position = this.pitchLngLat(e.position);
if (!position) return;
let pickedPolyline = false;
map.viewer.scene.drillPick(e.position).map((item) => {
if (item.id instanceof Cesium.Entity) {
if (item.id.polyline) {
pickedPolyline = true;
}
}
});
if (!pickedPolyline) {
this.setText(this.index + "(当前点位)");
await this.setPosition(graphic, position.lng, position.lat);
let currentItem = this.list[this.index];
currentItem.lng = position.lng;
currentItem.lat = position.lat;
this.showPoints();
} else {
let index = this.getIndex(e.position);
if (index + 1 <= this.index) {
this.index++;
}
this.list.splice(index + 1, 0, {
time: undefined,
lng: position.lng,
lat: position.lat,
height: undefined,
createUser: undefined,
createTime: undefined,
updateUser: undefined,
updateTime: undefined,
});
this.showPoints();
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 鼠标左键按下
hander.setInputAction((e) => {
map.viewer.scene.drillPick(e.position).map((item) => {
let graphics = graphicLayer.getGraphics();
for (let i = 0; i < graphics.length; i++) {
let graphic = graphics[i];
if (
graphic.id == item.id.id &&
graphic instanceof mars3d.graphic.BillboardEntity
) {
picked = graphic;
pickedIndex = graphic.options.index;
}
}
});
if (!picked) return;
map.viewer.scene.screenSpaceCameraController.enableRotate = false; // 锁定相机
move = true;
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
// 鼠标左键抬起
hander.setInputAction((e) => {
if (!move) return;
picked = false;
map.viewer.scene.screenSpaceCameraController.enableRotate = true; // 取消锁定相机
move = false;
this.showPoints();
}, Cesium.ScreenSpaceEventType.LEFT_UP);
// 鼠标移动
hander.setInputAction((e) => {
if (!move) return;
let position = this.pitchLngLat(e.endPosition);
this.setPosition(picked, position.lng, position.lat);
this.list[pickedIndex].lng = position.lng;
this.list[pickedIndex].lat = position.lat;
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 鼠标右击
hander.setInputAction((e) => {
map.viewer.scene.drillPick(e.position).map((item) => {
let graphics = graphicLayer.getGraphics();
for (let i = 0; i < graphics.length; i++) {
let graphic = graphics[i];
if (
graphic.id == item.id.id &&
graphic instanceof mars3d.graphic.BillboardEntity
) {
picked = graphic;
pickedIndex = graphic.options.index;
}
}
});
if (!picked) return;
if (
picked.options.index != this.index &&
picked.options.index != 0 &&
picked.options.index != this.list.length - 1
) {
this.list.splice(picked.options.index, 1);
if (picked.options.index < this.index) {
this.index--;
}
this.showPoints();
}
picked = undefined;
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
},
// 取消绑定地图点击事件
unbindMapClick() {
if (hander && !hander.isDestroyed()) {
hander.destroy();
hander = undefined;
}
},
//拾取经纬度
pitchLngLat(position) {
let cartesian = map.viewer.camera.pickEllipsoid(
position,
map.viewer.scene.globe.ellipsoid
);
if (!cartesian) return undefined;
let cartographic = Cesium.Cartographic.fromCartesian(cartesian);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
return { lng, lat };
},
// 查询在线上拾取的点位在哪两个点之间
getIndex(position) {
let minDistance;
let index = -1;
let p = this.pitchLngLat(position);
for (let i = 0; i < this.list.length - 1; i++) {
let item1 = this.list[i];
let item2 = this.list[i + 1];
let p1 = [item1.lng, item1.lat];
let p2 = [item2.lng, item2.lat];
let point = turf.point([p.lng, p.lat]);
let line = turf.lineString([p1, p2]);
let distance = turf.pointToLineDistance(point, line, {
units: "kilometers",
});
if (!minDistance) {
minDistance = distance;
index = i;
} else {
if (distance < minDistance) {
minDistance = distance;
index = i;
}
}
}
return index;
},
// 取消按钮
cancel() {
this.open = false;
},
// 确定
ok() {
this.open = false;
this.$emit("selectPositionsOK", this.tag, this.index, this.list);
},
async setPosition(graphic, lng, lat) {
this.lng = lng;
this.lat = lat;
if (graphic) {
let height = await getHeightByLngLat(map, lng, lat);
graphic.position = [lng, lat, height];
}
},
setText(text) {
if (graphic) {
graphic.label.text = text;
}
},
// 等待this.$refs.three准备就绪
async waitThreeReady() {
return new Promise((resolve, reject) => {
this.waitHandle = setInterval(() => {
if (this.$refs.three) {
clearInterval(this.waitHandle);
this.waitHandle = undefined;
resolve();
}
}, 100);
});
},
// 等待terrainProvider准备就绪
async waitTerrainReady() {
return new Promise((resolve, reject) => {
this.waitHandle2 = setInterval(() => {
if (map && map.viewer && map.viewer.terrainProvider.availability) {
clearInterval(this.waitHandle2);
this.waitHandle2 = undefined;
resolve();
}
}, 100);
});
},
},
};
</script>