vue3 + potree 渲染点云数据记录

potree 官网示例
在这里插入图片描述

前置条件:

potree 无法直接加载 LAS,LCD,PLY等格式的点云文件,
需要通过 PotreeConverte 转换为 octree 数据格式,前端渲染中加载转换后的 json 格式

格式转换方向
.las ---- potreeConverter ----> .json

结合 vue3 使用

1、模板 html 引入 potree 官网示例中的所有 js 依赖

potree 示例中的 js 依赖如下:
在这里插入图片描述
确保能在 vue 项目中,window 中有 potree

2、创建渲染视图区域组件

在这里插入图片描述
组件挂载后初始化 viewer

// 全局常量
const Potree = window.Potree;
const THREE = window.THREE;
const i18next = window.i18next;
const TWEEN = window.TWEEN;

  const viewer: Ref = ref(null); // viewer 容器
  // 初始化视图容器
  const initViewer = () => {
    if (!Potree || viewer.value) return;
    let viewerArgs = {
      noDragAndDrop: true,
    };
    viewer.value = new Potree.Viewer(
      document.getElementById(`potree_render_area${areaIdIndex}`),
      viewerArgs,
    );
    // 创建原始引用
    const Viewer = toRaw(viewer.value)
    // 设置裁剪属性
    Viewer.setClipTask(Potree.ClipTask.SHOW_INSIDE);
    Viewer.loadGUI();
    // 禁用 edl
    Viewer.setEDLEnabled(false);
    // 设置视场角
    Viewer.setFOV(60);
    // viewer最大可视点云数量固定
    Viewer.setPointBudget(50 * 1000 * 1000);

    Viewer.loadSettingsFromURL();

    // 设置天背景
    Viewer.setBackground('black');

    Viewer.setDescription('');
    Viewer.setMinNodeSize(0);

    Viewer.setControls(Viewer.earthControls);
    // 官方 自带 GUi
    // this.viewer.loadGUI(() => {
    //   this.viewer.setLanguage('zh');
    //   window.$('#menu_tools').next().show();
    //   window.$('#menu_clipping').next().show();
    //   this.viewer.toggleSidebar();
    // });

    // 注册 Sidebar
    sidebar = new Sidebar(Viewer)
    sidebar.init()
    console.log('viewer initialized');
  };
  
  onMounted(() => {
    nextTick(() => {
      // 挂载后初始化视图
      initViewer();
    });
  });
3、加载点云数据
      Potree.loadPointCloud(path).then((e: any) => {
        // PointCloudOctree实例
        // 该实例有唯一的uuid,可以保存起来方便后续对该实例进行操作
        let pointcloud = e.pointcloud;
        // console.log(pointcloud)
        appStore.setOriginFileUuid(fileId, pointcloud.uuid)

        // 材质
        let material = pointcloud.material;
        // name赋值
        // pointcloud.name = 'fileName';

        // 检查是否有彩色信息
        let hasRGBA =
          pointcloud.getAttributes().attributes.find((a: any) => a.name === 'rgba') !== undefined;
        if (hasRGBA) {
          material.activeAttributeName = 'rgba';
        } else {
          material.activeAttributeName = 'color';
        }

        // 点云点大小
        material.size = 1;
        material.minSize = 1;
        material.maxSize = 16;
        material.pointSizeType = Potree.PointSizeType.FIXED;
        // 设置高程渐变方案
        material.gradient = Potree.Gradients.TURBO;

        // 将点云数据添加到场景
        viewer.value.scene.addPointCloud(pointcloud);
        viewer.value.setBackground('black');
        // 设置相机
        viewer.value.zoomTo(e.pointcloud);
        // 设置控制器
        // viewer.value.setControls(viewer.value.earthControls);
        // 设置视场角
        viewer.value.setFOV(60);
        // 设置最小节点大小,值越小越清晰
        viewer.value.setMinNodeSize(0);
        // 打开高质量渲染
        viewer.value.useHQ = false;

        // 点云数量
        fetch(path)
          .then((response) => response.json())
          .then((jsonData) => {
            // name赋值
            pointcloud.name = jsonData.name;
            // 点云数量
            // 根据导入的点云数据来设置对应的点云数量
            // viewer.value.setPointBudget(jsonData.points);
            // 设置点云数量
            pointcloud.pointBudget = jsonData.points;
            // 设置初始视角
            viewer.value.scene.view.setView(new THREE.Vector3(6.44, -6.70, 5.52), new THREE.Vector3(-0.53, 0.55, -0.63));
            viewer.value.scene.view.lookAt(-0.53, 0.55, -0.63);
          });

      })
4、结合官网提供的 工具栏代码,进行其他深入操作
export class Sidebar {
  viewer: any
  measuringTool: any
  profileTool: any
  volumeTool: any

  constructor(viewer: any) {
    this.viewer = viewer;
    this.measuringTool = viewer.measuringTool;
    this.profileTool = viewer.profileTool;
    this.volumeTool = viewer.volumeTool;
  }

  initScene() {
    // 监听点云追加
    let onPointCloudAdded = (e: any) => {
      let pointcloud: any = e.pointcloud;
      console.log('initScene--pointcloud------>')
      console.log(pointcloud)
      SceneObj.pointclouds.push(pointcloud)
      // let cloudIcon = `${Potree.resourcePath}/icons/cloud.svg`;
      // let node = createNode(pcID, pointcloud.name, cloudIcon, pointcloud);

      pointcloud.addEventListener("visibility_changed", () => {
        console.log(pointcloud.visible)
        if (pointcloud.visible) {
          // tree.jstree('check_node', node);
        } else {
          // tree.jstree('uncheck_node', node);
        }
      });
    };
    // 监听测量对象
    let onMeasurementAdded = (e: any) => {
      let measurement = e.measurement;
      console.log('添加测量对象')
      // console.log(measurement);
      nextTick(() => {
        setTimeout(() => {
          const meItem = {
            checked: true,
            label: measurement.name,
            id: measurement.uuid,
          }
          console.log(e.scene.measurements);
          // 追加测量对象数
          appStore.addMeasurementsTreeData(meItem, measurement.uuid)
          // let icon = Utils.getMeasurementIcon(measurement);
          // createNode(measurementID, measurement.name, icon, measurement);
        }, 0);
      })

    };
    // 监听体积增加
    let onVolumeAdded = (e: any) => {
      let volume = e.volume;
      // let icon = Utils.getMeasurementIcon(volume);
      // let node = createNode(measurementID, volume.name, icon, volume);

      volume.addEventListener("visibility_changed", () => {
        console.log(volume)
        if (volume.visible) {
          // tree.jstree('check_node', node);
        } else {
          // tree.jstree('uncheck_node', node);
        }
      });
    };
    // 监听高度坡面增加
    let onProfileAdded = (e: any) => {
      let profile = e.profile;
      // let icon = Utils.getMeasurementIcon(profile);
      // createNode(measurementID, profile.name, icon, profile);
    };
    // 监听标注事件
    let onAnnotationAdded = (e: any) => {
      let annotation = e.annotation;

      // let annotationIcon = `${Potree.resourcePath}/icons/annotation.svg`;
      // let parentID = this.annotationMapping.get(annotation.parent);
      // let annotationID = createNode(parentID, annotation.title, annotationIcon, annotation);
      // this.annotationMapping.set(annotation, annotationID);

      // annotation.addEventListener("annotation_changed", (e) => {
      //   let annotationsRoot = $("#jstree_scene").jstree().get_json("annotations");
      //   let jsonNode = annotationsRoot.children.find(child => child.data.uuid === annotation.uuid);

      //   $.jstree.reference(jsonNode.id).rename_node(jsonNode.id, annotation.title);
      // });
    };

    // 相机动作
    let onCameraAnimationAdded = (e: any) => {
      const animation = e.animation;

      // const animationIcon = `${Potree.resourcePath}/icons/camera_animation.svg`;
      // createNode(otherID, "animation", animationIcon, animation);
    };

    // 向导图
    let onOrientedImagesAdded = (e: any) => {
      const images = e.images;

      // const imagesIcon = `${Potree.resourcePath}/icons/picture.svg`;
      // const node = createNode(imagesID, "images", imagesIcon, images);

      images.addEventListener("visibility_changed", () => {
        console.log('images.visible', images.visible)
        if (images.visible) {
          // tree.jstree('check_node', node);
        } else {
          // tree.jstree('uncheck_node', node);
        }
      });
    };

    // img 360
    let onImages360Added = (e: any) => {
      const images = e.images;

      // const imagesIcon = `${Potree.resourcePath}/icons/picture.svg`;
      // const node = createNode(imagesID, "360° images", imagesIcon, images);

      images.addEventListener("visibility_changed", () => {
        console.log('360images.visible', images.visible)
        if (images.visible) {
          // tree.jstree('check_node', node);
        } else {
          // tree.jstree('uncheck_node', node);
        }
      });
    };

    // 地质
    const onGeopackageAdded = (e: any) => {
      const geopackage = e.geopackage;

      // const geopackageIcon = `${Potree.resourcePath}/icons/triangle.svg`;
      // const tree = $(`#jstree_scene`);
      // const parentNode = "vectors";

      // for (const layer of geopackage.node.children) {
      //   const name = layer.name;

      //   let shpPointsID = tree.jstree('create_node', parentNode, {
      //     "text": name,
      //     "icon": geopackageIcon,
      //     "object": layer,
      //     "data": layer,
      //   },
      //     "last", false, false);
      //   tree.jstree(layer.visible ? "check_node" : "uncheck_node", shpPointsID);
      // }

    };

    this.viewer.scene.addEventListener("pointcloud_added", onPointCloudAdded);
    this.viewer.scene.addEventListener("measurement_added", onMeasurementAdded);
    this.viewer.scene.addEventListener("profile_added", onProfileAdded);
    this.viewer.scene.addEventListener("volume_added", onVolumeAdded);
    this.viewer.scene.addEventListener("camera_animation_added", onCameraAnimationAdded);
    this.viewer.scene.addEventListener("oriented_images_added", onOrientedImagesAdded);
    this.viewer.scene.addEventListener("360_images_added", onImages360Added);
    this.viewer.scene.addEventListener("geopackage_added", onGeopackageAdded);
    this.viewer.scene.addEventListener("polygon_clip_volume_added", onVolumeAdded);
    this.viewer.scene.annotations.addEventListener("annotation_added", onAnnotationAdded);

    /**
     * 删除操作
     */
    // let onMeasurementRemoved = (e) => {
    //   let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
    //   let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.measurement.uuid);

    //   tree.jstree("delete_node", jsonNode.id);
    // };

    // let onVolumeRemoved = (e) => {
    //   let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
    //   let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.volume.uuid);

    //   tree.jstree("delete_node", jsonNode.id);
    // };

    // let onPolygonClipVolumeRemoved = (e) => {
    //   let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
    //   let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.volume.uuid);

    //   tree.jstree("delete_node", jsonNode.id);
    // };

    // let onProfileRemoved = (e) => {
    //   let measurementsRoot = $("#jstree_scene").jstree().get_json("measurements");
    //   let jsonNode = measurementsRoot.children.find(child => child.data.uuid === e.profile.uuid);

    //   tree.jstree("delete_node", jsonNode.id);
    // };

    // this.viewer.scene.addEventListener("measurement_removed", onMeasurementRemoved);
    // this.viewer.scene.addEventListener("volume_removed", onVolumeRemoved);
    // this.viewer.scene.addEventListener("polygon_clip_volume_removed", onPolygonClipVolumeRemoved);
    // this.viewer.scene.addEventListener("profile_removed", onProfileRemoved);

    // {
    //   let annotationIcon = `${Potree.resourcePath}/icons/annotation.svg`;
    //   this.annotationMapping = new Map();
    //   this.annotationMapping.set(this.viewer.scene.annotations, annotationsID);
    //   this.viewer.scene.annotations.traverseDescendants(annotation => {
    //     let parentID = this.annotationMapping.get(annotation.parent);
    //     let annotationID = createNode(parentID, annotation.title, annotationIcon, annotation);
    //     this.annotationMapping.set(annotation, annotationID);
    //   });
    // }

    const scene = this.viewer.scene;
    for (let pointcloud of scene.pointclouds) {
      onPointCloudAdded({ pointcloud: pointcloud });
    }

    for (let measurement of scene.measurements) {
      onMeasurementAdded({ measurement: measurement });
    }

    for (let volume of [...scene.volumes, ...scene.polygonClipVolumes]) {
      onVolumeAdded({ volume: volume });
    }

    for (let animation of scene.cameraAnimations) {
      onCameraAnimationAdded({ animation: animation });
    }

    for (let images of scene.orientedImages) {
      onOrientedImagesAdded({ images: images });
    }

    for (let images of scene.images360) {
      onImages360Added({ images: images });
    }

    for (const geopackage of scene.geopackages) {
      onGeopackageAdded({ geopackage: geopackage });
    }

    for (let profile of scene.profiles) {
      onProfileAdded({ profile: profile });
    }

    // {
    //   createNode(otherID, "Camera", null, new Camera());
    // }

    this.viewer.addEventListener("scene_changed", (e: any) => {
      // propertiesPanel.setScene(e.scene);

      e.oldScene.removeEventListener("pointcloud_added", onPointCloudAdded);
      e.oldScene.removeEventListener("measurement_added", onMeasurementAdded);
      e.oldScene.removeEventListener("profile_added", onProfileAdded);
      e.oldScene.removeEventListener("volume_added", onVolumeAdded);
      e.oldScene.removeEventListener("polygon_clip_volume_added", onVolumeAdded);
      // e.oldScene.removeEventListener("measurement_removed", onMeasurementRemoved);

      e.scene.addEventListener("pointcloud_added", onPointCloudAdded);
      e.scene.addEventListener("measurement_added", onMeasurementAdded);
      e.scene.addEventListener("profile_added", onProfileAdded);
      e.scene.addEventListener("volume_added", onVolumeAdded);
      e.scene.addEventListener("polygon_clip_volume_added", onVolumeAdded);
      // e.scene.addEventListener("measurement_removed", onMeasurementRemoved);
    });

  }
  init() {
    this.initScene();
  }
}

参考学习:potree 使用手册
鸣谢作者;

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

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

相关文章

【Python】类和对象

类和对象 构造方法封装继承多继承 多态 类: 类是一个模板,描述一类对象的行为和状态。 有了模板我们就可以根据这个模板创建具体的对象。 对象: 对象是类的一个具体实例,有状态和行为。 class 类名称: 类的属性类的行为 # 其中 c…

Python 复杂密码图形化生成工具,支持选择生成10位和12位复杂密码(初版)

代码 #!/usr/bin/env python # -*- coding: utf-8 -*- # Time : 2024/3/26 15:22 # Author : wyq # File : 部署测试.py import random import string from tkinter import *def generate_password(length):characters string.ascii_letters string.digits string.p…

2006-2021年各省能源消费总量数据(无缺失)

2006-2021年各省能源消费总量数据(无缺失) 1、时间:2006-2021年 2、来源:能源年鉴、各省年鉴 3、范围:30个省 4、指标:能源消费总量(万吨标煤) 5、缺失情况:无缺失 …

智能网联汽车自动驾驶数据记录系统DSSAD数据元素

目录 第一章 数据元素分级 第二章 数据元素分类 第三章 数据元素基本信息表 表1 车辆及自动驾驶数据记录系统基本信息 表2 车辆状态及动态信息 表3 自动驾驶系统运行信息 表4 行车环境信息 表5 驾驶员操作及状态信息 第一章 数据元素分级 自动驾驶数据记录系统记录的数…

设计模式-组合模式(Composite Pattern)

1. 概念 组合模式是一种结构型设计模式,它允许将对象组合成树状的层次结构,用来表示“整体-部分”的关系。 2. 原理结构图 原理图 抽象角色(Component):这是组合模式的核心,它定义了树叶和树枝构件的公…

跟TED演讲学英文:The inside story of ChatGPT‘s astonishing potential by Greg Brockman

The inside story of ChatGPT’s astonishing potential Link: https://www.ted.com/talks/greg_brockman_the_inside_story_of_chatgpt_s_astonishing_potential Speaker: Greg Brockman Date:April 2023 文章目录 The inside story of ChatGPTs astonishing potentialIntro…

第100+5步 ChatGPT文献复现:ARIMAX预测肺结核 vol. 5

基于WIN10的64位系统演示 一、写在前面 我们继续往下看,首先例行回顾文章: 《PLoS One》杂志的2023年一篇题目为《A comparative study of three models to analyze the impact of air pollutants on the number of pulmonary tuberculosis cases in …

zdpreact_antdesginpro 研究一下react里面比较流行的一个UI框架,开发后台管理系统

首先看一下最开始的代码: 这里面大部分的东西都可以删掉,比如README,只留下中文的那个就可以了。 之后看看README.md中介绍的特性。 特性 💡 TypeScript: 应用程序级 JavaScript 的语言📜 区块: 通过区块模板快速…

LeetCode 热题 100 题解(二):双指针部分(2)| 滑动窗口部分(1)

题目四:接雨水(No. 43) 题目链接:https://leetcode.cn/problems/trapping-rain-water/description/?envTypestudy-plan-v2&envIdtop-100-liked 难度:困难 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&am…

[数据概念|数据技术]智能合约如何助力数据资产变现

“ 区块链上数据具有高可信度,智能合约将区块链变得更加智能化,以支持企业场景。” 之前鼹鼠哥已经发表了一篇文章,简单介绍了区块链,那么,智能合约又是什么呢?它又是如何助力数据资产变现的呢?…

Python空间分析简明教程

数据世界是一个活生生的、会呼吸的事物。 当一个城市的犯罪率上升时,这是因为现实世界中有人在某个地方犯罪。 有警察局、住宅区和商业区、人口密度以及可以与位置相关联的人的地方。 所有这些东西都存在于数据框和表格之外的世界中。 空间分析使数据科学家能够回答…

成都百洲文化传媒有限公司靠谱吗?怎么样?

随着互联网的迅猛发展,电子商务行业迎来了前所未有的发展机遇。在这个变革的浪潮中,成都百洲文化传媒有限公司凭借其深厚的行业经验和创新的服务模式,正逐渐成为电商服务领域的新领军者。 一、创新引领,塑造电商服务新标准 成都百…

FX110网:Exness平台2024年3月交易量环比增长9%

FX110获知,多资产公司Exness 2024年3月份的客户交易量环比大幅增长9%,达到3.856万亿美元,而上个月为3.534万亿美元。 交易量激增的同时,活跃客户数量不断增加,3月份达到破纪录的836,873位交易者,超过了上个…

51单片机学习笔记——LED点亮

一、独立按键控制LED元器件和原理图 根据厂家给的原理图找到独立按键模块,观察下图我们知道按钮的一个头接GND,一头接IO口。由此可知我们如果需要使用第一个按钮则需要用p31。 二、独立按键控制LED程序 程序编写需要使用到IF else语句 当如果P310时P20…

vue快速入门(十六)事件修饰符

注释很详细&#xff0c;直接上代码 上一篇 新增内容 事件修饰符之阻止冒泡事件修饰符之阻止默认行为 源码 <!DOCTYPE html> <html lang"en"><head><meta charset"UTF-8"><meta name"viewport" content"widthdev…

uniapp开发小程序,点击右上角<重新进入小程序>进行刷新时,设置开屏加载页面

一、需求及问题 问题&#xff1a;使用uniapp开发小程序时&#xff0c;有【学生端】和【企业端】两个入口&#xff0c;一进入小程序默认进入【学生端首页】&#xff0c;但是当前处于【企业端】时&#xff0c;点击右上角<重新进入小程序>进行刷新时&#xff0c;页面默认进…

通过 KEIL 制作 QSPI 接口的外部 Flash 下载算法

1. 引言 随着用户的应用越来越复杂以及 GUI 等需要大存储空间的需求越来越多,很多时候我们需要将代码或数据放在外扩的 Flash 存储空间。但是这样存在一个外部 Flash 烧写的问题,尤其是在应用调试时,需要将代码或数据烧录到外部 Flash。如果调试工具不能够一键烧录,势必会…

ELFK (Filebeat+ELK)日志分析系统

一. 相关介绍 Filebeat&#xff1a;轻量级的开源日志文件数据搜集器。通常在需要采集数据的客户端安装 Filebeat&#xff0c;并指定目录与日志格式&#xff0c;Filebeat 就能快速收集数据&#xff0c;并发送给 logstash 进或是直接发给 Elasticsearch 存储&#xff0c;性能上相…

(vue)el-radio鼠标移入可提示图片

(vue)el-radio鼠标移入可提示图片 效果&#xff1a; <el-form-item label"图表选择"><el-radio-group v-model"formInline.echartType"><el-tooltip v-for"(item, index) of echartTypeOptions" :key"index" placement…

Vue前端框架

1.vue基本使用1 1.vue环境搭建 一般创建vue项目是在cmd命令中用&#xff1a;vue ui 命令&#xff0c;采用ui图形界面的方式直观创建项目。 2.vue基本使用方式&#xff1a;vue组件 3.文本插值 4.属性绑定 5.事件绑定 6.双向绑定 7.条件渲染 2.vue基本使用2 1.axios 安装axios命令…