npm install vue-cesium --save
main.js
import VueCesium from 'vue-cesium'
app.use(VueCesium).mount('#app')
页面中使用
<template>
<div class="taskArea">
<vc-viewer ref="vierDraw" @ready="onViewerReady" accessToken='替换为自己的cesiumkey' geocoder sceneModePicker>
<!-- 修改定位 和 位置偏移 -->
<vc-drawings
ref="drawingsRef"
position="bottom-left"
v-if="isCanvasShow"
:main-fab-opts="mainFabOpts"
:offset="[10, 30]"
@draw-evt="drawEvt"
@active-evt="activeEvt"
@editor-evt="editorEvt"
@mouse-evt="mouseEvt"
@clear-evt="clearEvt"
@ready="drawingsReadyDefault"
@drawings-closed="onDrawingsClosed"
:pin-drawing-opts="pinDrawingOpts"
:rectangle-drawing-opts="rectangleDrawingOpts"
:polygon-drawing-opts="polygonDrawingOpts"
></vc-drawings>
<!-- <vc-primitive-tileset
url="https://zouyaoji.top/vue-cesium/SampleData/Cesium3DTiles/Tilesets/dayanta/tileset.json"
@ready-promise="onTilesetReady"
></vc-primitive-tileset> -->
<vc-layer-imagery>
<vc-imagery-provider-tianditu map-style="img_c" :maximum-level="17" token="436ce7e50d27eede2f2929307e6b33c0"></vc-imagery-provider-tianditu>
</vc-layer-imagery>
<div v-for="item in rectangleDrawingOptsStorage.preRenderDatas">
<vc-entity v-if="item.type == 'Polygon'">
<vc-graphics-polygon ref="polygon1" :hierarchy="item.arrayList" material="rgba(255, 255, 0, 0.5)"></vc-graphics-polygon>
<!-- <vc-graphics-polyline v-else-if="item.type == 'polyline'" :width="2" :clampToGround="false" :hierarchy="item.arrayList" material="rgba(255, 255, 0, 0.5)"></vc-graphics-polyline> -->
</vc-entity>
<div v-if="item.type == 'Point'">
<vc-entity v-if="item.typeName == 'Point'" :position="item.arrayList">
<vc-graphics-point ref="point1" color="rgba(255, 255, 0,1)" :pixel-size="8"></vc-graphics-point>
</vc-entity>
<vc-entity v-else-if="item.typeName == 'pin'" :position="item.arrayList">
<vc-graphics-billboard :image="require('@/assets/airport.png')" />
</vc-entity>
<vc-entity v-else-if="item.typeName == 'circle'" :position="item.arrayList">
<vc-graphics-ellipse :semiMinorAxis="item.radius" :semiMajorAxis="item.radius" :height="0.0" material="rgba(255, 255, 0,0.5)" :outline="false"></vc-graphics-ellipse>
</vc-entity>
</div>
<vc-entity v-if="item.type == 'LineString'">
<vc-graphics-polyline :positions="item.arrayList" material="rgba(255, 255, 0,1)"></vc-graphics-polyline>
</vc-entity>
</div>
<vc-layer-imagery>
<vc-imagery-provider-tianditu :map-style="mapSty" token="436ce7e50d27eede2f2929307e6b33c0"></vc-imagery-provider-tianditu>
</vc-layer-imagery>
</vc-viewer>
<a-button style="position: absolute;top: 10px;left: 20px;z-index: 9;background: rgba(0,0,0,.5);color: #fff;" @click="toggleLabels">
{{ isShowGeoName ? $t('message.hidePlaceName') : $t('message.placeName') }}
</a-button>
<div class="btnContainer">
<div>
<a-list size="small" style="color: #fff;text-align: center;background-color: rgba(0,0,0,.4);min-width: 200px;overflow-y:scroll;max-height: 230px;" :locale="{emptyText: ' '}" bordered :data-source="RegionList">
<template #renderItem="{ item,index }">
<a-list-item style="color: #fff;" :class="{ 'selected-item': isSelected(index) }">
<a-tooltip placement="leftTop">
<template #title>
<div>
<a-button type="link" @click="clearAllDrawn(index,item)" danger>{{ $t('message.delete') }}</a-button>
<a-button type="link" @click="item.isResetName = true" danger>{{ $t('message.Rename') }}</a-button>
</div>
</template>
<p style="cursor: pointer;text-align: center;width: 100%;" @click="checkAlist(index)" v-if="!item.isResetName">{{ item.name }}</p>
<div v-else>
<a-input v-model:value="item.name" style="width: 70%;" />
<a-button type="link" @click="addAreaList(item,index)">{{ $t('message.ok') }}</a-button>
</div>
</a-tooltip>
</a-list-item>
</template>
<template #header>
<div>{{ $t('message.areaList') }}</div>
</template>
<template v-if="!isCanvasShow" #footer>
<div style="font-size: 20px;font-weight: 600;cursor: pointer;" @click="mapDialog = true">+</div>
</template>
</a-list>
</div>
</div>
<a-modal v-model:visible="mapDialog" title="" ok-text="确认" cancel-text="取消" @ok="clickAddArea">
<span>选择区域类型:</span>
<a-select v-model:value="checkAregion" style="width: 200px;">
<a-select-option value="1">任务区</a-select-option>
<!-- <a-select-option value="2">结束区</a-select-option> -->
</a-select>
</a-modal>
</div>
</template>
<script setup>
import { ConsoleSqlOutlined } from "@ant-design/icons-vue"
import {ref,onMounted,nextTick,reactive } from "vue"
import { postAddArea,getAreaList,delArea } from "../utils/api.js";
import { message } from "ant-design-vue";
let checkAregion = ref('1')
let isCanvasShow = ref(false)
let selectedRegionIndices = reactive([]);
const vierDraw = ref(null)
const drawingsRef = ref(null)
let mapDialog = ref(false)
const RegionList = reactive([])
let isShowDraw = ref(false)
let isShowGeoName = ref(false)
const attributes = ref(null)
let rectangleDrawingOpts = reactive({
preRenderDatas: [
[
// [108.957758283111, 34.21988950430172, 2.0019682259091733], [108.95827467484429, 34.21940420082603, 2.0019682256560865]
]
]
});
let mapSty = ref('img_c')
// 响应式数据
const addTerrain = ref(false);
const editable = ref(false);
const clampToGround = ref(false);
const mainFabOpts = reactive({
direction: 'right'
});
const isSelected = (index) => {
return selectedRegionIndices.includes(index);
};
const checkAlist = (index) => {
const selectedIndex = selectedRegionIndices.indexOf(index);
if (selectedIndex > -1) {
selectedRegionIndices.splice(selectedIndex, 1); // Deselect the item
rectangleDrawingOptsStorage.preRenderDatas.splice(selectedIndex, 1);
} else {
selectedRegionIndices.push(index); // Select the item
if(RegionList[index].type == 'LineString'){ //判断是线段
let postionasdas = RegionList[index].geometry.coordinates
let ooss = postionasdas.map(pos => Cesium.Cartesian3.fromDegrees(pos[0], pos[1]))
moveToPolygon(ooss)
rectangleDrawingOptsStorage.preRenderDatas.push({
arrayList: [],
type: RegionList[index].type,
indexi:index,
})
//改变数据格式后才可以绘制出线段
for (let ii = 0; ii < RegionList[index].geometry.coordinates.length; ii++) {
const element = RegionList[index].geometry.coordinates[ii];
rectangleDrawingOptsStorage.preRenderDatas[rectangleDrawingOptsStorage.preRenderDatas.length - 1].arrayList.push({
lng:element[0],
lat:element[1]
})
}
}else if(RegionList[index].type == 'Point'){//判断是不是绘制的点
//判断是图标点还是颜色点,pin为图标点
if(RegionList[index].properties && RegionList[index].properties.type && RegionList[index].properties.type == 'pin'){
rectangleDrawingOptsStorage.preRenderDatas.push({
arrayList: RegionList[index].geometry.coordinates,
type: RegionList[index].type,
indexi:index,
typeName: 'pin'
})
}else if(RegionList[index].properties && RegionList[index].properties.type && RegionList[index].properties.type == 'circle'){
rectangleDrawingOptsStorage.preRenderDatas.push({
arrayList: RegionList[index].geometry.coordinates,
radius: RegionList[index].properties.radius,
type: RegionList[index].type,
indexi:index,
typeName: 'circle'
})
}else{
rectangleDrawingOptsStorage.preRenderDatas.push({
arrayList: RegionList[index].geometry.coordinates,
type: RegionList[index].type,
indexi:index,
typeName: 'Point'
})
}
let postionasda = RegionList[index].geometry.coordinates
let pointOss = Cesium.Cartesian3.fromDegrees(postionasda[0],postionasda[1],3000)
moveTo(pointOss)
}else{
// delete RegionList[index].geometry.coordinates[0][RegionList[index].geometry.coordinates[0].length -1]
rectangleDrawingOptsStorage.preRenderDatas.push({
arrayList: RegionList[index].geometry.coordinates[0],
indexi:index,
type: RegionList[index].type
})
let postionasda = RegionList[index].geometry.coordinates[0]
let oos = postionasda.map(pos => Cesium.Cartesian3.fromDegrees(pos[0], pos[1]))
moveToPolygon(oos)
}
// polygonDrawingOpts.preRenderDatas.push(RegionList[index].geometry.coordinates[0])
// drawingsRef.value.getDrawingActionInstances()[4].cmpRef.value.renderDatas.value.push([
// 'position',Cesium.Cartesian3.fromDegreesArrayHeights(rectangleDrawingOptsStorage.preRenderDatas[index][0].flat())
// ])
}
// console.log(rectangleDrawingOptsStorage.preRenderDatas,'哈哈哈哈哈')
// console.log(Cesium.Cartesian3.fromDegreesArrayHeights(rectangleDrawingOptsStorage.preRenderDatas[index][0].flat()),'阿萨大1231大')
// console.log(rectangleDrawingOptsStorage.preRenderDatas,'阿萨大大')
// drawingsRef.value.reload()
// onViewerReady()
};
//点击切换是否显示地点名称
const toggleLabels = () => {
isShowGeoName.value = !isShowGeoName.value;
if (isShowGeoName.value) {
mapSty.value = 'cva_c'
} else {
mapSty.value = 'img_c'
}
}
let rectangleDrawingOptsStorage = reactive({
preRenderDatas: [
]
});
const pinDrawingOpts = reactive({
billboardOpts: {
image: require('@/assets/airport.png'),
onClick(e) {
console.log(e);
}
},
labelOpts: {
text: '图标点',
pixelOffset: [0, -60],
onClick(e) {
console.log(e);
}
}
});
const polygonDrawingOpts = reactive({
preRenderDatas: []
});
let restoreCursorMove = ref('')
let drawing = ref('')
//绘制图形事件
const drawEvt = (e, viewer) => {
const restoreCursor = getComputedStyle(viewer.canvas).cursor;
vierDraw.value = viewer
if (e.finished) { //状态为绘制结束
if (e.type === 'move') {
viewer.canvas.setAttribute('style', `cursor: ${restoreCursorMove.value}`);
}
drawing.value = false;
// rectangleDrawingOptsStorage.preRenderDatas
// rectangleDrawingOptsStorage.preRenderDatas.push([e.positionsDegreesArray])
if(e.name != 'point' && e.name != 'pin'){ //e.name是绘制的类型
// rectangleDrawingOptsStorage.preRenderDatas.push({
// arrayList:e.positionsDegreesArray,
// type:e.name
// })
if(e.name == 'polygon'){ //因为传递参数的时候用的是GEOjson的格式,所以要把第一个字母大写
e.name = 'Polygon'
RegionList.push({name:'',isResetName:true,type:e.name,geometry:{coordinates:[e.positionsDegreesArray]}})
}else if(e.name == 'polyline'){ //面
e.name = 'LineString'
RegionList.push({name:'',isResetName:true,type:e.name,geometry:{coordinates:e.positionsDegreesArray}})
}else if(e.name == 'circle'){ //圆形
RegionList.push({name:'',isResetName:true,type:e.name,geometry:{coordinates:e.positionsDegreesArray}})
}else if(e.name == 'rectangle'){ //矩形,进页面初始绘制矩形获取不到polygonPositionsDegreesArray,绘制矩形要清除一次后再绘制
RegionList.push({name:'',isResetName:true,type:e.name,geometry:{coordinates: [e.polygonPositionsDegreesArray]}})
}else if(e.name == "regular"){//正多边形
RegionList.push({name:'',isResetName:true,type:e.name,geometry:{coordinates: [e.polygonPositionsDegreesArray]}})
}
}else{
if(e.name == 'pin'){
RegionList.push({name:'',isResetName:true,type:e.name,geometry:{coordinates:e.positionDegrees}})
}else if(e.name == 'point'){
e.name = 'Point'
RegionList.push({name:'',isResetName:true,type:e.name,geometry:{coordinates:e.positionDegrees}})
}
}
// console.log(drawingsRef.value.getDrawingActionInstances()[3].cmpRef.value.renderDatas,'adada阿三大苏asdasd打')
} else {
drawing.value = true;
if (e.type === 'move') {
viewer.canvas.setAttribute('style', 'cursor: move');
}
if (e.type === 'new') {
viewer.canvas.setAttribute('style', 'cursor: crosshair');
}
}
};
const changeData = (list) => {
const centerDegrees = list[0]
const pointDegrees = list[1]
const center = Cesium.Cartesian3.fromDegrees(...centerDegrees)
const point = Cesium.Cartesian3.fromDegrees(...pointDegrees)
const radius = Cesium.Cartesian3.distance(center, point)
return radius
}
const convertToGeoJSON = (rectangleDrawingOptsStorage,index) => {
let coordinates = RegionList[index].geometry.coordinates;
let geoJSON = {
"type": "Feature",
"properties": {
name:RegionList[index].name,
},
// bbox: [0],
// crs: {
// properties: {},
// type: "name"
// },
geometry: {
},
};
const geometry = {
type: RegionList[index].type,
coordinates: coordinates
};
if(geometry.type == 'regular'){
geometry.type = 'Polygon'
RegionList[index].type = 'Polygon'
}
if(geometry.type == 'Polygon'){
coordinates[0].push(coordinates[0][0])
}
if(geometry.type == 'rectangle'){
geometry.type = 'Polygon'
RegionList[index].type = 'Polygon'
}
//type=pin是图标点,GeoJSON里面没有图标点格式,所以properties加了一个type
if(geometry.type == 'pin'){
geometry.type = 'Point'
geoJSON.properties.type = 'pin'
RegionList[index].properties = {
type : 'pin'
}
RegionList[index].type = 'Point'
}
if(geometry.type == 'circle'){
geometry.type = 'Point'
RegionList[index].type = 'Point'
geoJSON.properties.type = 'circle'
RegionList[index].properties = {
type : 'circle'
}
geometry.coordinates = RegionList[index].geometry.coordinates[0]
geoJSON.properties.radius = changeData(RegionList[index].geometry.coordinates)
}
geoJSON.geometry = geometry;
return geoJSON;
};
const addAreaList = (item,index) => {
const geoJSONData = convertToGeoJSON(RegionList,index);
if(item.name == ''){
message.error('请输入名称!')
return
}
postAddArea(geoJSONData).then(res => {
if(res.code == 200){
message.success(res.data)
item.isResetName = false
// selectedRegionIndices.push(index)
isCanvasShow.value = false
}
})
}
const clickAddArea = () => {
mapDialog.value = false
isCanvasShow.value = true
}
const getList = () => {
rectangleDrawingOptsStorage.preRenderDatas = []
getAreaList().then(res => {
for (let index = 0; index < res.data.length; index++) {
const element = res.data[index];
element.name = element.properties.name
element.isResetName = false
element.type = element.geometry.type
RegionList.push(element)
// rectangleDrawingOptsStorage.preRenderDatas.push([element.geometry.coordinates[0]])
}
// drawingsRef.value.reload()
})
}
const clearAllDrawn = (i,item) => {
if(item.id){
delArea({id:item.id}).then(res => {
if(res.code == 200){
RegionList.splice(i,1)
let inil = rectangleDrawingOptsStorage.preRenderDatas.findIndex((item) => item.indexi === i);
rectangleDrawingOptsStorage.preRenderDatas.splice(inil,1)
// for (let index = 0; index < selectedRegionIndices.length; index++) {
// const element = selectedRegionIndices[index];
// if(element == i){
// selectedRegionIndices.splice(selectedRegionIndices.indexOf(i),1)
// }
// }
// rectangleDrawingOptsStorage.preRenderDatas.splice(i,1)
message.success(res.data)
}else{
message.error(res.message)
}
})
}else{
RegionList.splice(i,1)
rectangleDrawingOptsStorage.preRenderDatas.splice(i,1)
// rectangleDrawingOptsStorage.preRenderDatas.splice(i,1)
}
}
var viewerx
var tempEntities = reactive([])
const onViewerReady = ({ Cesium, viewer } ) => {
const destination = Cesium.Cartesian3.fromDegrees(103.954204, 30.845838, 5000)
const orientation = {
heading : Cesium.Math.toRadians(0.0),
pitch: Cesium.Math.toRadians(-90.0),
roll : 0.0
}
viewer.camera.setView({destination, orientation})
vierDraw.value = viewer
viewerx = viewer
}
const moveTo = (position) => {
viewerx.camera.flyTo({
destination: position,
orientation: {
heading: Cesium.Math.toRadians(0),
pitch: Cesium.Math.toRadians(-90),
roll: 0.0
}
})
}
const moveToPolygon = (positions) => {
const boundingSphere = Cesium.BoundingSphere.fromPoints(positions)
viewerx.camera.flyToBoundingSphere(boundingSphere)
}
// 方法和事件处理程序
const drawingsReadyDefault = ({ Cesium, viewer, cesiumObject }) => {
console.log('绘制选项参数:', cesiumObject);
viewer = viewer;
vierDraw.value = viewer
drawingsRef.value.clearAll()
};
const toggle = (drawingActionInstance) => {
drawingsRef.value.toggleAction(drawingActionInstance.name);
};
// const onTilesetReady = (tileset, viewer) => {
// const cartographic = Cesium.Cartographic.fromCartesian(tileset.boundingSphere.center);
// const surface = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, cartographic.height);
// const offset = Cesium.Cartesian3.fromRadians(cartographic.longitude, cartographic.latitude, 5);
// const translation = Cesium.Cartesian3.subtract(offset, surface, new Cesium.Cartesian3());
// tileset.modelMatrix = Cesium.Matrix4.fromTranslation(translation);
// viewer.zoomTo(tileset);
// viewer.scene.globe.depthTestAgainstTerrain = true;
// restoreCursorMove.value = 'auto';
// drawing.value = false;
// };
const activeEvt = (e, viewer) => {
console.log(e);
viewer.canvas.setAttribute('style', `cursor: ${e.isActive ? 'crosshair' : 'auto'}`);
if (!e.isActive) {
drawing.value = false;
restoreCursorMove.value = 'auto';
}
};
const editorEvt = (e, viewer) => {
if (e.type === 'move') {
viewer.canvas.setAttribute('style', 'cursor: move');
drawing.value = true;
} else {
viewer.canvas.setAttribute('style', 'cursor: auto');
}
};
const mouseEvt = (e, viewer) => {
const restoreCursor = getComputedStyle(viewer.canvas).cursor;
if (!drawing.value) {
console.log(e);
if (e.type === 'onmouseover') {
restoreCursorMove.value = restoreCursor;
viewer.canvas.setAttribute('style', 'cursor: pointer');
} else {
viewer.canvas.setAttribute('style', `cursor: ${restoreCursorMove.value || 'auto'}`);
}
}
};
const clearEvt = (e, viewer) => {
if(RegionList[RegionList.length-1].isResetName){
RegionList.splice(RegionList.length-1,1)
}
};
const unload = () => {
drawingsRef.value.unload();
};
const load = () => {
const drawingsInstance = drawingsRef.value
// 手动触发load事件
drawingsInstance.load()
};
const reload = () => {
drawingsRef.value.reload();
};
const pickEvt = (e) => {
console.log(e);
};
const polygon1 = ref(null)
onMounted(() => {
getList()
const viewer = vierDraw.value.getCesiumObject();
if (viewer) {
onViewerReady({ viewer });
}
})
</script>
<!-- Add "scoped" attribute to limit CSS to this component only -->
<style>
::-webkit-scrollbar {
display: none; /* Chrome Safari */
}
.cesium-viewer-bottom {
display: none !important;
}
#cesiumContainer {
width: 100%;
height: 100%;
}
.taskArea{
width: 100%;
height: 99.3%;
position: relative;
}
.ant-list-empty-text{
color: #fff !important;
}
.btnContainer {
position: absolute;
left: 5px;
top: 40px;
padding: 10px 15px;
/*添加圆角边框*/
border-radius: 5px;
/* border: 1px solid rgba(128,128,128, 0.5); */
color: #ffffff;
max-width: 300px;
}
button {
background: transparent;
border: 1px solid #00d0ffb8;
color: white;
padding: 7px 9px;
border-radius: 3px;
/*鼠标光标变为手形*/
cursor: pointer;
}
.selected-item {
background-color: #1890ff;
}
.bg-primary{
background-color: #1890ff !important;
}
.bg-info{
background-color: #1890ff !important;
}
.cesium-widget canvas{
height: 98% !important;
}
</style>
vc-drawings是vue-cesium的绘制图形工具。
a-list是绘制添加后的区域列表,点击区域列表进行回显