Cesium有很多很强大的功能,可以在地球上实现很多炫酷的3D效果。今天给大家分享一个视线分析功能。
1.话不多说,先展示
视频:视线分析
2.设计思路
点击绘制在地图上绘制一个点,再次点击绘制第二个点,第一个点看向第二个点,如果被遮挡物遮挡的话,可见部分绿色部分表示,不可见部分红色表示,俩点之间的高度可自己设置,在此我代码中设定高度为30。
3.具体代码
<template>
<div class="page">
<el-button @click="draw">绘制</el-button>
<el-table :data="dataList" border @row-click="rowClick">
<el-table-column prop="name" label="名称" align="center" />
<el-table-column prop="action" label="操作" align="center">
<template #default="scope">
<el-button link type="primary" size="small" @click="delEntity(scope.row, scope.$index)"><el-icon
:size="16"><ele-Delete /> </el-icon></el-button>
</template>
</el-table-column>
</el-table>
</div>
<el-dialog v-model="dialogFormVisible" title="配置" width="500" :close-on-press-escape="false"
:close-on-click-modal="false" :show-close="false">
<el-form ref="formRef" :model="form" label-width="auto" :rules="rules">
<el-form-item label="视线分析名称" prop="title">
<el-input v-model="form.title" placeholder="请输入" />
</el-form-item>
</el-form>
<template #footer>
<div class="dialog-footer">
<el-button type="primary" @click="submitForm(formRef)"> 确定 </el-button>
</div>
</template>
</el-dialog>
</template>
<script setup lang="ts">
import { debounce } from 'lodash';
import { onMounted, onUnmounted, reactive, ref } from 'vue';
import { Cesium } from '/@/utils/cesium';
const props = defineProps(['viewer']);
const dialogFormVisible = ref(false);
var handler: any = null;
const formRef = ref();
const rules = {
title: { required: true, message: '请输入视线分析名称', trigger: 'blur' },
};
// 视点名称
const form = reactive({
title: '',
});
// 列表数据
const dataList: any = reactive([]);
/**
* 添加定位实体
*/
var addElectronicFence = (name: string, positions: any) => {
dataList.push({
id: new Date().getTime(),
name: name,
positions: positions,
});
};
const drawing = ref(false);
let positions: any = [];
let markers: any = [];//点实体
let lineEntity: any = [];//线实体
let entity: any = [];//一条列表记录的实体
let entityObj: any = {};
/**
* 点击绘制按钮
*/
const draw = () => {
drawing.value = true;
};
/**
* 删除已绘制的图形
*/
const delEntity = (item: any, index: number) => {
console.log(entity);
props.viewer.entities.remove(entity[index].pointA);
props.viewer.entities.remove(entity[index].pointB);
props.viewer.entities.remove(entity[index].lineA);
if (entity[index].lineB) {
props.viewer.entities.remove(entity[index].lineB);
}
entity.splice(index, 1);
dataList.splice(index, 1);
};
/**
* 点击表格一行
*/
const rowClick = (row: any, column: any, event: Event) => {
if (column && column.property) {
let index = dataList.findIndex((v: any) => v.id === row.id);
if (index !== -1) {
props.viewer.flyTo(entity[index].lineA, {
offset: {
heading: Cesium.Math.toRadians(299.7957386132123),
pitch: Cesium.Math.toRadians(-10.012383459541422),
roll: 0.001963990089065944,
},
});
}
}
};
/**
* 点击确定绘制
*/
const submitForm = async (formEl: any) => {
const valid = await formEl.validate();
if (valid) {
var pointList: any = [];
pointList.push(positions);
entity.push(entityObj);
entityObj = {};
//重新置为空方便下次绘制
positions = [];
lineEntity = [];
markers = [];
addElectronicFence(form.title, pointList);
dialogFormVisible.value = false;
formEl.resetFields();
}
};
/**
* 添加鼠标事件
*/
const addClickHandler = () => {
handler = new Cesium.ScreenSpaceEventHandler(props.viewer.scene.canvas);
// 单击绘制
handler.setInputAction((event: any) => {
leftClickHandler(event);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
};
/**
* 单击左键
*/
const leftClickHandler = debounce((movement: any) => {
if (drawing.value) {
let cartesian = props.viewer.scene.pickPosition(movement.position);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian)
let longitude = Cesium.Math.toDegrees(cartographic.longitude)
let latitude = Cesium.Math.toDegrees(cartographic.latitude)
let height = cartographic.height
let position1 = Cesium.Cartesian3.fromDegrees(longitude, latitude, 30);
if (cartesian) {
if (markers.length === 0) {
positions.push(position1);//加点
//创建点实体
const startpoint = props.viewer.entities.add({
position: position1,
// position: cartesian,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
point: {
color: Cesium.Color.RED,
pixelSize: 10,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2
}
});
console.log(startpoint);
markers.push(startpoint);
entityObj.pointA = startpoint;
} else if (markers.length >= 1) {
positions.push(cartesian);//加点
const endpoint = props.viewer.entities.add({
position: cartesian,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
point: {
color: Cesium.Color.YELLOW,
pixelSize: 10,
heightReference: Cesium.HeightReference.CLAMP_TO_GROUND,
outlineColor: Cesium.Color.BLACK,
outlineWidth: 2
}
});
markers.push(endpoint);
entityObj.pointB = endpoint;
let linePoint = [];
linePoint.push(positions[0]);
linePoint.push(positions[positions.length - 1]);
analysisVisible(linePoint);//开始分析
//画完视线分析后停止
drawing.value = false;
dialogFormVisible.value = true;
}
}
}
}, 100);
const analysisVisible = (positions: any) => {
// 计算射线的方向
let direction = Cesium.Cartesian3.normalize(
Cesium.Cartesian3.subtract(
positions[1],
positions[0],
new Cesium.Cartesian3()
),
new Cesium.Cartesian3()
);
// 建立射线
let ray = new Cesium.Ray(positions[0], direction);
// 计算交互点,返回第一个
let result = props.viewer.scene.pickFromRay(ray);
// console.log(result)
if (Cesium.defined(result) && Cesium.defined(result.object)) {
let lineA = drawLine(result.position, positions[0], Cesium.Color.GREEN); // 可视区域
entityObj.lineA = lineA;
let lineB = drawLine(result.position, positions[1], Cesium.Color.RED); // 不可视区域
entityObj.lineB = lineB;
} else {
let lineA = drawLine(positions[0], positions[1], Cesium.Color.GREEN);
console.log("不在模型上")
entityObj.lineA = lineA;
}
}
function drawLine(leftPoint: any, secPoint: any, color: any) {
let line = props.viewer.entities.add({
polyline: {
positions: [leftPoint, secPoint],
width: 2,
material: color,
depthFailMaterial: color,// 被地形遮挡部分的颜色
}
});
lineEntity.push(line)
return line;
}
const handleIntervisibilityCancel = () => {
props.viewer.entities.removeAll();
entity = [];//列表数据实体
entityObj = {};
}
onMounted(() => {
addClickHandler();
});
onUnmounted(() => {
handleIntervisibilityCancel();
handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
});
</script>
<style scoped>
.page {
position: absolute;
right: 10px;
top: 10px;
color: #fff;
background: #fff;
padding: 10px;
border-radius: 5px;
width: 300px;
}
</style>