开源!在goview中实现cesium的低代码可视化编辑

大家好,我是日拱一卒的攻城师不浪,专注可视化、数字孪生、前端、nodejs、AI学习、GIS等学习沉淀,这是2024年输出的第19/100篇文章;

前言

前阵子写了一篇goview二开的文章教程,很多小伙伴留言对goview嵌套cesium并实现cesium的低代码可视化编辑很感兴趣,今天我就来将我的实现和思路分享给大家,希望能够对大家有帮助。

对goview二开还不怎么了解的朋友可以先看我上篇文章:理解了GoView低代码平台(可视化大屏)的开发原理,基于它进行了二开,因为二开需要对goview的整个框架有一个基础的了解才可。

goview嵌套cesium

安装cesium相关npm包

npm i cesium
npm i vite-plugin-cesium

vite-plugin-cesium:一个能够快速初始化cesium的vite插件,帮你省去cesium的繁琐配置;

vite.config.js中使用:

import cesium from 'vite-plugin-cesium';plugins: [vue(),//...cesium(),],

构建cesium组件基础文件配置

首先,在goview中,如果想新增一个cesium可视化组件,需要先在src/packages/components/Cesium/Base/CesiumBase目录下新建4个基础文件。

index.ts

cesium的可视化的配置文件

import { ConfigType, PackagesCategoryEnum, ChartFrameEnum } from '@/packages/index.d'
import { CesiumCategoryEnum, CesiumCategoryEnumName } from '../../index.d'export const CesiumBaseConfig: ConfigType = {key: 'CesiumBase',chartKey: 'VCesiumBase',conKey: 'VCCesiumBase',title: 'Cesium地球',category: CesiumCategoryEnum.CESIUM_BASE,categoryName: CesiumCategoryEnumName.CESIUM_BASE,package: PackagesCategoryEnum.CESIUM,chartFrame: ChartFrameEnum.COMMON,image: 'global.png',redirectComponent: `Cesium/Base/CesiumBase` // 跳转组件路径规则
}

index.vue

渲染cesium的vue文件。

<template><div id="cesiumContainer"></div>
</template>
<script setup>
import * as Cesium from "cesium";
import { useChartDataFetch } from '@/hooks'
import { useChartEditStore } from '@/store/modules/chartEditStore/chartEditStore'
import { ref, onMounted, toRefs, watch } from 'vue';
import { LocationEnum } from "./config"
import { getImg, resolveGeojson } from '@/api/path'//token
Cesium.Ion.defaultAccessToken ="xxx";const props = defineProps({chartConfig: {type: Object,required: true}
})
let {center,locationMode,markImgUrl,markGeojsonData
} = toRefs(props.chartConfig.option.viewOptions)
onMounted(() => {init();
});
let viewer = null
const stopWatch = watch(() => props.chartConfig.option.viewOptions,options => {init(options)},{deep: true}
)const init = async (opts) => {if (opts) {// 当配置属性发生变化时触发当前分支const watchCenter = opts.centerconst {center: watch_center,locationMode: watch_locationMode,markImgUrl: watch_markImgUrl,geojsonFileName: watch_geojsonFileName,} = opts// 中心坐标const centerArr = watch_center?.split(",")if (centerArr?.length) {const arr = centerArr.map(Number)cameraLocation(watch_locationMode, arr)}// 若geojson文件存在if (watch_geojsonFileName) {try {const res = await resolveGeojson({fileName: watch_geojsonFileName})if (res?.data?.features?.length) {renderMarks(res.data.features, watch_markImgUrl)}} catch (err) {console.error(JSON.stringify(err))}}} else {// 初始化viewer = new Cesium.Viewer("cesiumContainer", {infoBox: false,timeline: false, // 是否显示时间线控件});// 去除logoviewer.cesiumWidget.creditContainer.style.display = "none";// 显示帧率viewer.scene.debugShowFramesPerSecond = true;viewer.scene.globe.depthTestAgainstTerrain = true;const centerArr = center.value?.split(",")if (centerArr?.length && locationMode.value) {const arr = centerArr.map(Number)cameraLocation(locationMode.value, arr)}// 调试使用window.viewer = viewer// 监听点击事件,拾取坐标const handler = new Cesium.ScreenSpaceEventHandler(viewer.scene.canvas);handler.setInputAction((e) => {const clickPosition = viewer.scene.camera.pickEllipsoid(e.position);const randiansPos = Cesium.Cartographic.fromCartesian(clickPosition);console.log("经度:" +Cesium.Math.toDegrees(randiansPos.longitude) +", 纬度:" +Cesium.Math.toDegrees(randiansPos.latitude));}, Cesium.ScreenSpaceEventType.LEFT_CLICK);}
};const fly = (option) => {const lntlon = option.lntlonviewer.camera.flyTo({// 从以度为单位的经度和纬度值返回笛卡尔3位置。destination: Cesium.Cartesian3.fromDegrees(...lntlon, 40000),orientation: {// heading:默认方向为正北,正角度为向东旋转,即水平旋转,也叫偏航角。// pitch:默认角度为-90,即朝向地面,正角度在平面之上,负角度为平面下,即上下旋转,也叫俯仰角。// roll:默认旋转角度为0,左右旋转,正角度向右,负角度向左,也叫翻滚角heading: Cesium.Math.toRadians(0.0), // 正东,默认北pitch: Cesium.Math.toRadians(-90), // 向正下方看roll: 0.0, // 左右},duration: 3, // 飞行时间(s)})
}
const setView = (option) => {const lntlon = option.lntlonviewer.camera.setView({destination: Cesium.Cartesian3.fromDegrees(...lntlon, 40000),})
}
// 相机飞行代码
const cameraLocation = (locationMode = LocationEnum.FLY, lntlon) => {if (locationMode === LocationEnum.FLY) {fly({lntlon})} else if (locationMode === LocationEnum.SET_VIEW) {setView({ lntlon })}
}const renderMarks = async (json, imgName) => {const imgRes = await getImg({fileName: imgName})let imgUrl = ""if (imgRes.data) {imgUrl = imgRes.data.imgUrl}if (json?.length) {formatJsonData(json, imgUrl)}
}
// 打点代码
const formatJsonData = (features, img) => {const billboardsCollection = viewer.scene.primitives.add(new Cesium.BillboardCollection());for (let i = 0; i < features.length; i++) {const feature = features[i];const coordinates = feature.geometry.coordinates;const position = Cesium.Cartesian3.fromDegrees(coordinates[0],coordinates[1],100);const name = feature.properties.name;// 带图片的点billboardsCollection.add({image: img,width: 32,height: 32,position,});}
}
</script>
<style lang='scss' scoped>
#cesiumContainer {position: absolute;top: 0;bottom: 0;left: 0;right: 0;
}
</style>

config.ts

这个文件是cesium的配置项文件,也就是这个模块的内容;

import { PublicConfigClass } from '@/packages/public'
import { CreateComponentType } from '@/packages/index.d'
import { CesiumBaseConfig } from './index'
import { chartInitConfig } from '@/settings/designSetting'
import cloneDeep from 'lodash/cloneDeep'
import dataJson from "./data.json"
import type { UploadFileInfo } from 'naive-ui'// 定位模式
export enum LocationEnum {FLY = "fly", // 飞行SET_VIEW = "setView" // 直接定位
}export const option = {dataset: dataJson,viewOptions: {center: "", // 中心点坐标locationMode: LocationEnum.FLY, // 定位模式markImgUrl: "",markImgList: [] as UploadFileInfo[],geojsonFileName: "", // geojson文件名geojsonFileList: [] as UploadFileInfo[], // json列表// markGeojsonData: {}, // mark geojson},
}export default class Config extends PublicConfigClass implements CreateComponentType {public key = CesiumBaseConfig.keypublic attr = { ...chartInitConfig, w: 1000, h: 800, zIndex: -1 }public chartConfig = cloneDeep(CesiumBaseConfig)public option = cloneDeep(option)
}

config.vue

OK,配置项准备好之后,我们再继续画这个配置模块的vue页面。

<template><collapse-item name="基础配置" :expanded="true" key="baseSetting"><setting-item-box name="中心坐标" :alone="true"><setting-item><n-input placeholder="例如 120.36, 36.09" v-model:value="optionState.center" size="small"></n-input></setting-item></setting-item-box><setting-item-box name="定位模式" :alone="true"><setting-item><n-select v-model:value="optionState.locationMode" :options="locationOpts" /></setting-item></setting-item-box><setting-item-box><n-button type="primary" @click="handleSave">保存</n-button></setting-item-box></collapse-item>
</template><script setup lang="ts">
import { PropType, reactive, nextTick, ref, computed } from 'vue'
import { option, LocationEnum } from './config'
import { CollapseItem, SettingItemBox, SettingItem } from '@/components/Pages/ChartItemSetting'
import cloneDeep from 'lodash/cloneDeep'const props = defineProps({optionData: {type: Object as PropType<typeof option>,required: true}
})
const locationOpts = [{label: "飞行定位",value: LocationEnum.FLY,},{label: "直接定位",value: LocationEnum.SET_VIEW,}
]// 此处深拷贝是为了当viewOptions是一个深层次的对象时,深层次的引用改变能够不直接影响props.optionData.viewOptions
const cloneViewOpts = cloneDeep(props.optionData.viewOptions)
const optionState = reactive(cloneViewOpts)const handleSave = () => {props.optionData.viewOptions = Object.assign(props.optionData.viewOptions, optionState)
}
</script>

组件大类新增Cesium选项卡

也就是这里。

src/packages/index.ts中在大类组件列表中配置cesium模块:

// * 所有图表
export let packagesList: PackagesType = {//...[PackagesCategoryEnum.CESIUM]: CesiumList,
}

接着在src/views/chart/ContentCharts/hooks/useAside.hook.ts侧边栏渲染的hooks文件中配置cesium名称和图标:

const packagesListObj = {//...[PackagesCategoryEnum.CESIUM]: {icon: renderIcon(SpellCheckIcon),label: PackagesCategoryName.CESIUM}
}

OK,经过以上配置,我们其实就已经完成了cesium在goview中的嵌套,只不过还有一个问题是,当我点击页面预览的时候,在预览界面cesium没有渲染成功,报错:cesiumContainer这个dom元素找不到

cesium组件预览

玩过cesium的应该都知道,cesium在技术上的渲染底层是基于canvas,canvas的渲染需要基于基础元素,由于低代码平台的原理,所有组件渲染都是基于全局组件动态渲染去完成的。

而goview的预览是新开一个浏览器标签页,因此会导致cesium初始化渲染的时候无法找到cesiumContainer这个dom元素

因此,我们需要重点修改一下预览界面文件的动态组件component的配置,当检测到是cesium组件时,需要提前定义好元素id为cesiumContainer
src/views/preview/components/PreviewRenderList/index.vue

<!-- 此处chartkey为VCesiumBase时可以区分是因为在预览页面找不到对应container --><component v-else :is="item.chartConfig.chartKey":id="item.chartConfig.chartKey === 'VCesiumBase' ? 'cesiumContainer' : item.id" :chartConfig="item":svgEl="item.props?.svgEl" :themeSetting="themeSetting" :themeColor="themeColor":style="{ ...getSizeStyle(item.attr) }" v-on="useLifeHandler(item)"></component>

这样,问题就迎刃而解。

总结

如果想对goview进行二开,首先得必须了解整个项目架构,这个开源整体的代码质量还是不错的,文件分布比较合理清晰,对于二开的小伙伴来说非常友好,其实跟平常自己新封装一个组件的难度基本无差。

我把二开的代码已上传到github,有需要的小伙伴自取,如果觉得有帮助请star,以鼓励和支持我持续开源下去。

【开源地址】:https://github.com/tingyuxuan2302/goview-fe

有需要进技术产品开发交流群(可视化&GIS)可以加我:brown_7778,也欢迎数字孪生可视化领域的交流合作。

最后,如果觉得文章对你有帮助,也希望可以一键三连👏👏👏,支持我持续开源和分享~

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

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

相关文章

TCP编程

自学python如何成为大佬(目录):https://blog.csdn.net/weixin_67859959/article/details/139049996?spm1001.2014.3001.5501 由于TCP连接具有安全可靠的特性&#xff0c;所以TCP应用更为广泛。创建TCP连接时&#xff0c;主动发起连接的叫客户端&#xff0c;被动响应连接的叫服…

【TIM输出比较】

TIM输出比较 1.简介1.1.输出比较功能1.2.PWM 2.输出比较通道2.1.结构原理图2.2.模式分类 3.输出PWM波形及参数计算4.案例所需外设4.1.案例4.2.舵机4.3.直流单机 链接: 15-TIM输出比较 1.简介 1.1.输出比较功能 输出比较&#xff0c;英文全称Output Compare&#xff0c;简称O…

AI 情感聊天机器人之旅 —— 相关论文调研

开放域闲聊场景 Prompted LLMs as Chatbot Modules for Long Open-domain Conversation 发布日期&#xff1a;2023-05-01 简要介绍&#xff1a;作者提出了 MPC&#xff08;模块化提示聊天机器人&#xff09;&#xff0c;这是一种无需微调即可创建高质量对话代理的新方法&…

【转载】使用 .NET Upgrade Assistant(升级助手)升级 .NET 老旧版本项目

使用 .NET Upgrade Assistant&#xff08;升级助手&#xff09;升级 .NET 老旧版本项目&#xff1a;https://blog.csdn.net/ChaITSimpleLove/article/details/134711604

【QT问题解决】QT Modbus rtu 拖动主界面时modbus的槽函数无法响应的解决方案

目录 问题 Modbus放在主线程&#xff0c;界面事件会阻塞信号传输解决方案 将modbus放在子线程实现 问题 Modbus放在主线程&#xff0c;界面事件会阻塞信号传输 在使用QT5.14.2时 使用QT自带的QModbusClient类实现对一个力传感器的数据读取。本人为了测试就将modbus读取逻辑等都…

各种编码格式和网站工具

1、url编码 格式如&#xff1a; 中文 > %E4%B8%AD%E6%96%87 hello > hello [] > %5B%5D 在线工具&#xff1a;https://www.bejson.com/enc/urlencode/ 2、utf8编码 格式如&#xff1a; 中文 > \xe4\xb8\xad\xe6\x96\x87 hello > \x68\x65\x6c\x6c\x6f [] >…

基于深度学习的向量图预测

基于深度学习的向量图预测 向量图预测&#xff08;Vector Graphics Prediction&#xff09;是计算机视觉和图形学中的一个新兴任务&#xff0c;旨在从像素图像&#xff08;栅格图像&#xff09;生成相应的向量图像。向量图像由几何图形&#xff08;如线条、曲线、多边形等&…

【网络安全产品】---网闸

了解了不少安全产品&#xff0c;但是对网闸的理解一直比较模糊&#xff0c;今天 what 网闸是安全隔离与信息交换系统的简称&#xff0c;使得在不影响数据正常通信的前提下&#xff0c;让络在不连通的情况下数据的安全交换和资源共享&#xff0c;对不同安全域/网络之间实现真正…

u8g2 使用IIC驱动uc1617 lcd有时候某些像素显示不正确

折腾了很久&#xff0c;本来lcd是挂载到已经存在的iic总线上的&#xff0c;总线原来是工作正常的&#xff0c;挂载之后lcd也能显示&#xff0c;但是有时候显示不正确&#xff0c;有时候全白的时候有黑色的杂点。 解决方案&#xff1a; 1.最开始以为是IIC总线速度快&#xff0…

【机器学习】从理论到实践:决策树算法在机器学习中的应用与实现

&#x1f4dd;个人主页&#xff1a;哈__ 期待您的关注 目录 &#x1f4d5;引言 ⛓决策树的基本原理 1. 决策树的结构 2. 信息增益 熵的计算公式 信息增益的计算公式 3. 基尼指数 4. 决策树的构建 &#x1f916;决策树的代码实现 1. 数据准备 2. 决策树模型训练 3.…

Vim基础操作:常用命令、安装插件、在VS Code中使用Vim及解决Vim编辑键盘错乱

Vim模式 普通模式&#xff08;Normal Mode&#xff09;&#xff1a; 这是 Vim 的默认模式&#xff0c;用于执行文本编辑命令&#xff0c;如复制、粘贴、删除等。在此模式下&#xff0c;你可以使用各种 Vim 命令来操作文本。插入模式&#xff08;Insert Mode&#xff09;&#…

【Python日志模块全面指南】:记录每一行代码的呼吸,掌握应用程序的脉搏

文章目录 &#x1f680;一、了解日志&#x1f308;二、日志作用&#x1f308;三、了解日志模块⭐四、日志级别&#x1f4a5;五、记录日志-基础❤️六、记录日志-处理器handler&#x1f3ac;七、记录日志-格式化记录☔八、记录日志-配置logger&#x1f44a;九、流程梳理 &#x…

如何在linux中下载R或者更新R

一、问题阐述 package ‘Seurat’ was built under R version 4.3.3Loading required package: SeuratObject Error: This is R 4.0.4, package ‘SeuratObject’ needs > 4.1.0 当你在rstudio中出现这样的报错时&#xff0c;意味着你需要更新你的R 的版本了。 二、解决方…

Openldap集成Kerberos

文章目录 一、背景二、Openldap集成Kerberos2.1kerberos服务器中绑定Ldap服务器2.1.1创建LDAP管理员用户2.1.2添加principal2.1.3生成keytab文件2.1.4赋予keytab文件权限2.1.5验证keytab文件2.1.6增加KRB5_KTNAME配置 2.2Ldap服务器中绑定kerberos服务器2.2.1生成LDAP数据库Roo…

第二十五篇——信息加密:韦小宝说谎的秘诀

目录 一、背景介绍二、思路&方案三、过程1.思维导图2.文章中经典的句子理解3.学习之后对于投资市场的理解4.通过这篇文章结合我知道的东西我能想到什么&#xff1f; 四、总结五、升华 一、背景介绍 加密这件事&#xff0c;对于这个时代的我们来说非常重要&#xff0c;那么…

ElasticSearch学习笔记(二)文档操作、RestHighLevelClient的使用

文章目录 前言3 文档操作3.1 新增文档3.2 查询文档3.3 修改文档3.3.1 全量修改3.3.2 增量修改 3.4 删除文档 4 RestAPI4.1 创建数据库和表4.2 创建项目4.3 mapping映射分析4.4 初始化客户端4.5 创建索引库4.6 判断索引库是否存在4.7 删除索引库 5 RestClient操作文档5.1 准备工…

Android RecyclerView原理语法和用法

RecyclerView 是 Android 中用于显示大量数据的高级视图组件。它是 ListView 的改进版&#xff0c;具有更高的性能和灵活性。以下是关于 RecyclerView 的详细介绍&#xff0c;包括其原理、语法、优缺点和使用方法。 原理 RecyclerView 的核心原理是通过视图复用来提高性能。当…

什么是Java中的单例模式?请列举几种常见的单例实现方式,并讨论其优缺点。请解释Java中的设计模式,并列举几种常见的设计模式及其应用场景。

什么是Java中的单例模式&#xff1f;请列举几种常见的单例实现方式&#xff0c;并讨论其优缺点。 Java中的单例模式是一种创建型设计模式&#xff0c;它确保一个类只有一个实例&#xff0c;并提供一个全局访问点来访问这个实例。这种模式在多线程环境中特别有用&#xff0c;因…

融资A轮B轮是什么意思?

环境&#xff1a; 融资 问题描述&#xff1a; 融资A轮B轮是什么意思 解决方案&#xff1a; 在A轮融资之前&#xff0c;通常有以下几轮融资阶段&#xff1a; 种子轮&#xff08;Seed Round&#xff09;&#xff1a;这是企业生命周期中最早的融资阶段&#xff0c;通常发生在…

模拟原神圣遗物系统-小森设计项目,设计圣遗物(生之花,死之羽,时之沙,空之杯,理之冠)抽象类

分析圣遗物 在圣遗物系统&#xff0c;玩家操控的是圣遗物的部分 因此我们应该 物以类聚 人与群分把每个圣遗物的部分&#xff0c;抽象出来 拿 生之花&#xff0c;死之羽为例 若是抽象 类很好的扩展 添加冒险家的生之花 时候继承生之花 并且名称冒险者- 生之花 当然圣遗物包含…