Vue3加vite使用Cesium绘制图形
1、项目开发准备
Node版本:16.20.2
1.1创建一个新的工程:my-cesium-app
npm create vite@latest my-cesium-app – --template vue
1.2 安装Element Plus
npm install element-plus --save
// main.js
import ElementPlus from 'element-plus'
import 'element-plus/dist/index.css'
import * as ElementPlusIconsVue from '@element-plus/icons-vue'
const app = createApp(App)
//字体图标注册
for (const [key, component] of Object.entries(ElementPlusIconsVue)) {
app.component(key, component)
}
1.3安装cesium
npm i cesium@1.99 vite-plugin-cesium
// main.js
import * as Cesium from 'cesium';
const app = createApp(App)
app.use(Cesium)
//vite.config.js
import { defineConfig } from 'vite'
import vue from '@vitejs/plugin-vue'
import cesium from 'vite-plugin-cesium'
export default defineConfig({ plugins: [ vue(),cesium()]})
1.4注册Cesium
首先需要去注册一个免费的Cesium ion账户
打开https://ion.cesium.com/ 然后注册一个新的账户。
点击”Access Token”,跳转到Access Tokens page页面。
选择Default默认的access token拷贝到contents中。
2、使用Draw.js绘制对应图形
2.1基础参数
constructor(viewer, config) {
this.viewer = viewer;
this.config = config || {
borderColor: Cesium.Color.BLUE,
borderWidth: 2,
material: Cesium.Color.GREEN.withAlpha(0.5),
};
this.infoDetail = { point: [], line: [], rectangle: [], circle: [], planeSelf: [], sector: [] };
//判断是否右键销毁
this.isDestroy = false
//是否是销毁状态
this.isRemove = false
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
}
2.2 绘制点
drawPoint () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isDestroy = false
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((click) => {
let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
let id = new Date().getTime();
this.viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(lng, lat, 0),
name: 'point',
id: id,
point: {
color: this.config.material,
pixelSize: 12,
outlineColor: this.config.borderColor,
outlineWidth: this.config.borderWidth,
},
});
this.infoDetail.point.push({ id: id, position: [lng, lat] });
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
this.handler.setInputAction((click) => {
if (this.handler) {
this.isDestroy = true
this.handler.destroy();
//重新点击进行绘制
this.drawPoint()
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
2.3绘制矩形区域
/*******
* @function: function
* @description: 绘制矩形区域
*/
drawRectangle () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false
this.isDestroy = false
let westSouthEastNorth = [];
let id = null;
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((click) => {
let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
let lng1 = Cesium.Math.toDegrees(cartographic.longitude);
let lat1 = Cesium.Math.toDegrees(cartographic.latitude);
westSouthEastNorth = [lng1, lat1];
id = new Date().getTime();
if (westSouthEastNorth) {
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
let polygons = this.viewer.entities.add({
name: 'rectangle',
id: id,
polygon: {
hierarchy: new Cesium.CallbackProperty(function () {
return {
positions: Cesium.Cartesian3.fromDegreesArray(westSouthEastNorth),
};
}, false),
height: 0,
material: this.config.material,
fill: true,
show: true,
},
polyline: {
positions: new Cesium.CallbackProperty(function () { return Cesium.Cartesian3.fromDegreesArray(westSouthEastNorth); }, false),
material: this.config.borderColor,
width: this.config.borderWidth,
zIndex: 1,
},
});
this.handler.setInputAction((move) => {
let cartesian = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
westSouthEastNorth = [lng1, lat1, lng1, lat, lng, lat, lng, lat1, lng1, lat1];
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
//右键完成绘制
this.handler.setInputAction(() => {
if (this.handler) {
this.isDestroy = true
this.handler.destroy();
//重新点击进行绘制
this.drawRectangle()
}
this.infoDetail.rectangle.push({ id: id, position: westSouthEastNorth });
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
2.4绘制圆形区域,并计算面积和周长
drawCircle() {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false;
this.isDestroy = false;
let id = null;
let radius = 0;
let lngLat = [];
let circleEntity = null;
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((click) => {
id = new Date().getTime();
let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
lngLat = [lng, lat];
circleEntity = this.viewer.entities.add({
position: new Cesium.CallbackProperty(function () { return Cesium.Cartesian3.fromDegrees(...lngLat, 0); }, false),
name: 'circle',
id: id,
ellipse: {
height: 0,
outline: true,
material: this.config.material,
outlineColor: this.config.borderColor,
outlineWidth: this.config.borderWidth,
semiMajorAxis: new Cesium.CallbackProperty(function () { return radius; }, false),
semiMinorAxis: new Cesium.CallbackProperty(function () { return radius; }, false),
},
});
this.handler.setInputAction((move) => {
let cartesian2 = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
radius = Cesium.Cartesian3.distance(cartesian, cartesian2);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
this.handler.setInputAction(() => {
// 计算面积(πr²)和周长(2πr)
let area = Math.PI * Math.pow(radius, 2); // 单位:平方米
let circumference = 2 * Math.PI * radius; // 单位:米
this.infoDetail.circle.push({
id: id,
center: lngLat,
radius: radius,
area: area, // 保存面积
circumference: circumference // 保存周长
});
if (this.handler) {
this.isDestroy = true;
this.handler.destroy();
this.drawCircle();
//this.enableCircleDragging(circleEntity, id); // 启用拖动功能
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
2.5绘制自定义区域,并计算面积
drawPlane() {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false;
this.isDestroy = false;
let id = new Date().getTime();
let positions = [];
let codeInfo = [];
let polygon = new Cesium.PolygonHierarchy();
let _polygonEntity = new Cesium.Entity();
let polyObj = null;
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((movement) => {
let cartesian = this.viewer.camera.pickEllipsoid(movement.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
if (cartesian && cartesian.x) {
if (positions.length == 0) {
positions.push(cartesian.clone());
}
codeInfo.push([lng, lat]);
positions.push(cartesian.clone());
polygon.positions.push(cartesian.clone());
if (!polyObj) {
_polygonEntity.polyline = {
width: this.config.borderWidth,
material: this.config.borderColor,
clampToGround: false,
};
_polygonEntity.polyline.positions = new Cesium.CallbackProperty(function () {
return positions;
}, false);
_polygonEntity.polygon = {
hierarchy: new Cesium.CallbackProperty(function () {
return polygon;
}, false),
material: this.config.material,
clampToGround: false,
};
_polygonEntity.name = 'planeSelf';
_polygonEntity._id = id;
polyObj = this.viewer.entities.add(_polygonEntity);
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
this.handler.setInputAction((movement) => {
let cartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
if (positions.length >= 0) {
if (cartesian && cartesian.x) {
positions.pop();
positions.push(cartesian);
polygon.positions.pop();
polygon.positions.push(cartesian);
codeInfo.pop();
codeInfo.push([lng, lat]);
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 右键完成绘制
this.handler.setInputAction((movement) => {
// 计算多边形面积
let area = this.calculatePolygonArea(positions);
this.infoDetail.planeSelf.push({
id: id,
positions: codeInfo,
area: area // 保存面积
});
if (this.handler) {
this.isDestroy = true;
this.handler.destroy();
this.drawPlane();
//this.enablePolygonDragging(polyObj, id); // 启用拖动功能
}
positions.push(positions[0]);
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
2.6绘制线段并计算其长度
drawLine () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false;
this.isDestroy = false;
let id = null;
let positions = [];
let codeInfo = [];
let polyObj = null;
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((movement) => {
id = new Date().getTime();
let cartesian = this.viewer.camera.pickEllipsoid(movement.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
if (cartesian && cartesian.x) {
if (positions.length == 0) {
positions.push(cartesian.clone());
}
codeInfo.push([lng, lat]);
positions.push(cartesian.clone());
if (!polyObj) {
polyObj = this.viewer.entities.add({
polyline: {
positions: new Cesium.CallbackProperty(() => positions, false),
width: this.config.borderWidth,
material: this.config.borderColor,
clampToGround: false,
},
name: 'line',
id: id,
});
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
this.handler.setInputAction((movement) => {
let cartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
if (positions.length >= 0) {
if (cartesian && cartesian.x) {
positions.pop();
positions.push(cartesian);
codeInfo.pop();
codeInfo.push([lng, lat]);
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 右键完成绘制
this.handler.setInputAction(() => {
// 计算线段长度
let length = this.calculateLineLength(positions);
this.infoDetail.line.push({
id: id,
positions: codeInfo,
length: length // 保存线段长度
});
if (this.handler) {
this.isDestroy = true;
this.handler.destroy();
this.drawLine(); // 重新启用绘制功能
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
/*******
* @function: function
* @description: 计算线段的总长度
*/
calculateLineLength (positions) {
let totalLength = 0;
for (let i = 0; i < positions.length - 1; i++) {
let startCartographic = Cesium.Cartographic.fromCartesian(positions[i]);
let endCartographic = Cesium.Cartographic.fromCartesian(positions[i + 1]);
let startPos = Cesium.Cartesian3.fromRadians(
startCartographic.longitude,
startCartographic.latitude
);
let endPos = Cesium.Cartesian3.fromRadians(
endCartographic.longitude,
endCartographic.latitude
);
let segmentLength = Cesium.Cartesian3.distance(startPos, endPos);
totalLength += segmentLength;
}
return totalLength; // 返回线段总长度,单位:米
}
2.7绘制扇形,并计算面积和弧长
drawSector () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false;
this.isDestroy = false;
let id = null;
let center = null;
let radius = 0;
let startAngle = 0;
let endAngle = 0;
let positions = [];
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
// 第一次点击确定中心点
this.handler.setInputAction((click) => {
id = new Date().getTime();
center = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
if (!center) return;
const cartographic = Cesium.Cartographic.fromCartesian(center);
const lng = Cesium.Math.toDegrees(cartographic.longitude);
const lat = Cesium.Math.toDegrees(cartographic.latitude);
let entity = this.viewer.entities.add({
id: id,
position: center,
polygon: {
hierarchy: new Cesium.CallbackProperty(() => new Cesium.PolygonHierarchy(positions), false),
material: this.config.material,
outline: true,
outlineColor: this.config.borderColor,
outlineWidth: this.config.borderWidth,
},
});
this.handler.setInputAction((move) => {
const movePosition = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
if (!movePosition) return;
radius = Cesium.Cartesian3.distance(center, movePosition);
const cartographicMove = Cesium.Cartographic.fromCartesian(movePosition);
const lngMove = Cesium.Math.toDegrees(cartographicMove.longitude);
const latMove = Cesium.Math.toDegrees(cartographicMove.latitude);
endAngle = Math.atan2(latMove - lat, lngMove - lng);
// 动态计算扇形边界点
positions = this.computeSectorPositions(center, radius, startAngle, endAngle);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 设置起始角度
this.handler.setInputAction((click) => {
const startCartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
if (!startCartesian) return;
const cartographicStart = Cesium.Cartographic.fromCartesian(startCartesian);
const lngStart = Cesium.Math.toDegrees(cartographicStart.longitude);
const latStart = Cesium.Math.toDegrees(cartographicStart.latitude);
startAngle = Math.atan2(latStart - lat, lngStart - lng);
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 右键完成绘制
this.handler.setInputAction(() => {
// 计算扇形的面积和弧长
let area = this.calculateSectorArea(radius, startAngle, endAngle);
let arcLength = this.calculateArcLength(radius, startAngle, endAngle);
this.infoDetail.sector.push({
id: id,
center: Cesium.Cartographic.fromCartesian(center),
radius: radius,
startAngle: startAngle,
endAngle: endAngle,
area: area, // 保存面积
arcLength: arcLength // 保存弧长
});
if (this.handler) {
this.isDestroy = true;
this.handler.destroy();
// 重新启用绘制功能
this.drawSector();
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
/*******
* @function: function
* @description: 计算扇形的面积
*/
calculateSectorArea (radius, startAngle, endAngle) {
let angleDiff = endAngle - startAngle;
if (angleDiff < 0) {
angleDiff += 2 * Math.PI; // 确保角度差为正
}
return 0.5 * Math.pow(radius, 2) * angleDiff;
}
2.8移除绘制对象,获取绘制对象的数据
/*******
* @function: function
* @description: 移除绘制对象
*/
removeEntity () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = true
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((move) => {
let pick = this.viewer.scene.pick(move.endPosition);
if (pick && pick.id && pick.id.id && this.isRemove) {
document.body.style.cursor = 'pointer';
this.handler.setInputAction((click) => {
let newPoint;
switch (pick.id.name) {
case 'point':
newPoint = this.infoDetail.point.filter((item) => item.id != pick.id._id);
this.infoDetail.point = newPoint;
break;
case 'line':
newPoint = this.infoDetail.line.filter((item) => item.id != pick.id._id);
this.infoDetail.line = newPoint;
break;
case 'rectangle':
newPoint = this.infoDetail.rectangle.filter((item) => item.id != pick.id._id);
this.infoDetail.rectangle = newPoint;
break;
case 'planeSelf':
newPoint = this.infoDetail.planeSelf.filter((item) => item.id != pick.id._id);
this.infoDetail.planeSelf = newPoint;
break;
case 'circle':
newPoint = this.infoDetail.circle.filter((item) => item.id != pick.id._id);
this.infoDetail.circle = newPoint;
break;
case 'sector':
newPoint = this.infoDetail.sector.filter((item) => item.id != pick.id._id);
this.infoDetail.sector = newPoint;
break;
default:
break;
}
this.viewer.entities.remove(pick.id);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
} else {
document.body.style = 'cursor: default;';
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}
/*******
* @function: function
* @description: 获取绘制对象
*/
backInfoDetail () {
return this.infoDetail;
}
3、页面代码
<template>
<div class="map">
<div class="flex-items search">
<el-radio-group v-model="btnType"
size="large"
@change="changeBtn">
<!-- <el-radio-button label="绘制点"
value="drawPoint" /> -->
<!-- <el-radio-button label="绘制矩形"
value="drawRectangle" /> -->
<el-radio-button label="绘制线段"
value="drawLine" />
<el-radio-button label="绘制多边形"
value="drawPlane" />
<el-radio-button label="绘制圆形"
value="drawCircle" />
<el-radio-button label="扇形"
value="drawSector" />
<el-radio-button label="移除绘制对象"
value="removeEntity" />
</el-radio-group>
<div class="flex-items">
<!--输入框形式-->
<!-- <el-input v-model="coordinates"
style="width: 240px"
:rows="2"
type="textarea"
placeholder="请输入坐标集" /> -->
<el-dropdown @command="addDraw">
<el-button type="primary"
style="margin-right: 10px;">
生成图形<el-icon class="el-icon--right"><arrow-down /></el-icon>
</el-button>
<template #dropdown>
<el-dropdown-menu>
<el-dropdown-item command="drawLineFromCoordinates">生成线段</el-dropdown-item>
<el-dropdown-item command="drawPlaneFromCoordinates">生成多边形</el-dropdown-item>
<el-dropdown-item command="drawCircleFromCoordinates">生成圆形</el-dropdown-item>
<el-dropdown-item command="drawSectorFromCoordinates">生成扇形</el-dropdown-item>
</el-dropdown-menu>
</template>
</el-dropdown>
<el-button type="success"
@click="getData()">获取图形数据</el-button>
</div>
</div>
</div>
<el-dialog v-model="drawVisible"
title="生成图形"
width="680">
<template v-if="drawInfo.key=='drawCircleFromCoordinates'">
<el-row :gutter="20">
<el-col :span="8">
<div>lng</div>
</el-col>
<el-col :span="8">
<div>lat</div>
</el-col>
<el-col :span="8">
<div>半径(米)</div>
</el-col>
<div class="flex-items draw-item"
v-for="(item,index) in drawInfo.coordinates"
:key="index">
<el-col :span="8">
<el-input v-model="item.lng"
placeholder="请输入lng" />
</el-col>
<el-col :span="8">
<el-input v-model="item.lat"
placeholder="请输入lat" /> </el-col>
<el-col :span="8">
<el-input v-model="item.radius"
placeholder="请输入半径">
</el-input>
</el-col>
</div>
</el-row>
</template>
<template v-else-if="drawInfo.key=='drawSectorFromCoordinates'">
<el-row :gutter="20">
<el-col :span="6">
<div>lng</div>
</el-col>
<el-col :span="6">
<div>lat</div>
</el-col>
<el-col :span="4">
<div>半径(米)</div>
</el-col>
<el-col :span="4">
<div>起始角度</div>
</el-col>
<el-col :span="4">
<div>结束角度</div>
</el-col>
<div class="flex-items draw-item"
v-for="(item,index) in drawInfo.coordinates"
:key="index">
<el-col :span="6">
<el-input v-model="item.lng"
placeholder="请输入lng" />
</el-col>
<el-col :span="6">
<el-input v-model="item.lat"
placeholder="请输入lat" />
</el-col>
<el-col :span="4">
<el-input v-model="item.radius"
placeholder="请输入半径">
</el-input>
</el-col>
<el-col :span="4">
<el-input v-model="item.startAngle"
placeholder="请输入">
</el-input>
</el-col>
<el-col :span="4">
<el-input v-model="item.endAngle"
placeholder="请输入">
</el-input>
</el-col>
</div>
</el-row>
</template>
<template v-else>
<el-row :gutter="20">
<el-col :span="10">
<div>lng</div>
</el-col>
<el-col :span="10">
<div>lat</div>
</el-col>
<el-col :span="4">
<div>操作</div>
</el-col>
<div class="flex-items draw-item"
v-for="(item,index) in drawInfo.coordinates"
:key="index">
<el-col :span="10">
<el-input v-model="item.lng"
placeholder="请输入lng" />
</el-col>
<el-col :span="10">
<el-input v-model="item.lat"
placeholder="请输入lat" />
</el-col>
<el-col :span="4">
<el-button type="danger"
circle
v-if="drawInfo.coordinates.length>1"
@click="delItem(index)">
<el-icon>
<delete />
</el-icon>
</el-button>
</el-col>
</div>
</el-row>
<el-button type="primary"
@click="addItem">添加坐标</el-button>
</template>
<template #footer>
<div class="dialog-footer">
<el-button @click="drawVisible = false">取消</el-button>
<el-button type="primary"
@click="createDraw()">
确认生成
</el-button>
</div>
</template>
</el-dialog>
<div id="cesiumContainer"
ref="cesiumContainer"></div>
</template>
<script>
import { onMounted, ref } from 'vue'
import * as Cesium from 'cesium'
import Draw from './Draw' // 确保路径正确
import { ArrowDown, Delete } from '@element-plus/icons-vue'
import { ElMessage } from 'element-plus'
export default {
name: 'CesiumMap',
setup() {
const btnType = ref('')
const cesiumContainer = ref(null)
const coordinates = ref('')
const drawVisible = ref(false)
const drawInfo = ref({
key: '',
coordinates: [
{ lng: '', lat: '', radius: 0, startAngle: 0, endAngle: 90 },
],
center: [],
radius: 0,
})
let viewer
let draw
onMounted(() => {
// 初始化地球
Cesium.Ion.defaultAccessToken = '注册的token'
viewer = new Cesium.Viewer(cesiumContainer.value, {
geocoder: true,
homeButton: true, //是否显示首页位置工具
sceneModePicker: true, //是否显示视角模式切换工具
baseLayerPicker: true, //是否显示默认图层选择工具
navigationHelpButton: false, //是否显示导航帮助工具
animation: false, //是否显示动画工具
timeline: false, //是否显示时间轴工具
fullscreenButton: true, //是否显示全屏按钮工具
})
//初始化绘制方法
draw = new Draw(viewer, {
borderColor: Cesium.Color.RED,
borderWidth: 2,
material: Cesium.Color.BLUE.withAlpha(0.5),
})
})
//点击不同的类型按钮,触发不同的绘制效果
function changeBtn(val) {
draw[val]()
}
//获取绘制的图形信息
function getData() {
console.log(draw.backInfoDetail())
}
//添加生成图形中的坐标对象
const addItem = () => {
drawInfo.value.coordinates.push({
lng: '',
lat: '',
radius: 0,
startAngle: 0,
endAngle: 90,
})
}
//删除生成图形中的坐标对象
const delItem = (index) => {
drawInfo.value.coordinates.splice(index, 1)
}
//弹窗形式,逐个添加坐标对象
const addDraw = (command) => {
drawVisible.value = true
drawInfo.value.key = command
drawInfo.value.coordinates = [
{ lng: '', lat: '', radius: 0, startAngle: 0, endAngle: 90 },
]
}
//生成图形
const createDraw = () => {
let isEmpty = false
drawInfo.value.coordinates.forEach((item) => {
if (item.lng === '' || item.lat === '') {
isEmpty = true
}
})
if (isEmpty) {
ElMessage({
message: '坐标内容不能为空!',
type: 'warning',
})
return
}
if (drawInfo.value.key == 'drawCircleFromCoordinates') {
draw[drawInfo.value.key](
[
Number(drawInfo.value.coordinates[0].lng),
Number(drawInfo.value.coordinates[0].lat),
],
Number(drawInfo.value.coordinates[0].radius)
)
} else if (drawInfo.value.key == 'drawSectorFromCoordinates') {
const startAngle = Cesium.Math.toRadians(
Number(drawInfo.value.coordinates[0].startAngle)
) // 起始角度,0度(东向)
const endAngle = Cesium.Math.toRadians(
Number(drawInfo.value.coordinates[0].endAngle)
) // 结束角度,90度(北向)
draw[drawInfo.value.key](
[
Number(drawInfo.value.coordinates[0].lng),
Number(drawInfo.value.coordinates[0].lat),
],
Number(drawInfo.value.coordinates[0].radius),
startAngle,
endAngle
)
} else {
let coordinates = []
drawInfo.value.coordinates.forEach((item) => {
coordinates.push([Number(item.lng), Number(item.lat)])
})
draw[drawInfo.value.key](coordinates)
}
drawVisible.value = false
}
//输入框形式,直接输入坐标集,生成图形
const handleCommand = (command) => {
if (command == 'drawCircleFromCoordinates') {
const center = [-84.89521051429298, 47.804323372822424] // 圆心的经纬度
const radius = 944831.9687022993 // 半径,单位:米
draw[command](center, radius)
} else if (command == 'drawSectorFromCoordinates') {
const startAngle = Cesium.Math.toRadians(Number(0)) // 起始角度,0度(东向)
const endAngle = Cesium.Math.toRadians(Number(90)) // 结束角度,90度(北向)
const center = [116.391193, 39.907776] // 圆心的经纬度
const radius = 944831.9687022993 // 半径,单位:米
draw[command](center, radius, startAngle, endAngle)
} else {
// 清理输入字符串
let cleanedInput = coordinates.value.trim()
if (!cleanedInput) {
ElMessage({
message: '坐标集不能为空!',
type: 'warning',
})
return
}
let parsedArray = null
// 修复尾随逗号
cleanedInput = cleanedInput.replace(/,\s*([}\]])/g, '$1')
// 处理其他常见问题
// 替换单引号为双引号
cleanedInput = cleanedInput.replace(/'/g, '"')
// 尝试解析 JSON
try {
// 解析字符串为数组
parsedArray = JSON.parse(cleanedInput)
draw[command](parsedArray)
} catch (error) {
ElMessage.error(error)
}
}
}
return {
handleCommand,
changeBtn,
getData,
addItem,
createDraw,
delItem,
addDraw,
cesiumContainer,
btnType,
coordinates,
drawInfo,
drawVisible,
}
},
}
</script>
<style scoped>
#cesiumContainer {
width: 100%;
height: calc(100vh - 120px);
}
.flex-items {
display: flex;
align-items: center;
}
.search {
margin-bottom: 10px;
justify-content: space-between;
}
.draw-item {
margin: 10px 0;
width: 100%;
}
</style>
4页面效果图
除了绘制图形之外,还新增了使用通过输入坐标点集合生成上述图形(WGS84 坐标) ,以及圆形、线段、多边形的拖拽,但是拖拽效果不是很理想,只能当做参考
5、完整的Draw.js
// Draw.js 绘制方法
import * as Cesium from 'cesium'
export default class Draw {
constructor(viewer, config) {
this.viewer = viewer;
this.config = config || {
borderColor: Cesium.Color.BLUE,
borderWidth: 2,
material: Cesium.Color.GREEN.withAlpha(0.5),
};
this.infoDetail = { point: [], line: [], rectangle: [], circle: [], planeSelf: [], sector: [] };
//判断是否右键销毁
this.isDestroy = false
//是否是销毁状态
this.isRemove = false
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
}
/*******
* @function: function
* @description: 绘制点
*/
drawPoint () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isDestroy = false
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((click) => {
let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
let id = new Date().getTime();
this.viewer.entities.add({
position: Cesium.Cartesian3.fromDegrees(lng, lat, 0),
name: 'point',
id: id,
point: {
color: this.config.material,
pixelSize: 12,
outlineColor: this.config.borderColor,
outlineWidth: this.config.borderWidth,
},
});
this.infoDetail.point.push({ id: id, position: [lng, lat] });
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
this.handler.setInputAction((click) => {
if (this.handler) {
this.isDestroy = true
this.handler.destroy();
//重新点击进行绘制
this.drawPoint()
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
/*******
* @function: function
* @description: 绘制矩形区域
*/
drawRectangle () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false
this.isDestroy = false
let westSouthEastNorth = [];
let id = null;
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((click) => {
let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
let lng1 = Cesium.Math.toDegrees(cartographic.longitude);
let lat1 = Cesium.Math.toDegrees(cartographic.latitude);
westSouthEastNorth = [lng1, lat1];
id = new Date().getTime();
if (westSouthEastNorth) {
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
let polygons = this.viewer.entities.add({
name: 'rectangle',
id: id,
polygon: {
hierarchy: new Cesium.CallbackProperty(function () {
return {
positions: Cesium.Cartesian3.fromDegreesArray(westSouthEastNorth),
};
}, false),
height: 0,
material: this.config.material,
fill: true,
show: true,
},
polyline: {
positions: new Cesium.CallbackProperty(function () { return Cesium.Cartesian3.fromDegreesArray(westSouthEastNorth); }, false),
material: this.config.borderColor,
width: this.config.borderWidth,
zIndex: 1,
},
});
this.handler.setInputAction((move) => {
let cartesian = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid, new Cesium.Cartographic());
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
westSouthEastNorth = [lng1, lat1, lng1, lat, lng, lat, lng, lat1, lng1, lat1];
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
//右键完成绘制
this.handler.setInputAction(() => {
if (this.handler) {
this.isDestroy = true
this.handler.destroy();
//重新点击进行绘制
this.drawRectangle()
}
this.infoDetail.rectangle.push({ id: id, position: westSouthEastNorth });
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
/*******
* @function: function
* @description: 绘制圆形区域,并计算面积和周长,同时支持拖动
*/
drawCircle() {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false;
this.isDestroy = false;
let id = null;
let radius = 0;
let lngLat = [];
let circleEntity = null;
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((click) => {
id = new Date().getTime();
let cartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
lngLat = [lng, lat];
circleEntity = this.viewer.entities.add({
position: new Cesium.CallbackProperty(function () { return Cesium.Cartesian3.fromDegrees(...lngLat, 0); }, false),
name: 'circle',
id: id,
ellipse: {
height: 0,
outline: true,
material: this.config.material,
outlineColor: this.config.borderColor,
outlineWidth: this.config.borderWidth,
semiMajorAxis: new Cesium.CallbackProperty(function () { return radius; }, false),
semiMinorAxis: new Cesium.CallbackProperty(function () { return radius; }, false),
},
});
this.handler.setInputAction((move) => {
let cartesian2 = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
radius = Cesium.Cartesian3.distance(cartesian, cartesian2);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 完成圆形绘制后,允许拖动
this.handler.setInputAction(() => {
// 计算面积(πr²)和周长(2πr)
let area = Math.PI * Math.pow(radius, 2); // 单位:平方米
let circumference = 2 * Math.PI * radius; // 单位:米
this.infoDetail.circle.push({
id: id,
center: lngLat,
radius: radius,
area: area, // 保存面积
circumference: circumference // 保存周长
});
if (this.handler) {
this.isDestroy = true;
this.handler.destroy();
this.drawCircle();
this.enableCircleDragging(circleEntity, id); // 启用拖动功能
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}
/*******
* @function: function
* @description: 绘制自定义区域,并计算面积,同时支持拖动
*/
drawPlane() {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false;
this.isDestroy = false;
let id = new Date().getTime();
let positions = [];
let codeInfo = [];
let polygon = new Cesium.PolygonHierarchy();
let _polygonEntity = new Cesium.Entity();
let polyObj = null;
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((movement) => {
let cartesian = this.viewer.camera.pickEllipsoid(movement.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
if (cartesian && cartesian.x) {
if (positions.length == 0) {
positions.push(cartesian.clone());
}
codeInfo.push([lng, lat]);
positions.push(cartesian.clone());
polygon.positions.push(cartesian.clone());
if (!polyObj) {
_polygonEntity.polyline = {
width: this.config.borderWidth,
material: this.config.borderColor,
clampToGround: false,
};
_polygonEntity.polyline.positions = new Cesium.CallbackProperty(function () {
return positions;
}, false);
_polygonEntity.polygon = {
hierarchy: new Cesium.CallbackProperty(function () {
return polygon;
}, false),
material: this.config.material,
clampToGround: false,
};
_polygonEntity.name = 'planeSelf';
_polygonEntity._id = id;
polyObj = this.viewer.entities.add(_polygonEntity);
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
this.handler.setInputAction((movement) => {
let cartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
if (positions.length >= 0) {
if (cartesian && cartesian.x) {
positions.pop();
positions.push(cartesian);
polygon.positions.pop();
polygon.positions.push(cartesian);
codeInfo.pop();
codeInfo.push([lng, lat]);
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 右键完成绘制
this.handler.setInputAction((movement) => {
// 计算多边形面积
let area = this.calculatePolygonArea(positions);
this.infoDetail.planeSelf.push({
id: id,
positions: codeInfo,
area: area // 保存面积
});
if (this.handler) {
this.isDestroy = true;
this.handler.destroy();
this.drawPlane();
this.enablePolygonDragging(polyObj, id); // 启用拖动功能
}
positions.push(positions[0]);
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
/*******
* @function: function
* @description: 绘制线段并计算其长度
*/
drawLine () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false;
this.isDestroy = false;
let id = null;
let positions = [];
let codeInfo = [];
let polyObj = null;
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((movement) => {
id = new Date().getTime();
let cartesian = this.viewer.camera.pickEllipsoid(movement.position, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
if (cartesian && cartesian.x) {
if (positions.length == 0) {
positions.push(cartesian.clone());
}
codeInfo.push([lng, lat]);
positions.push(cartesian.clone());
if (!polyObj) {
polyObj = this.viewer.entities.add({
polyline: {
positions: new Cesium.CallbackProperty(() => positions, false),
width: this.config.borderWidth,
material: this.config.borderColor,
clampToGround: false,
},
name: 'line',
id: id,
});
}
}
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
this.handler.setInputAction((movement) => {
let cartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
let cartographic = Cesium.Cartographic.fromCartesian(cartesian, this.viewer.scene.globe.ellipsoid);
let lng = Cesium.Math.toDegrees(cartographic.longitude);
let lat = Cesium.Math.toDegrees(cartographic.latitude);
if (positions.length >= 0) {
if (cartesian && cartesian.x) {
positions.pop();
positions.push(cartesian);
codeInfo.pop();
codeInfo.push([lng, lat]);
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 右键完成绘制
this.handler.setInputAction(() => {
// 计算线段长度
let length = this.calculateLineLength(positions);
this.infoDetail.line.push({
id: id,
positions: codeInfo,
length: length // 保存线段长度
});
if (this.handler) {
this.isDestroy = true;
this.handler.destroy();
this.drawLine(); // 重新启用绘制功能
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
/*******
* @function: function
* @description: 计算扇形的面积
*/
calculateSectorArea (radius, startAngle, endAngle) {
let angleDiff = endAngle - startAngle;
if (angleDiff < 0) {
angleDiff += 2 * Math.PI; // 确保角度差为正
}
return 0.5 * Math.pow(radius, 2) * angleDiff;
}
/*******
* @function: function
* @description: 计算扇形的弧长
*/
calculateArcLength (radius, startAngle, endAngle) {
let angleDiff = endAngle - startAngle;
return radius * angleDiff; // 单位:米
}
/*******
* @function: function
* @description: 计算扇形的边界点
*/
computeSectorPositions (center, radius, startAngle, endAngle) {
const positions = [];
const numPoints = 50; // 调整为更平滑或更粗糙的扇形
const angleDiff = endAngle - startAngle;
for (let i = 0; i <= numPoints; i++) {
const angle = startAngle + (i * angleDiff) / numPoints;
const x = center.x + radius * Math.cos(angle);
const y = center.y + radius * Math.sin(angle);
positions.push(new Cesium.Cartesian3(x, y, center.z));
}
positions.push(center);
return positions;
}
/*******
* @function: function
* @description: 绘制扇形,并计算面积和弧长
*/
drawSector () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = false;
this.isDestroy = false;
let id = null;
let center = null;
let radius = 0;
let startAngle = 0;
let endAngle = 0;
let positions = [];
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
// 第一次点击确定中心点
this.handler.setInputAction((click) => {
id = new Date().getTime();
center = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
if (!center) return;
const cartographic = Cesium.Cartographic.fromCartesian(center);
const lng = Cesium.Math.toDegrees(cartographic.longitude);
const lat = Cesium.Math.toDegrees(cartographic.latitude);
let entity = this.viewer.entities.add({
id: id,
position: center,
polygon: {
hierarchy: new Cesium.CallbackProperty(() => new Cesium.PolygonHierarchy(positions), false),
material: this.config.material,
outline: true,
outlineColor: this.config.borderColor,
outlineWidth: this.config.borderWidth,
},
});
this.handler.setInputAction((move) => {
const movePosition = this.viewer.camera.pickEllipsoid(move.endPosition, this.viewer.scene.globe.ellipsoid);
if (!movePosition) return;
radius = Cesium.Cartesian3.distance(center, movePosition);
const cartographicMove = Cesium.Cartographic.fromCartesian(movePosition);
const lngMove = Cesium.Math.toDegrees(cartographicMove.longitude);
const latMove = Cesium.Math.toDegrees(cartographicMove.latitude);
endAngle = Math.atan2(latMove - lat, lngMove - lng);
// 动态计算扇形边界点
positions = this.computeSectorPositions(center, radius, startAngle, endAngle);
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
// 设置起始角度
this.handler.setInputAction((click) => {
const startCartesian = this.viewer.camera.pickEllipsoid(click.position, this.viewer.scene.globe.ellipsoid);
if (!startCartesian) return;
const cartographicStart = Cesium.Cartographic.fromCartesian(startCartesian);
const lngStart = Cesium.Math.toDegrees(cartographicStart.longitude);
const latStart = Cesium.Math.toDegrees(cartographicStart.latitude);
startAngle = Math.atan2(latStart - lat, lngStart - lng);
this.handler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
// 右键完成绘制
this.handler.setInputAction(() => {
// 计算扇形的面积和弧长
let area = this.calculateSectorArea(radius, startAngle, endAngle);
let arcLength = this.calculateArcLength(radius, startAngle, endAngle);
this.infoDetail.sector.push({
id: id,
center: Cesium.Cartographic.fromCartesian(center),
radius: radius,
startAngle: startAngle,
endAngle: endAngle,
area: area, // 保存面积
arcLength: arcLength // 保存弧长
});
if (this.handler) {
this.isDestroy = true;
this.handler.destroy();
// 重新启用绘制功能
this.drawSector();
}
}, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}
/*******
* @function: function
* @description: 通过坐标数组生成线段并计算长度
*/
drawLineFromCoordinates (coordinates) {
let id = new Date().getTime();
let positions = coordinates.map(coord => {
return Cesium.Cartesian3.fromDegrees(coord[0], coord[1]);
});
// 计算线段长度
let length = this.calculateLineLength(positions);
this.infoDetail.line.push({
id: id,
positions: coordinates,
length: length // 保存线段长度
});
let _polygonEntity = new Cesium.Entity();
_polygonEntity.polyline = {
width: this.config.borderWidth,
material: this.config.borderColor,
clampToGround: false,
positions: positions,
};
_polygonEntity.name = 'line';
_polygonEntity._id = id;
this.viewer.entities.add(_polygonEntity);
} /*******
* @function: function
* @description: 计算线段的总长度
*/
calculateLineLength (positions) {
let totalLength = 0;
for (let i = 0; i < positions.length - 1; i++) {
let startCartographic = Cesium.Cartographic.fromCartesian(positions[i]);
let endCartographic = Cesium.Cartographic.fromCartesian(positions[i + 1]);
let startPos = Cesium.Cartesian3.fromRadians(
startCartographic.longitude,
startCartographic.latitude
);
let endPos = Cesium.Cartesian3.fromRadians(
endCartographic.longitude,
endCartographic.latitude
);
let segmentLength = Cesium.Cartesian3.distance(startPos, endPos);
totalLength += segmentLength;
}
return totalLength; // 返回线段总长度,单位:米
}
/*******
/*******
* @function: function
* @description: 通过坐标数组生成自定义区域,并计算面积
*/
drawPlaneFromCoordinates (coordinates) {
let id = new Date().getTime();
let positions = coordinates.map(coord => {
return Cesium.Cartesian3.fromDegrees(coord[0], coord[1]);
});
// 关闭区域,用第一个点作为最后一个点
positions.push(positions[0]);
// 计算多边形面积
let area = this.calculatePolygonArea(positions);
this.infoDetail.planeSelf.push({
id: id,
positions: coordinates,
area: area // 保存面积
});
let polygonHierarchy = new Cesium.PolygonHierarchy(positions);
let _polygonEntity = new Cesium.Entity();
_polygonEntity.polyline = {
width: this.config.borderWidth,
material: this.config.borderColor,
clampToGround: false,
positions: positions,
};
_polygonEntity.polygon = {
hierarchy: polygonHierarchy,
material: this.config.material,
clampToGround: false,
};
_polygonEntity.name = 'planeSelf';
_polygonEntity._id = id;
this.viewer.entities.add(_polygonEntity);
this.enablePolygonDragging(_polygonEntity, id); // 启用拖动功能
}
/*******
* @function: function
* @description: 启用多边形的拖动功能
*/
enablePolygonDragging(polygonEntity, id) {
this.dragHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
let isDragging = false;
let originalPositions = null;
this.dragHandler.setInputAction((click) => {
const pickedObject = this.viewer.scene.pick(click.position);
if (Cesium.defined(pickedObject) && pickedObject.id === polygonEntity) {
isDragging = true;
originalPositions = pickedObject.id.polygon.hierarchy.getValue(Cesium.JulianDate.now()).positions;
}
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
this.dragHandler.setInputAction((movement) => {
if (isDragging && polygonEntity) {
const newCartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
if (newCartesian) {
const deltaX = newCartesian.x - originalPositions[0].x;
const deltaY = newCartesian.y - originalPositions[0].y;
const deltaZ = newCartesian.z - originalPositions[0].z;
const newPositions = originalPositions.map((position) => {
return new Cesium.Cartesian3(
position.x + deltaX,
position.y + deltaY,
position.z + deltaZ
);
});
// 更新多边形和折线的位置信息
polygonEntity.polygon.hierarchy = new Cesium.CallbackProperty(() => {
return new Cesium.PolygonHierarchy(newPositions);
}, false);
polygonEntity.polyline.positions = new Cesium.CallbackProperty(() => {
return newPositions;
}, false);
// 实时更新 infoDetail 中多边形的位置
const polygonInfo = this.infoDetail.planeSelf.find(plane => plane.id === id);
if (polygonInfo) {
polygonInfo.positions = newPositions.map(pos => {
const cartographic = Cesium.Cartographic.fromCartesian(pos);
return [
Cesium.Math.toDegrees(cartographic.longitude),
Cesium.Math.toDegrees(cartographic.latitude)
];
});
}
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
this.dragHandler.setInputAction(() => {
isDragging = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);
}
/*******
* @function: function
* @description: 启用圆形的拖动功能
*/
enableCircleDragging(circleEntity, id) {
this.dragHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
let isDragging = false;
let originalPosition = null;
this.dragHandler.setInputAction((click) => {
const pickedObject = this.viewer.scene.pick(click.position);
if (Cesium.defined(pickedObject) && pickedObject.id === circleEntity) {
isDragging = true;
originalPosition = pickedObject.id.position.getValue(Cesium.JulianDate.now());
}
}, Cesium.ScreenSpaceEventType.LEFT_DOWN);
this.dragHandler.setInputAction((movement) => {
if (isDragging && circleEntity) {
const newCartesian = this.viewer.camera.pickEllipsoid(movement.endPosition, this.viewer.scene.globe.ellipsoid);
if (newCartesian) {
circleEntity.position = new Cesium.CallbackProperty(() => newCartesian, false);
const newCartographic = Cesium.Cartographic.fromCartesian(newCartesian);
const newLngLat = [
Cesium.Math.toDegrees(newCartographic.longitude),
Cesium.Math.toDegrees(newCartographic.latitude)
];
// 实时更新 infoDetail 中圆形的位置
const circleInfo = this.infoDetail.circle.find(circle => circle.id === id);
if (circleInfo) {
circleInfo.center = newLngLat;
}
}
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
this.dragHandler.setInputAction(() => {
if (isDragging && circleEntity) {
const finalPosition = circleEntity.position.getValue(Cesium.JulianDate.now());
const finalCartographic = Cesium.Cartographic.fromCartesian(finalPosition);
const finalLngLat = [
Cesium.Math.toDegrees(finalCartographic.longitude),
Cesium.Math.toDegrees(finalCartographic.latitude)
];
// 结束拖动后,更新 infoDetail 中的圆心坐标
const circleInfo = this.infoDetail.circle.find(circle => circle.id === id);
if (circleInfo) {
circleInfo.center = finalLngLat;
}
}
isDragging = false;
}, Cesium.ScreenSpaceEventType.LEFT_UP);
}
/*******
* @function: function
* @description: 通过坐标和半径生成圆形区域,并计算面积和周长
*/
drawCircleFromCoordinates (center, radius) {
let id = new Date().getTime();
// 将中心点转换为 Cartesian3 坐标
let centerCartesian = Cesium.Cartesian3.fromDegrees(center[0], center[1]);
// 计算面积(πr²)和周长(2πr)
let area = Math.PI * Math.pow(radius, 2); // 单位:平方米
let circumference = 2 * Math.PI * radius; // 单位:米
this.infoDetail.circle.push({
id: id,
center: center,
radius: radius,
area: area, // 保存面积
circumference: circumference // 保存周长
});
let entity = this.viewer.entities.add({
position: centerCartesian,
name: 'circle',
id: id,
ellipse: {
semiMajorAxis: radius,
semiMinorAxis: radius,
height: 0,
outline: true,
material: this.config.material,
outlineColor: this.config.borderColor,
outlineWidth: this.config.borderWidth,
},
});
// 启用拖动功能
this.enableCircleDragging(entity, id);
}
/*******
* @function: function
* @description: 通过坐标、半径、起始角度和结束角度生成扇形,并计算面积和弧长
*/
drawSectorFromCoordinates (center, radius, startAngle, endAngle) {
let id = new Date().getTime();
// 将中心点转换为 Cartesian3 坐标
let centerCartesian = Cesium.Cartesian3.fromDegrees(center[0], center[1]);
// 计算扇形的边界点
let positions = this.computeSectorPositions(centerCartesian, radius, startAngle, endAngle);
// 计算扇形的面积和弧长
let area = this.calculateSectorArea(radius, startAngle, endAngle);
let arcLength = this.calculateArcLength(radius, startAngle, endAngle);
this.infoDetail.sector.push({
id: id,
center: center,
radius: radius,
startAngle: startAngle,
endAngle: endAngle,
area: area, // 保存面积
arcLength: arcLength // 保存弧长
});
let entity = this.viewer.entities.add({
polygon: {
hierarchy: new Cesium.PolygonHierarchy(positions),
material: this.config.material,
outline: true,
outlineColor: this.config.borderColor,
outlineWidth: this.config.borderWidth,
},
name: 'sector',
id: id,
});
}
/*******
* @function: function
* @description: 移除绘制对象
*/
removeEntity () {
if (this.handler && !this.isDestroy) {
this.handler.destroy();
}
this.isRemove = true
this.handler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
this.handler.setInputAction((move) => {
let pick = this.viewer.scene.pick(move.endPosition);
if (pick && pick.id && pick.id.id && this.isRemove) {
document.body.style.cursor = 'pointer';
this.handler.setInputAction((click) => {
let newPoint;
switch (pick.id.name) {
case 'point':
newPoint = this.infoDetail.point.filter((item) => item.id != pick.id._id);
this.infoDetail.point = newPoint;
break;
case 'line':
newPoint = this.infoDetail.line.filter((item) => item.id != pick.id._id);
this.infoDetail.line = newPoint;
break;
case 'rectangle':
newPoint = this.infoDetail.rectangle.filter((item) => item.id != pick.id._id);
this.infoDetail.rectangle = newPoint;
break;
case 'planeSelf':
newPoint = this.infoDetail.planeSelf.filter((item) => item.id != pick.id._id);
this.infoDetail.planeSelf = newPoint;
break;
case 'circle':
newPoint = this.infoDetail.circle.filter((item) => item.id != pick.id._id);
this.infoDetail.circle = newPoint;
break;
case 'sector':
newPoint = this.infoDetail.sector.filter((item) => item.id != pick.id._id);
this.infoDetail.sector = newPoint;
break;
default:
break;
}
this.viewer.entities.remove(pick.id);
}, Cesium.ScreenSpaceEventType.LEFT_CLICK);
} else {
document.body.style = 'cursor: default;';
}
}, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}
backInfoDetail () {
return this.infoDetail;
}
}