开源!Cesium动态绘制点线面,都应用在哪些领域?

大家好,我是日拱一卒的攻城师不浪,致力于技术与艺术的融合。这是2024年输出的第35/100篇文章。
可视化&Webgis交流群+V:brown_7778(备注来意)

前言

Cesium 中,点、线、面的绘制场景很常见,例如智慧城市应急管理军事演练中经常会有它们的身影。

应用场景

军事模拟与训练

  • 态势图绘制:使用点线面表示战场中的关键设施、阵地、部队运动路线等,辅助指挥决策。

  • 战术规划:面可以用于表示作战区域,线则可以表示部队移动路线或作战计划。

城市规划与管理

  • 应用点线面表示建筑物、道路和区域:在城市数字孪生系统中,可以使用面来表示建筑物的占地面积和功能区域,使用线来表示道路网络,使用点来标记关键设施,如交通信号供水系统

交通管理与物流

  • 路径规划和线路优化:使用线条绘制运输路线、航线、轨道交通等,可以帮助决策者进行路径优化以及交通流量分析。

  • 关键点标记:使用点标记物流节点、运输中心、充电桩等重要位置,结合路径线条,可以实现物流管理和运力调度。

环境监测与灾害管理

  • 环境信息的可视化:通过面来标示特定区域,如生态保护区污染区域洪水淹没区域等,并通过点标记监测站或数据采集点的位置。

  • 应急路径规划:在自然灾害,如洪水地震等发生时,使用线条绘制应急疏散路线或避难所位置,辅助救援决策。

能源和基础设施管理

  • 电网、管道系统的可视化:线条可以用来绘制电力线路燃气管道供水管道等,点则用于标记变电站、泵站等关键节点。面可以用来显示能源分布区域。

  • 资产管理:结合点线面数据,可以实现对基础设施的管理、监控和维护。

虚拟旅游和历史遗迹展示

  • 路线引导和兴趣点展示:通过线条绘制旅游路线,用点标记景区关键位置,用面展示景区边界,帮助用户进行虚拟游览或展示遗迹的历史背景。

  • 文化遗产保护:通过面展示文化遗产的保护范围,帮助进行规划和管理。

代码开发

接下来,我们把点线面的绘制功能封装成一个类,方便后期使用以及维护。

激活绘制方法

activate(drawType) {
    this.clearAll();
    this._drawType = drawType;
    this._dataSource = new Cesium.CustomDataSource("_dataSource");
    this.viewer.dataSources.add(this._dataSource);
    this._registerEvents(); // 注册鼠标事件
}

解析:首先清空之前的绘制内容,drawType是绘制类型(点、线或面)。最后调用 _registerEvents() 方法注册鼠标事件来处理绘制逻辑。


注册鼠标事件

_registerEvents() {
    this._drawHandler = new Cesium.ScreenSpaceEventHandler(this.viewer.scene.canvas);
    this.viewer.scene.globe.depthTestAgainstTerrain = true; //开启深度测试
    switch (this._drawType) {
        case "Point":
            this._leftClickEventForPoint();
            break;
        case "Polyline":
            this._leftClickEventForPolyline();
            this._mouseMoveEventForPolyline();
            this._rightClickEventForPolyline();
            break;
        case "Polygon":
            this._leftClickEventForPolygon();
            this._mouseMoveEventForPolygon();
            this._rightClickEventForPolygon();
            break;
    }
}

解析:根据不同的绘制类型(点、线、面),注册不同的鼠标事件来响应绘制操作,包括左键点击、鼠标移动、右键点击。开启 depthTestAgainstTerrain,确保绘制的内容正确地与地形结合。


绘制点

_leftClickEventForPoint() {
    this._drawHandler.setInputAction((e) => {
        let p = this.viewer.scene.pickPosition(e.position);
        if (!p) return;
        let carto_pt = Cesium.Cartographic.fromCartesian(p);
        let p1 = [
            Cesium.Math.toDegrees(carto_pt.longitude),
            Cesium.Math.toDegrees(carto_pt.latitude),
            carto_pt.height + 50,
        ];
        this._addPoint(p1);
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}

解析:响应鼠标左键点击事件,获取点击位置的坐标,并通过 _addPoint 方法在指定位置添加一个点实体,点绘制的高度增加 50 米。


绘制线

_leftClickEventForPolyline() {
    this._drawHandler.setInputAction((e) => {
        let p = this.viewer.scene.pickPosition(e.position);
        if (!p) return;
        this._tempPositions.push(p);
        this._addPolyline();
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}

_mouseMoveEventForPolyline() {
    this._drawHandler.setInputAction((e) => {
        let p = this.viewer.scene.pickPosition(e.endPosition);
        if (!p) return;
        this._mousePos = p;
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}

_rightClickEventForPolyline() {
    this._drawHandler.setInputAction((e) => {
        let p = this.viewer.scene.pickPosition(e.position);
        if (!p) return;
        this._removeAllEvent();
        this._dataSource.entities.removeAll();
        this._dataSource.entities.add({
            polyline: {
                positions: this._tempPositions,
                clampToGround: true, //贴地
                width: 3,
                material: new Cesium.PolylineDashMaterialProperty({
                    color: Cesium.Color.YELLOW,
                }),
            },
        });
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}

解析

  • 左键点击:每次点击后获取点位置并保存到 this._tempPositions 数组中,调用 _addPolyline 方法更新绘制线。

  • 鼠标移动:实时更新鼠标当前位置,动态显示未完成的线段。

  • 右键点击:表示结束绘制,移除事件监听并将线固定在最终位置。


6. 绘制面

_leftClickEventForPolygon() {
    this._drawHandler.setInputAction((e) => {
        let p = this.viewer.scene.pickPosition(e.position);
        if (!p) return;
        this._tempPositions.push(p);
        this._addPolygon();
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK);
}

_mouseMoveEventForPolygon() {
    this._drawHandler.setInputAction((e) => {
        let p = this.viewer.scene.pickPosition(e.endPosition);
        if (!p) return;
        this._mousePos = p;
    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE);
}

_rightClickEventForPolygon() {
    this._drawHandler.setInputAction((e) => {
        let p = this.viewer.scene.pickPosition(e.position);
        if (!p) return;
        this._tempPositions.push(this._tempPositions[0]);
        this._removeAllEvent();
        this._dataSource.entities.removeAll();
        this._dataSource.entities.add({
            polygon: {
                hierarchy: this._tempPositions,
                material: Cesium.Color.RED.withAlpha(0.4),
                clampToGround: true,
            },
        });
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK);
}

解析

  • 左键点击:添加点到 _tempPositions 数组,并动态更新绘制的面。

  • 鼠标移动:用于实时显示当前面的形状,随着鼠标移动改变面边缘位置。

  • 右键点击:结束绘制面,并将首尾相连,最终生成封闭的多边形。


Entity添加点、线、面

_addPoint(p) {
    this._dataSource.entities.add({
        position: Cesium.Cartesian3.fromDegrees(p[0], p[1], p[2]),
        point: {
            color: Cesium.Color.RED,
            pixelSize: 10,
            outlineColor: Cesium.Color.YELLOW,
            outlineWidth: 2,
        },
    });
}

_addPolyline() {
    this._dataSource.entities.add({
        polyline: {
            positions: new Cesium.CallbackProperty(() => {
                let c = Array.from(this._tempPositions);
                if (this._mousePos) {
                    c.push(this._mousePos);
                }
                return c;
            }, false),
            clampToGround: true,
            width: 3,
            material: new Cesium.PolylineDashMaterialProperty({
                color: Cesium.Color.YELLOW,
            }),
        },
    });
}

_addPolygon() {
    this._dataSource.entities.add({
        polygon: {
            hierarchy: new Cesium.CallbackProperty(() => {
                let poss = Array.from(this._tempPositions);
                if (this._mousePos) {
                    poss.push(this._mousePos);
                }
                return new Cesium.PolygonHierarchy(poss);
            }, false),
            material: Cesium.Color.RED.withAlpha(0.4),
            clampToGround: true,
        },
    });
}

解析

  • _addPoint:根据传入的坐标在指定位置绘制点,设置点的颜色、大小和外边框。

  • _addPolyline:通过 CallbackProperty 动态更新线的终点,使线段随着鼠标移动实时变化。

  • _addPolygon:类似线的动态更新,使用 CallbackProperty 实时更新多边形的边界点。


清除和重置

_removeAllEvent() {
    if (this._drawHandler) {
        this._drawHandler.removeInputAction(Cesium.ScreenSpaceEventType.LEFT_CLICK);
        this._drawHandler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE);
        this._drawHandler.removeInputAction(Cesium.ScreenSpaceEventType.RIGHT_CLICK);
        this._drawHandler.destroy();
        this._drawHandler = null;
    }
}

_resetParams() {
    if (this._dataSource != null) {
        this._dataSource.entities.removeAll();
        this.viewer.dataSources.remove(this._dataSource);
    }
    this._dataSource = null;
    this._tempPositions = [];
    this._mousePos = null;
    this._drawType = null;
}

clearAll() {
    this._removeAllEvent();
    this._resetParams();
}

解析

  • _removeAllEvent:移除所有鼠标事件监听器,停止绘制。

  • _resetParams:重置所有临时变量并移除当前绘制的实体。

  • clearAll:事件清除、参数重置。

最后

以上就是绘制点线面的主要步骤,更详细的源码可以参考:https://github.com/tingyuxuan2302/cesium-vue3-vite/blob/github/src/views/geometry/draw.vue

果如认为有帮助,希望可以给博主一个免费的star,激励博主持续开源更多代码。

作者的Cesium系列课程《Cesium从入门到实战》 ,将Cesium的知识点进行串联,让不了解Cesium的小伙伴拥有一个完整的学习路线,学完后能够直接上手做项目且不再迷茫,课程细节联系作者:brown_7778(备注来意)。

另外有需要进可视化&Webgis交流群可以加我:brown_7778(备注来意),也欢迎数字孪生可视化领域的交流合作。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值