当前位置: > > > Vue.js - 集成Cesium构建三维可视化场景教程4(添加标签、点、折线、多边形、圆形、椭球实体)

Vue.js - 集成Cesium构建三维可视化场景教程4(添加标签、点、折线、多边形、圆形、椭球实体)

    在前面的教程中,我们已经完成了 CesiumVue.js 项目中的基础搭建,并实现了三维场景中的坐标获取与交互能力。本文将围绕 CesiumEntity 体系,系统讲解如何在 Vue.js 项目中添加并管理标签、点、折线、多边形、圆形以及椭球等常见三维实体。

四、添加标签、点、折线、多边形、圆形、椭球等实体

1,样例代码

    下面代码通过初始化 Cesium Viewer 构建基础三维场景,并在界面中提供工具栏按钮,用于动态添加和管理多种常见三维实体。
<template>
    <div class="layer-container">
        <div ref="cesiumContainer" class="cesium-container"></div>

        <!-- 加载状态 -->
        <div v-if="loading" class="loading-overlay">
            <div class="loading-spinner"></div>
            <p>加载三维场景中...</p>
        </div>

        <!-- 工具栏 -->
        <div class="toolbar" v-if="!loading">
            <button @click="addLabelEntity">+标签</button>
            <button @click="addPointEntity1">+点</button>
            <button @click="addPointEntity2">+带标签的点</button>
            <button @click="addPolylineEntity">+折线</button>
            <button @click="addPolygonEntity">+多边形</button>
            <button @click="addCircleEntity">+圆</button>
            <button @click="addEllipsoidEntity">+椭球</button>
            <button @click="removeAllEntities">清除所有实体</button>
        </div>
    </div>
</template>

<script>
// 引入 Cesium 核心类
import * as Cesium from "cesium";

export default {
    name: 'CesiumViewer',

    data() {
        return {
            viewer: null,
            loading: true,
            terrainEnabled: true,
            sampleEntities: [],
        }
    },

    mounted() {
        // 确保 Cesium 已加载
        if (typeof Cesium === 'undefined') {
            console.error('Cesium 未加载,请检查配置')
            this.loading = false
            return
        }

        // 初始化 Cesium 场景
        this.initCesium()
    },

    beforeDestroy() {
        // 组件销毁时清理资源
        this.destroyCesium()
    },

    methods: {
        // 初始化 Cesium
        async initCesium() {
            try {
                // 配置 Cesium 访问令牌(如果需要,从 Cesium Ion 获取)
                //Cesium.Ion.defaultAccessToken = '你的Cesium Ion访问令牌'

                // 创建 Viewer 实例
                this.viewer = new Cesium.Viewer(this.$refs.cesiumContainer, {
                    animation: false, // 是否显示动画控件
                    baseLayerPicker: false, // 是否显示图层选择控件
                    fullscreenButton: false, // 是否显示全屏按钮
                    geocoder: false, // 是否显示地理编码搜索控件
                    homeButton: false, // 是否显示Home按钮
                    infoBox: false, // 是否显示信息框
                    sceneModePicker: false, // 是否显示3D/2D选择器
                    selectionIndicator: false, // 是否显示选取指示器组件
                    timeline: false, // 是否显示时间轴
                    navigationHelpButton: false, // 是否显示帮助按钮
                    scene3DOnly: true, // 每个几何实例仅以3D渲染以节省GPU内存
                    shouldAnimate: true, // 自动播放动画
                    // 地形配置
                    terrainProvider: await Cesium.createWorldTerrainAsync()
                })

                // 强制重新计算画布尺寸
                this.viewer.canvas.width = this.viewer.container.clientWidth;
                this.viewer.canvas.height = this.viewer.container.clientHeight;
                this.viewer.resize(); // 核心方法:告知 Cesium 容器大小已变

                // 设置初始视图
                this.setInitialView()

                // 加载完成
                this.loading = false

                console.log('Cesium 场景初始化完成')
            } catch (error) {
                console.error('初始化 Cesium 失败:', error)
                this.loading = false
            }
        },

        // 设置初始视图
        setInitialView() {
            // 设置初始位置(例如:北京天安门)
            const initialPosition = Cesium.Cartesian3.fromDegrees(116.3914, 39.9074, 1100)
            this.viewer.camera.setView({
                destination: initialPosition,
                orientation: {
                    heading: Cesium.Math.toRadians(0),
                    pitch: Cesium.Math.toRadians(-90),
                    roll: 0
                }
            })
        },

        // 添加标签实体
        addLabelEntity() {
            const labelEntity = this.viewer.entities.add({
                position: Cesium.Cartesian3.fromDegrees(116.3914, 39.9062),
                label: {
                    text: '天安门前大马路',
                    font: '14pt sans-serif',
                    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                    outlineWidth: 2,
                    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                    pixelOffset: new Cesium.Cartesian2(0, -20),
                    disableDepthTestDistance: Number.POSITIVE_INFINITY // 关闭深度测试,防止被遮挡
                }
            })

            this.sampleEntities.push(labelEntity)
        },

        // 添加点
        addPointEntity1() {
            const beijingPoint = this.viewer.entities.add({
                name: '北京天安门',
                position: Cesium.Cartesian3.fromDegrees(116.3910, 39.9070),
                point: {
                    pixelSize: 10,
                    color: Cesium.Color.BLUE,
                    outlineColor: Cesium.Color.WHITE,
                    outlineWidth: 2
                }
            })

            this.sampleEntities.push(beijingPoint)
        },

        // 添加带标签的点
        addPointEntity2() {
            const beijingPoint = this.viewer.entities.add({
                name: '北京天安门',
                position: Cesium.Cartesian3.fromDegrees(116.3914, 39.9074),
                point: {
                    pixelSize: 10,
                    color: Cesium.Color.RED,
                    outlineColor: Cesium.Color.WHITE,
                    outlineWidth: 2
                },
                label: {
                    text: '北京天安门',
                    font: '14pt sans-serif',
                    style: Cesium.LabelStyle.FILL_AND_OUTLINE,
                    outlineWidth: 2,
                    verticalOrigin: Cesium.VerticalOrigin.BOTTOM,
                    pixelOffset: new Cesium.Cartesian2(0, -20),
                    disableDepthTestDistance: Number.POSITIVE_INFINITY // 关闭深度测试,防止被遮挡
                }
            })

            this.sampleEntities.push(beijingPoint)
        },

        // 添加折线
        addPolylineEntity() {
            const positions = [
                Cesium.Cartesian3.fromDegrees(116.3889, 39.9063),
                Cesium.Cartesian3.fromDegrees(116.3899, 39.9063),
                Cesium.Cartesian3.fromDegrees(116.3899, 39.9059),
                Cesium.Cartesian3.fromDegrees(116.3918, 39.9059)
            ]

            const polyline = this.viewer.entities.add({
                name: '折线',
                polyline: {
                    positions: positions,
                    width: 2,
                    material: new Cesium.PolylineOutlineMaterialProperty({
                        color: Cesium.Color.BLUE,
                        outlineWidth: 2,
                        outlineColor: Cesium.Color.BLUE
                    })
                }
            })

            this.sampleEntities.push(polyline)
        },

        // 添加多边形实体
        addPolygonEntity() {
            const positions = [
                Cesium.Cartesian3.fromDegrees(116.3918, 39.9064),
                Cesium.Cartesian3.fromDegrees(116.3932, 39.9064),
                Cesium.Cartesian3.fromDegrees(116.3932, 39.9060),
                Cesium.Cartesian3.fromDegrees(116.3918, 39.9060)
            ]

            const polygon = this.viewer.entities.add({
                name: '多边形',
                polygon: {
                    hierarchy: new Cesium.PolygonHierarchy(positions),
                    material: Cesium.Color.GREEN.withAlpha(0.4),
                    outline: true,
                    outlineColor: Cesium.Color.BLACK,
                    outlineWidth: 2,
                    height: 3 // 离地高度
                }
            })

            this.sampleEntities.push(polygon)
        },

        // 添加圆
        addCircleEntity() {
            const circle = this.viewer.entities.add({
                name: '圆',
                position: Cesium.Cartesian3.fromDegrees(116.3914, 39.9074),
                ellipse: {
                    semiMinorAxis: 40, // 短半轴
                    semiMajorAxis: 40, // 长半轴
                    height: 0,
                    material: Cesium.Color.BLUE.withAlpha(0.5),
                    outline: true,
                    outlineColor: Cesium.Color.BLACK,
                    outlineWidth: 2
                }
            })

            this.sampleEntities.push(circle)
        },

        // 添加椭球
        addEllipsoidEntity() {
            const ellipsoid = this.viewer.entities.add({
                name: '椭球',
                position: Cesium.Cartesian3.fromDegrees(116.3904, 39.9070),
                ellipsoid: {
                    radii: new Cesium.Cartesian3(40, 40, 40),
                    material: Cesium.Color.ORANGE.withAlpha(0.5),
                    outline: true,
                    outlineColor: Cesium.Color.BLACK,
                    outlineWidth: 2
                }
            })

            this.sampleEntities.push(ellipsoid)
        },

        // 清空所有实体
        removeAllEntities() {
            this.viewer.entities.removeAll();
            this.sampleEntities = [];
        },

        // 销毁 Cesium 资源
        destroyCesium() {
            if (this.viewer) {
                this.viewer.destroy()
                this.viewer = null
            }
        }
    }
}
</script>

<style scoped>
.layer-container {
    position: relative;
    width: 100%;
    height: 100%;
}


.cesium-container {
    width: 100%;
    height: 100%;
}

.loading-overlay {
    position: absolute;
    top: 0;
    left: 0;
    width: 100%;
    height: 100%;
    background: rgba(0, 0, 0, 0.7);
    display: flex;
    flex-direction: column;
    justify-content: center;
    align-items: center;
    z-index: 1000;
    color: white;
}

.loading-spinner {
    border: 4px solid rgba(255, 255, 255, 0.3);
    border-radius: 50%;
    border-top: 4px solid #42b983;
    width: 50px;
    height: 50px;
    animation: spin 1s linear infinite;
    margin-bottom: 15px;
}

@keyframes spin {
    0% {
        transform: rotate(0deg);
    }

    100% {
        transform: rotate(360deg);
    }
}

.toolbar {
    position: absolute;
    top: 20px;
    left: 20px;
    z-index: 999;
    display: flex;
    gap: 5px;
}

.toolbar button {
    padding: 8px 15px;
    background: rgba(40, 44, 52, 0.8);
    color: white;
    border: none;
    border-radius: 4px;
    cursor: pointer;
    font-size: 14px;
    transition: background 0.3s;
}

.toolbar button:hover {
    background: rgba(66, 185, 131, 0.8);
}
</style>

2,运行效果

(1)点击工具栏“+标签”按钮会在天安门前大马路附近的位置生成一个文字标签,标签内容为“天安门前大马路”,文字始终面向屏幕,并通过关闭深度测试避免被地形或其他对象遮挡。

(2)点击工具栏“+点”按钮会在指定经纬度位置生成一个蓝色点实体,点具有白色描边,用于表示一个简单的空间位置标记。

(3)点击工具栏“+带标签的点”按钮会在场景中添加一个红色点,并在其上方同步显示文字标签“北京天安门”,点与标签保持空间关联,常用于需要同时展示位置与名称信息的业务场景。

(4)点击工具栏“+折线”按钮会在地面上绘制一条由多个经纬度点组成的折线,折线以蓝色显示,用于模拟路径、线路或巡检路线等线状空间对象。

(5)点击工具栏“+多边形”会按钮在指定区域绘制一个半透明的绿色多边形,带有黑色边框,并略微抬高于地表,用于表示区域范围、作业区、管控区等面状空间对象。

(6)点击工具栏+圆”按钮便会以指定点为圆心,在地面生成一个半透明蓝色圆形区域,圆形具有描边效果,常用于表示影响范围、缓冲区或安全距离。

(7)点击工具栏“+椭球”按钮可以在场景中生成一个半透明的橙色椭球体,具有三维立体效果,用于演示体状空间对象的表达方式,例如设备影响体、空间包络或风险立体范围。

(8)点击工具栏“清除所有实体”按钮可以移除当前场景中已添加的所有实体要素,恢复到初始的纯三维地球视图,便于重新进行要素添加与演示。
评论0