Cesium有很多很强大的功能,可以在地球上实现很多炫酷的3D效果。今天给大家分享一个视线分析功能。

1.话不多说,先展示

【Cesium开发实战】视线分析功能的实现_Cesium

视频:视线分析

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>