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

news2024/11/24 9:01:16

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

实现效果:

    (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 坐标
}

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

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

CodeFormer模型构建指南

一、介绍 在 NeurIPS 2022 上&#xff0c;南洋理工大学-商汤科技联合研究中心 S-Lab 提出了一种基于 VQGANTransformer的人脸复原模型 CodeFormer。基于CodeFormer模型实现面部复原、增强旧照片/修复AI艺术、面部颜色增强和修复、面部修复四个功能。 二、特点 CodeFormer 是…

常用组件详解(九):学习率更新策略

文章目录 1.StepLR2.MultiStepLR3.ExponentialLR4.LinearLR5.PloyLR 适合的学习率能够更好地训练模型&#xff0c;为此衍生出多种学习率调整策略。一般来说&#xff0c;在训练初期希望学习率大一些&#xff0c;使得网络收敛迅速&#xff0c;在训练后期希望学习率小一些&#xf…

jmeter学习(4)提取器

同线程组https://blog.csdn.net/vikeyyyy/article/details/80437530 不同线程组 在JMeter中&#xff0c;正则表达式提取的参数可以跨线程组使用。 通过使用Beanshell后置处理器和属性设置函数&#xff0c;可以将提取的参数设置为全局变量&#xff0c;从而在多个线程组之间共享…

Spring Boot新闻推荐系统设计与实现

3系统分析 3.1可行性分析 通过对本新闻推荐系统实行的目的初步调查和分析&#xff0c;提出可行性方案并对其一一进行论证。我们在这里主要从技术可行性、经济可行性、操作可行性等方面进行分析。 3.1.1技术可行性 本新闻推荐系统采用JAVA作为开发语言&#xff0c;Spring Boot框…

Go语言实现长连接并发框架 - 开篇

文章目录 前言初步设计思路初步架构图项目地址最后 前言 你好&#xff0c;我是醉墨居士&#xff0c;国庆假期闲来无事&#xff0c;准备使用Go语言开发一个轻量级的长连接并发框架&#xff0c;希望能够帮助大家掌握这类框架的心脏与内核&#xff0c;也希望能给大伙带来灵感与启…

【微服务】负载均衡 - LoadBalance(day4)

下述所有代码都是在订单服务中修改的&#xff0c;商品服务并不需要修改&#xff0c;只需要启动多个实例即可。 引入 在介绍Eureka组件的最后&#xff0c;留下了一个问题就是&#xff0c;无论启动多少个实例&#xff0c;只能调用第一个。原因是因为服务调用时获取的是一个实例…

C/C++/EasyX——入门图形编程(3)

【说明】上一篇讲了基础图形的绘制&#xff0c;那么这一篇就来讲一下如何在窗口上绘制文字吧&#xff0c;友友们一起学习吧。&#xff08;&#xff1e;&#xff59;&#xff1c;&#xff09;&#xff08;&#xff3e;&#xff56;&#xff3e;&#xff09; 一&#xff1a;文字…

jQuery——对象的过滤

在 jQuery 对象中的元素对象数组中过滤出一部分元素来 ① first&#xff08;&#xff09; ② last&#xff08;&#xff09; ③ eq&#xff08;index / -index&#xff09; ④ filter&#xff08;selector&#xff09;&#xff1a;对当前元素提要求 ⑤ not&#xff08;sel…

电脑IP地址怎么换成二进制:详解转换过程与应用

在电脑网络的世界里&#xff0c;IP地址是每台设备独一无二的身份标识。而我们日常所见的IP地址&#xff0c;大多是以点分十进制的形式呈现。然而&#xff0c;在电脑内部&#xff0c;IP地址实际上是以二进制的形式进行存储和处理的。那么&#xff0c;电脑IP地址怎么换成二进制呢…

pygame入门(千字详细版)

千字赘述&#xff0c;万字总结&#xff0c;就为博客点一赞吧&#xff01; 1.安装pygame pip install pygame 安装完成后在python中输入检验有没有问题&#xff0c;回车键应该不会报错。 >>>import pygame >>>2.pygame模块预览 3.项目实战 3.1.0 hello w…

一键开启高清录屏:盘点Windows最火四款录屏工具

嘿&#xff0c;朋友们&#xff0c;今天咱们来聊聊那些让我在电脑前忙活得热火朝天的录屏神器究竟怎么样。作为一个经常需要录制教程、分享游戏精彩瞬间的普通用户&#xff0c;我可是对这几款软件有着满满的体验心得&#xff0c;现在就给你们一一道来。 一、福昕录屏大师 网址…

编码与解码

文章目录 编码与解码一、字节 & 字符二、编码 & 解码三、字符集 & 字符编码四、ASCII五、ISO-8859-1六、GB七、Unicode1、概述2、发展3、UTF-8 编码4、UTF-16 编码 八、Base64 编码1、概述2、原理3、代码示例 九、十六进制编码 编码与解码 一、字节 & 字符 字…

kali下编译AOSP报错(libncurses.so.5: cannot open shared object file)

编译报错信息&#xff1a;libncurses.so.5: cannot open shared object file: No such file or directory /bin/bash -c "PWD/proc/self/cwd prebuilts/clang/host/linux-x86/clang-3289846/bin/clang -Ifr ameworks/rs/script_api/include -Iexternal/clang/lib/Headers …

【AI知识点】小世界网络(Small-World Networks)

小世界网络&#xff08;Small-World Networks&#xff09; 是一种具有独特拓扑结构的网络模型&#xff0c;广泛应用于研究社交网络、神经网络、互联网以及其他复杂系统中的节点间连接方式。小世界网络的特点是节点之间的平均路径长度较短&#xff0c;并且大多数节点的局部连接较…

世邦通信股份有限公司IP网络对讲广播系统RCE

漏洞描述 SPON世邦IP网络广播系统采用的IPAudio™技术, 将音频信号以数据包形式在局域网和广域网上进行传送&#xff0c;是一套纯数字传输的双向音频扩声系统。传统广播系统存在的音质不佳&#xff0c;传输距离有限&#xff0c;缺乏互动等问题。该系统设备使用简便&#xff0c…

知识图谱入门——7:阶段案例:使用 Protégé、Jupyter Notebook 中的 spaCy 和 Neo4j Desktop 搭建知识图谱

在 Windows 环境中结合使用 Protg、Jupyter Notebook 中的 spaCy 和 Neo4j Desktop&#xff0c;可以高效地实现从自然语言处理&#xff08;NLP&#xff09;到知识图谱构建的全过程。本案例将详细论述环境配置、步骤实现以及一些扩展和不足之处。 文章目录 1. 环境准备1.1 Neo4j…

webGL入门(六)图形旋转

效果&#xff1a; 代码&#xff1a; <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><meta name"viewport" content"widthdevice-width, initial-scale1.0"><title>Document</…

PIDM—— 物理正则化扩散模型

概述 论文地址&#xff1a;https://arxiv.org/pdf/2403.14404 源码地址&#xff1a;https://github.com/jhbastek/physicsinformeddiffusionmodels 扩散模型在逼近非常复杂的数据分布方面具有极高的性能和多功能性&#xff0c;近年来在自然科学领域的应用迅速扩展。鉴于其在科…

两数相除111

1.//给你两个整数&#xff0c;被除数 dividend 和除数 divisor。将两数相除&#xff0c;要求 不使用 乘法、除法和取余运算。 //整数除法应该向零截断&#xff0c;也就是截去&#xff08;truncate&#xff09;其小数部分。 // 例如&#xff0c;8.345 将被截断为 8 &#xff0…

基于SSM框架和Layui的学院课程安排系统的设计与实现(源码+定制+定制)

博主介绍&#xff1a; ✌我是阿龙&#xff0c;一名专注于Java技术领域的程序员&#xff0c;全网拥有10W粉丝。作为CSDN特邀作者、博客专家、新星计划导师&#xff0c;我在计算机毕业设计开发方面积累了丰富的经验。同时&#xff0c;我也是掘金、华为云、阿里云、InfoQ等平台…