vue3 + ts + cesium:绘制、更新圆 ellipse

本文主要实现基础的绘制圆形,并且可以通过拖动圆心更新圆的位置,拖动圆上的边缘点改变圆的半径。

实现效果:

    (1)单击鼠标左键开始绘制,确定圆的圆心,移动鼠标,改变圆的半径;单击鼠标右键,结束绘制。

    (2)鼠标左键单击绘制的圆形,显示圆的圆心和边缘点;长按鼠标左键,拖动圆心,实时更新圆的位置;长按鼠标左键,拖动边缘点,实时更新圆的半径;单击鼠标右键,结束更新操作,不再显示圆心和边缘点。

1. components / CesiumViewer / hooks / drawCircle.ts(绘制/更新代码)

import * as Cesium from "cesium";
import {CallbackProperty} from "cesium";
import {
    cartesian2ToCartesian3,
    disableDefaultScreenSpaceEventHandlers,
    enableDefaultScreenSpaceEventHandlers
} from "@/components/CesiumViewer/hooks/utils";

/* 绘制圆 */
export const drawCircle = () => {
    const handler = new Cesium.ScreenSpaceEventHandler(window.viewer.scene.canvas)
    const updateHandler = new Cesium.ScreenSpaceEventHandler(window.viewer.scene.canvas)
    let isDrawing = true // 是否处于绘制状态
    let centerPosition: any // 中心点的位置
    let centerPoint: any // 中心点
    let radius: any // 圆的半径
    let tempCircles: any[] = [] // 保存一次绘制过程中产生的圆
    let endPosition: any // 边缘点的位置
    let endPoint: any // 边缘点
    let pickedCircle: any // 选中的圆
    // 单击左键 —— 绘制圆 / 选中圆
    handler.setInputAction((event: any) => {
        const pickedObject = window.viewer.scene.pick(event.position) // 拾取实体
        if (Cesium.defined(pickedObject) && pickedObject.id && pickedObject.id.ellipse && !isDrawing) { // 选中圆
            pickedCircle = pickedObject.id
            const centerEntity: any = {
                // position: centerPosition,
                position: new Cesium.CallbackProperty(() => centerPosition, false),
                point: {
                    pixelSize: 20, // 点的大小
                    color: Cesium.Color.YELLOW,
                    /* 根据视角远近控制点的比例 */
                    scaleByDistance: new Cesium.NearFarScalar(1.5e2, 2.0, 8.0e6, 0.0),
                    heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND
                },
                type: 'center'
            }
            centerPoint = window.viewer.entities.add(centerEntity)
            const endEntity: any = {
                // position: endPosition,
                position: new CallbackProperty(() => endPosition, false),
                point: {
                    pixelSize: 20, // 点的大小
                    color: Cesium.Color.YELLOW,
                    /* 根据视角远近控制点的比例 */
                    scaleByDistance: new Cesium.NearFarScalar(1.5e2, 2.0, 8.0e6, 0.0),
                    heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND
                },
                type: 'end'
            }
            endPoint = window.viewer.entities.add(endEntity)
        } else { // 绘制圆
            if (isDrawing) {
                centerPosition = window.viewer.camera.pickEllipsoid(event.position, window.viewer.scene.globe.ellipsoid) // cartesian3
                if (Cesium.defined(centerPosition) && isDrawing) {
                    centerPoint = window.viewer.entities.add({
                        position: centerPosition,
                        point: {
                            pixelSize: 20, // 点的大小
                            color: Cesium.Color.YELLOW,
                            /* 根据视角远近控制点的比例 */
                            scaleByDistance: new Cesium.NearFarScalar(1.5e2, 2.0, 8.0e6, 0.0),
                            heightReference: Cesium.HeightReference.RELATIVE_TO_GROUND
                        }
                    })
                    handler.setInputAction((movement: any) => {
                        // 计算半径【movement.endPosition 为 cartesian2 坐标】
                        radius = Cesium.Cartesian3.distance(centerPosition, <Cesium.Cartesian3>cartesian2ToCartesian3(movement.endPosition))
                        const tempCircle = window.viewer.entities.add({
                            position: centerPosition, // 圆心位置
                            ellipse: {
                                semiMinorAxis: new Cesium.CallbackProperty(() => radius, false), // 短半轴
                                semiMajorAxis: new Cesium.CallbackProperty(() => radius, false), // 长半轴(设置为相等以形成圆形)
                                material: new Cesium.ColorMaterialProperty(Cesium.Color.RED.withAlpha(0.5)), // 圆形的填充颜色和透明度
                                /*// 圆环
                                outline: true, // 轮廓线
                                outlineColor: Cesium.Color.YELLOW, // 轮廓颜色
                                fill: false // 无填充*/
                            }
                        })
                        tempCircles.push(tempCircle)
                        if (tempCircles.length > 1) {
                            for (let i = 0; i < tempCircles.length - 1; i++) {
                                window.viewer.entities.remove(tempCircles[i]) // 实时更新半径时会绘制多个圆,并且堆叠在一起,所以需要保证只球上只渲染最新的圆
                            }
                        }
                    }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
                }
            }
        }
    }, Cesium.ScreenSpaceEventType.LEFT_CLICK)
    // 单击右键 —— 结束绘制
    handler.setInputAction((event: any) => {
        handler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
        tempCircles = []
        window.viewer.entities.remove(centerPoint) // 结束绘制 / 结束更新后 移除中心点
        centerPoint = null
        if (isDrawing) {
            endPosition = window.viewer.camera.pickEllipsoid(event.position, window.viewer.scene.globe.ellipsoid) // 保存圆的边缘点
        }
        isDrawing = false
        window.viewer.entities.remove(endPoint) // 结束更新后 移除边缘点
    }, Cesium.ScreenSpaceEventType.RIGHT_CLICK)
    // 长按左键 —— 更新绘制的圆圈
    handler.setInputAction((event: any) => {
        const pickedObject = window.viewer.scene.pick(event.position)
        if (Cesium.defined(pickedObject) && pickedObject.id && pickedObject.id.point) {
            disableDefaultScreenSpaceEventHandlers()
            updateHandler.setInputAction((movement: any) => {
                const newPosition = window.viewer.camera.pickEllipsoid(movement.endPosition, window.viewer.scene.globe.ellipsoid)
                if (Cesium.defined(newPosition)) {
                    pickedObject.id.position = new Cesium.CallbackProperty(() => newPosition, false) // 实时更新拖动的点的位置
                    if (pickedObject.id.type === 'center') { // 中心点 —— 拖动圆
                        pickedCircle.position = new Cesium.CallbackProperty(() => newPosition, false) // 更新圆心位置
                        centerPosition = newPosition // 更新中心点的位置
                        // 计算新的边缘点位置
                        let offsetDirection = Cesium.Cartesian3.subtract(endPosition, centerPosition, new Cesium.Cartesian3()) // 从中心点到边缘点的方向
                        let normalizedDirection = Cesium.Cartesian3.normalize(offsetDirection, new Cesium.Cartesian3()) // 单位方向向量
                        endPosition = Cesium.Cartesian3.add(centerPosition, Cesium.Cartesian3.multiplyByScalar(normalizedDirection, radius, new Cesium.Cartesian3()), new Cesium.Cartesian3())
                        endPoint.position = new Cesium.CallbackProperty(() => endPosition, false)
                    }
                    if (pickedObject.id.type === 'end') { // 边缘点 —— 改变圆的半径
                        radius = Cesium.Cartesian3.distance(centerPosition, <Cesium.Cartesian3>cartesian2ToCartesian3(movement.endPosition))
                        pickedCircle.ellipse.semiMinorAxis = new Cesium.CallbackProperty(() => radius, false)
                        pickedCircle.ellipse.semiMajorAxis = new Cesium.CallbackProperty(() => radius, false)
                        endPosition = newPosition
                    }
                }
            }, Cesium.ScreenSpaceEventType.MOUSE_MOVE)
        }
    }, Cesium.ScreenSpaceEventType.LEFT_DOWN)
    // 抬起左键 —— 结束更新
    handler.setInputAction(() => {
        updateHandler.removeInputAction(Cesium.ScreenSpaceEventType.MOUSE_MOVE)
        enableDefaultScreenSpaceEventHandlers() // 恢复允许屏幕移动
    }, Cesium.ScreenSpaceEventType.LEFT_UP)
}

2. components / CesiumViewer / hooks / utils.ts (禁止/允许屏幕拖动、屏幕坐标转世界坐标代码)

import * as Cesium from "cesium";

// 保持地球不动
export function disableDefaultScreenSpaceEventHandlers() {
    window.viewer.scene.screenSpaceCameraController.enableRotate = false // 禁止旋转
    window.viewer.scene.screenSpaceCameraController.enableTranslate = false // 禁止平移
    window.viewer.scene.screenSpaceCameraController.enableZoom = false // 禁止缩放
    window.viewer.scene.screenSpaceCameraController.enableTilt = false // 禁止倾斜
    window.viewer.scene.screenSpaceCameraController.enableLook = false // 禁止观察(自由视角查看)
}

// 允许地球移动
export function enableDefaultScreenSpaceEventHandlers() {
    window.viewer.scene.screenSpaceCameraController.enableRotate = true
    window.viewer.scene.screenSpaceCameraController.enableTranslate = true
    window.viewer.scene.screenSpaceCameraController.enableZoom = true
    window.viewer.scene.screenSpaceCameraController.enableTilt = true
    window.viewer.scene.screenSpaceCameraController.enableLook = true
}

// 屏幕坐标转世界坐标(cartesian2 → cartesian3)
export function cartesian2ToCartesian3(cartesian2: Cesium.Cartesian2) {
    // 获取相机的射线
    const ray: any = window.viewer.camera.getPickRay(cartesian2)
    // 使用射线来获取地球表面上的位置
    return window.viewer.scene.globe.pick(ray, window.viewer.scene) // 返回 Cartesian3 坐标
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/887082.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Mac屏蔽系统更新,取出红点标记如果解锁hosts文件

引言&#xff1a;关闭系统更新&#xff0c;首先应该在系统偏好设置---软件更新---去掉自动更新的选项。即使如此&#xff0c;系统仍然进行macOS系统和自带safari等软件的检测更新&#xff0c;并图标右上角红点点标记提醒我们更新&#xff0c;那我们如果彻底屏蔽更新呢&#xff…

解决 Adobe 盗版弹窗

在这个文件夹下删除 Adobe CCXProcess 然后重装。 Adobe Premiere Pro 2024 (v24.6.1) Multilingual :: Варез от m0nkrusa [Warez by m0nkrus] (monkrus.ws) Adobe Photoshop 2024 (v25.12) Multilingual :: Варез от m0nkrusa [Warez by m0nkrus] (monkrus.…

Spring Boot RESTful API开发教程

一、RESTful API简介 RESTful API是一种基于HTTP协议的Web API&#xff0c;其设计原则是简单、可扩展、轻量级、可缓存、可靠、可读性强。RESTful API通常使用HTTP请求方法&#xff08;GET、POST、PUT、DELETE等&#xff09;来操作资源&#xff0c;使用HTTP状态码来表示操作结…

JQuery基本介绍和使用方法

文章目录 JQuery基本介绍和使用方法引入依赖 jQuery语法jQuery选择器jQuery事件操作元素获取/设置元素内容获取/设置元素属性获取/返回css属性添加元素删除元素 JQuery基本介绍和使用方法 W3C 标准给我们提供了⼀系列的函数, 让我们可以操作: ⽹⻚内容⽹⻚结构⽹⻚样式 但是…

案例-猜数字游戏

文章目录 效果展示初始画面演示视频 代码区 效果展示 初始画面 演示视频 猜数字游戏 代码区 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width,…

PostgreSQL Docker Error – 5432: 地址已被占用

PostgreSQL Docker Error – 5432: 地址已被占用 今天在学习【Spring Boot React】价值79.9美元&#xff0c;全栈开发&#xff0c;搭建个人网站、做毕业设计、试试这套课程第17~21节视频的时候&#xff0c;发现运行docker run --name demo-postgres -e POSTGRES_PASSWORDpass…

【C++】类与对象(三)

「前言」 &#x1f308;个人主页&#xff1a; 代码探秘者 &#x1f308;C语言专栏&#xff1a;C语言 &#x1f308;C专栏&#xff1a; C &#x1f308;喜欢的诗句:天行健,君子以自强不息. 目录 一、再谈构造函数 1.1 构造函数体赋值 1.2 初始化列表 1.3 explicit 关键字 二…

pycharm中使用anaconda创建多环境,无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称

问题描述 用的IDE是&#xff1a; 使用anaconda创建了一个Python 3.9的环境 结果使用pip命令的时候&#xff0c;报错 无法将“pip”项识别为 cmdlet、函数、脚本文件或可运行程序的名称 解决方案 为了不再增加系统变量&#xff0c;我们直接将变量添加在当前项目中你的Ter…

类型转换【C++提升】(隐式转换、显式转换、自定义转换、转换构造函数、转换运算符重载......你想知道的全都有)

更多精彩内容..... &#x1f389;❤️播主の主页✨&#x1f618; Stark、-CSDN博客 本文所在专栏&#xff1a; C系列语法知识_Stark、的博客-CSDN博客 座右铭&#xff1a;梦想是一盏明灯&#xff0c;照亮我们前行的路&#xff0c;无论风雨多大&#xff0c;我们都要坚持不懈。 一…

前端学习第三天笔记 JavaScript JavaScript的引入 数据类型 运算符 条件语句 字符串

这里写自定义目录标题 JavaScriptJavaScript引入到文件嵌入到HTML文件中引入本地独立js文件引入网络来源文件 JavaScript的注释方式嵌入在HTML文件中的注释JavaScript的输出方式数据类型原始类型&#xff08;基础类型&#xff09;合成类型&#xff08;复合类型&#xff09; 运算…

《15分钟轻松学 Python》教程目录

为什么要写这个教程呢&#xff0c;主要是因为即使是AI技术突起的时代&#xff0c;想要用好AI做开发&#xff0c;那肯定离不开Python&#xff0c;就算最轻量级的智能体都有代码块要写&#xff0c;所以不一定要掌握完完整整的Python&#xff0c;只要掌握基础就能应对大部分场景。…

使用VBA快速生成Excel工作表非连续列图片快照

Excel中示例数据如下图所示。 现在需要拷贝A2:A15,D2:D15,J2:J15,L2:L15,R2:R15为图片&#xff0c;然后粘贴到A18单元格&#xff0c;如下图所示。 大家都知道VBA中Range对象有CopyPicture方法可以拷贝为图片&#xff0c;但是如果Range对象为非连续区域&#xff0c;那么将产生10…

深刻理解Redis集群(下):Redis 哨兵(Sentinel)模式

背景 现在对3个节点的sentinel进行配置。sentinel的配置文件在redis的安装目录中已经存在&#xff0c;只需要复制到指定的位置即可。 sentinel是独立进程&#xff0c;有对应的脚本来执行。 基于之前的redis 一主二从的架构&#xff0c;我们继续启动3个sentinel进程。 哨兵模式的…

MAC备忘录空白解决方案

打开icloud->备忘录 取消勾选同步此MAC后再次勾选&#xff0c;然后点击完成即可。

【SpringCloud】服务注册/服务发现-Eureka

服务注册/服务发现-Eureka 1. 背景1.1 问题描述1.2 解决思路1.3 什么是注册中⼼1.4 CAP理论1.5 常⻅的注册中⼼ 2. Eureka 介绍3. 搭建Eureka Server 1. 背景 1.1 问题描述 上个章节的例⼦中可以看到, 远程调⽤时, 我们的URL是写死的 String url "http://127.0.0.1:90…

Ubuntu24.04远程开机

近来在几台机器上鼓捣linux桌面&#xff0c;顺便研究一下远程唤醒主机。 本篇介绍Ubuntu系统的远程唤醒&#xff0c;Windows系统的唤醒可搜索相关资料。 依赖 有远程唤醒功能的路由器&#xff08;当前一般都带这个功能&#xff09;有线连接主机&#xff08;无线连接有兴趣朋友…

推荐:五种限流(Rate Limiting)算法

推荐&#xff1a;五种限流(Rate Limiting)算法&#xff0c;发现一个不错的讲这个算法的UP,地址是&#xff1a;05~五种限流(Rate Limiting)算法_哔哩哔哩_bilibili https://www.bilibili.com/video/BV11k4SerE74/ 全部用动画展示&#xff0c;十分生动&#xff0c;比如漏桶算法&…

芝法酱学习笔记(0.5)——使用jenkins做自动打包

前言 上节讲了SpringBoot上的打包。但这些过程都是手动的&#xff0c;在实际的开发测试时&#xff0c;自动化的打包部署&#xff0c;可以大大提升团队开发的效率 一、去官网下载 1.1 官网安装命令 对于如何安装的问题&#xff0c;我向来推荐官网 wget -O /usr/share/keyri…

针对考研的C语言学习(定制化快速掌握重点4)

typedef的使用 简化变量类型 逻辑结构 集合结构&#xff1a;无关系 线性结构&#xff1a;一对一 树形结构&#xff1a;一对多 图形结构&#xff1a;多对多 存储结构 顺序存储和链式存储&#xff08;考代码&#xff09; 顺序存储优点&#xff1a;1.可以实现随机存取。2.…

C语言 | Leetcode C语言题解之题451题根据字符出现频率排序

题目&#xff1a; 题解&#xff1a; #define HASH_FIND_CHAR(head, findint, out) HASH_FIND(hh, head, findint, sizeof(char), out) #define HASH_ADD_CHAR(head, intfield, add) HASH_ADD(hh, head, intfield, sizeof(char), add)struct HashTable {char key;int val;UT_ha…