ToB企服应用市场:ToB评测及商务社交产业平台

标题: 【WebGis开发 - Cesium】三维可视化项目教程---图层管理基础 [打印本页]

作者: 知者何南    时间: 2024-12-4 22:49
标题: 【WebGis开发 - Cesium】三维可视化项目教程---图层管理基础
引言

   本教程主要是围绕Cesium这一开源三维框架开展的可视化项目教程。总结一下相干从业经验,如果有什么疑问或更好的见解,接待批评、私聊探讨,共同进步。教程依托于vue3前端框架,参考初始化内容:【WebGis开发 - Cesium】三维可视化项目教程—初始化场景
  本篇主要讨论如何管理二三维图层,仅以wmts和3dtiles做图层管理示例。我会在接下来的教程里专门针对其他各类图层加载方式做详细介绍。
二三维图层管理是三维可视化项目里紧张的组成部分,通过树结构管理图层,根据模块计划加载对应二三维数据。
先看效果:


一、功能计划

1. 主体功能

2. 细节题目

   图层数据大抵上可以分为二维图层和三维图层
  在三维引擎中针对这两者的表达方式有所差别


二、代码实现

1. 树形控件

树形控件采用 element-plus 组件库的 tree 组件。细节查阅:Tree 树形控件
几个必要注意的点:

html部分
  1.     <div class="layerManager">
  2.       <div>图层管理</div>
  3.       <el-tree
  4.         :data="dataSource"
  5.         show-checkbox
  6.         node-key="id"
  7.         default-expand-all
  8.         check-on-click-node
  9.         @check-change="checkChange"
  10.         :expand-on-click-node="false"
  11.       >
  12.       </el-tree>
  13.     </div>
复制代码
数据部分
二维数据使用天地图影像、矢量、注记三种。三维数据使用本地3dtiles数据(有在线数据替换一下)。
天地图wmts图层引用示例可以参考:天地图服务 ,在页面最下方有请求示例。

树节点数据主要包含几个内容

  1. const dataSource = ref([
  2.   {
  3.     id: "1",
  4.     label: "二维地图",
  5.     children: [
  6.       {
  7.         id: "1-1",
  8.         label: "天地图影像",
  9.         type: "wmts",
  10.         url: "https://t0.tianditu.gov.cn/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=你申请的天地图key",
  11.       },
  12.       {
  13.         id: "1-2",
  14.         label: "天地图矢量",
  15.         type: "wmts",
  16.         url: "https://t0.tianditu.gov.cn/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=你申请的天地图key",
  17.       },
  18.       {
  19.         id: "1-3",
  20.         label: "天地图矢量注记",
  21.         type: "wmts",
  22.         url: "https://t0.tianditu.gov.cn/cva_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cva&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=你申请的天地图key",
  23.       },
  24.     ],
  25.   },
  26.   {
  27.     id: "2",
  28.     label: "三维地图",
  29.     children: [
  30.       {
  31.         id: "2-1",
  32.         label: "测试3dtiles模型",
  33.         type: "3dtiles",
  34.         // 我这里准备一份本地3dtiles数据在 `public/3dtiles/` 目录下
  35.         url: "/3dtiles/test/tileset.json",
  36.       },
  37.     ],
  38.   },
  39. ]);
复制代码
2. 全局状态预备

   使用 pinia 来管理全局状态
  pinia的安装引入操作不外多赘述,自行查阅。
在 src/stores/ 目录下创建文件 layer.js
根据cesium提供的加载图层方法,可以发现

所以我们创建两个全局状态数组 imageryLayers 和 primitiveLayers ,分别存储二维图层和三维图层。同时对应创建添加和查询两个action方法,用于保存和查询图层的紧张信息(不必要把所有信息都存起来,只要存储特性值可以确保能在viewer中查询到对应图层即可)
  1. import { defineStore } from "pinia";
  2. export const useLayerStore = defineStore("LayerStore", {
  3.   state: () => ({ imageryLayers: [], primitiveLayers: [] }),
  4.   actions: {
  5.     addImageryLayer(data) {
  6.       this.imageryLayers.push(data);
  7.     },
  8.     getImageryLayer(id) {
  9.       return this.imageryLayers.find((item) => item.id === id);
  10.     },
  11.     addPrimitiveLayer(data) {
  12.       this.primitiveLayers.push(data);
  13.     },
  14.     getPrimitiveLayer(id) {
  15.       return this.primitiveLayers.find((item) => item.id === id);
  16.     },
  17.   },
  18. });
复制代码
3. 创建图层控制方法

   出于功能的可复用性思量,将图层管理的方法封装为hooks函数
  主要包含几个内容:
3.1 加载、卸载方法编写

这里不深究对应图层加载api的使用方法,我会在之后的文章里详细介绍各图层加载方法的api使用方法以及常用属性填写。
描述一下我的编写逻辑:
  1.   import { useLayerStore } from "@/stores/layer.js";
  2.   import { GenerateId } from "@/utils/cesium/common.js";
  3.   const layerStore = useLayerStore();
  4.   /**
  5.    * @description: 添加wmts图层
  6.    * @param {*} data
  7.    * @return {*}
  8.    */
  9.   const addWmtsLayer = (data) => {
  10.     // 先查询是否已经加载图层
  11.     const layerData = layerStore.getImageryLayer(data.id);
  12.     // 存在图层数据直接显示图层, 并返回
  13.     if (layerData) {
  14.       const layer = viewer.imageryLayers._layers.find(
  15.         (item) => item.layerId === layerData.layerId
  16.       );
  17.       layer.show = true;
  18.       layerData.show = true;
  19.       return;
  20.     }
  21.     // 不存在图层数据则重新加载图层
  22.     const imageMap = new Cesium.WebMapTileServiceImageryProvider({
  23.       url: data.url
  24.     });
  25.     // 添加图层
  26.     const layer = viewer.imageryLayers.addImageryProvider(imageMap);
  27.     // 添加图层标识
  28.     layer.layerId = GenerateId(18);
  29.     // 向全局状态输入图层数据
  30.     layerStore.addImageryLayer({ ...data, show: true, layerId: layer.layerId });
  31.   };
  32.   /**
  33.    * @description: 移除wmts图层
  34.    * @param {*} data
  35.    * @return {*}
  36.    */
  37.   const removeWmtsLayer = (data) => {
  38.     const layerData = layerStore.getImageryLayer(data.id);
  39.     if (layerData) {
  40.       //  获取图层数据并设置显示为false
  41.       const layer = viewer.imageryLayers._layers.find(
  42.         (item) => item.layerId === layerData.layerId
  43.       );
  44.       layer.show = false;
  45.       layerData.show = false;
  46.     }
  47.   };
  48.   /**
  49.    * @description: 添加3dtiles图层
  50.    * @param {*} data
  51.    * @return {*}
  52.    */
  53.   const add3dtilesLayer = async (data) => {
  54.     // 先查询是否已经加载图层
  55.     const layerData = layerStore.getPrimitiveLayer(data.id);
  56.     // 存在图层数据直接显示图层, 并返回
  57.     if (layerData) {
  58.       const layer = viewer.scene.primitives._primitives.find(
  59.         (item) => item.layerId === layerData.layerId
  60.       );
  61.       layer.show = true;
  62.       layerData.show = true;
  63.       return;
  64.     }
  65.     const tileset = await Cesium.Cesium3DTileset.fromUrl(data.url);
  66.     // 添加图层标识
  67.     tileset.layerId = GenerateId(18);
  68.     viewer.scene.primitives.add(tileset);
  69.     // 向全局状态输入图层数据
  70.     layerStore.addPrimitiveLayer({
  71.       ...data,
  72.       show: true,
  73.       layerId: tileset.layerId,
  74.     });
  75.     viewer.flyTo(tileset);
  76.   };
  77.   /**
  78.    * @description: 移除3dtiles图层
  79.    * @param {*} data
  80.    * @return {*}
  81.    */
  82.   const remove3dtilesLayer = (data) => {
  83.     const layerData = layerStore.getPrimitiveLayer(data.id);
  84.     if (layerData) {
  85.       const layer = viewer.scene.primitives._primitives.find(
  86.         (item) => item.layerId === layerData.layerId
  87.       );
  88.       layer.show = false;
  89.       layerData.show = false;
  90.     }
  91.   };
复制代码
3.2 统一对外袒露入口

   继承在3.1编写的hooks函数内添加子方法及变量。
  将差别类型的方法按照加载和卸载分为两类,以图层种别和方法作为 键值对 存储起来。
编写统一加载、卸载入口,通过图层种别去选择指定方法对图层进行加载和卸载操作。
  1.   /**
  2.    * @description: 添加图层方法map,用于存储不同图层加载方法
  3.    * @return {*}
  4.    */
  5.   const addLayerFunctions = {
  6.     "wmts": addWmtsLayer,
  7.     "3dtiles": add3dtilesLayer,
  8.     // 其他类型对应的类别和方法
  9.     // ...
  10.   };
  11.   /**
  12.    * @description: 移除图层方法map,用于存储不同图层卸载方法
  13.    * @return {*}
  14.    */
  15.   const removeLayerFunctions = {
  16.     "wmts": removeWmtsLayer,
  17.     "3dtiles": remove3dtilesLayer,
  18.     // 其他类型对应的类别和方法
  19.     // ...
  20.   };
  21.   /**
  22.    * @description: 添加图层入口函数,根据图层类型,分配对应加载函数
  23.    * @param {*} data
  24.    * @return {*}
  25.    */
  26.   const addLayer = (data) => {
  27.     return addLayerFunctions[data.type](data);
  28.   };
  29.   /**
  30.    * @description: 移除图层入口函数,根据图层类型,分配对应卸载函数
  31.    * @param {*} data
  32.    * @return {*}
  33.    */
  34.   const removeLayer = (data) => {
  35.     return removeLayerFunctions[data.type](data);
  36.   };
复制代码
3.3 提供图层种别的可拓展性

   继承在3.1编写的hooks函数内添加子方法及变量。
  提供查询和注入两个方法

这样做的好处是包管了hooks函数的封闭性同时可拓展,遵照了封装函数的开闭原则。
  1.   /**
  2.    * @description: 获取已有的图层加载类型
  3.    * @return {*}
  4.    */
  5.   const getAvialableLayerTypes = () => {
  6.     return Object.keys(addLayerFunctions);
  7.   };
  8.   /**
  9.    * @description: 手动添加特殊图层加载、卸载方法以及图层类别
  10.    * @param {*} type
  11.    * @param {*} addFunc
  12.    * @param {*} removeFunc
  13.    * @return {*}
  14.    */
  15.   const addLayerType = (type, addFunc, removeFunc) => {
  16.     if (addLayerFunctions[type]) {
  17.       console.warn("图层方法已存在: " + type);
  18.       return;
  19.     }
  20.     addLayerFunctions[type] = addFunc;
  21.     removeLayerFunctions[type] = removeFunc;
  22.   };
复制代码
3.1 完备代码

  1. import { useLayerStore } from "@/stores/layer.js";import { GenerateId } from "@/utils/cesium/common.js";export const useLayerManager = () => {  const layerStore = useLayerStore();  /**   * @description: 添加wmts图层   * @param {*} data   * @return {*}   */  const addWmtsLayer = (data) => {    // 先查询是否已经加载图层    const layerData = layerStore.getImageryLayer(data.id);    // 存在图层数据直接显示图层, 并返回    if (layerData) {      const layer = viewer.imageryLayers._layers.find(        (item) => item.layerId === layerData.layerId      );      layer.show = true;      layerData.show = true;      return;    }    // 不存在图层数据则重新加载图层    const imageMap = new Cesium.WebMapTileServiceImageryProvider({      url: data.url    });    // 添加图层    const layer = viewer.imageryLayers.addImageryProvider(imageMap);    layer.layerId = GenerateId(18);    // 向全局状态输入图层数据    layerStore.addImageryLayer({ ...data, show: true, layerId: layer.layerId });  };  /**   * @description: 移除wmts图层   * @param {*} data   * @return {*}   */  const removeWmtsLayer = (data) => {    const layerData = layerStore.getImageryLayer(data.id);    if (layerData) {      //  获取图层数据并设置显示为false      const layer = viewer.imageryLayers._layers.find(        (item) => item.layerId === layerData.layerId      );      layer.show = false;      layerData.show = false;    }  };  /**   * @description: 添加3dtiles图层   * @param {*} data   * @return {*}   */  const add3dtilesLayer = async (data) => {    // 先查询是否已经加载图层    const layerData = layerStore.getPrimitiveLayer(data.id);    // 存在图层数据直接显示图层, 并返回    if (layerData) {      //  获取图层数据并设置显示为false      const layer = viewer.scene.primitives._primitives.find(        (item) => item.layerId === layerData.layerId      );      layer.show = true;      layerData.show = true;      return;    }    const tileset = await Cesium.Cesium3DTileset.fromUrl(data.url);    // 添加标识    tileset.layerId = GenerateId(18);    viewer.scene.primitives.add(tileset);    // 向全局状态输入图层数据    layerStore.addPrimitiveLayer({      ...data,      show: true,      layerId: tileset.layerId,    });    viewer.flyTo(tileset);  };  /**   * @description: 移除3dtiles图层   * @param {*} data   * @return {*}   */  const remove3dtilesLayer = (data) => {    const layerData = layerStore.getPrimitiveLayer(data.id);    if (layerData) {      const layer = viewer.scene.primitives._primitives.find(        (item) => item.layerId === layerData.layerId      );      layer.show = false;      layerData.show = false;    }  };  /**   * @description: 添加图层方法map,用于存储差别图层加载方法   * @return {*}   */  const addLayerFunctions = {    "wmts": addWmtsLayer,    "3dtiles": add3dtilesLayer,  };  /**   * @description: 移除图层方法map,用于存储差别图层卸载方法   * @return {*}   */  const removeLayerFunctions = {    "wmts": removeWmtsLayer,    "3dtiles": remove3dtilesLayer,  };  /**   * @description: 添加图层入口函数,根据图层类型,分配对应加载函数   * @param {*} data   * @return {*}   */  const addLayer = (data) => {    return addLayerFunctions[data.type](data);  };  /**   * @description: 移除图层入口函数,根据图层类型,分配对应卸载函数   * @param {*} data   * @return {*}   */  const removeLayer = (data) => {    return removeLayerFunctions[data.type](data);  };  /**
  2.    * @description: 获取已有的图层加载类型
  3.    * @return {*}
  4.    */
  5.   const getAvialableLayerTypes = () => {
  6.     return Object.keys(addLayerFunctions);
  7.   };
  8.   /**
  9.    * @description: 手动添加特殊图层加载、卸载方法以及图层类别
  10.    * @param {*} type
  11.    * @param {*} addFunc
  12.    * @param {*} removeFunc
  13.    * @return {*}
  14.    */
  15.   const addLayerType = (type, addFunc, removeFunc) => {
  16.     if (addLayerFunctions[type]) {
  17.       console.warn("图层方法已存在: " + type);
  18.       return;
  19.     }
  20.     addLayerFunctions[type] = addFunc;
  21.     removeLayerFunctions[type] = removeFunc;
  22.   };
  23.   return {    addLayer,    removeLayer,    getAvialableLayerTypes,    addLayerType,  };};
复制代码
4. hooks函数使用方法

  1. <template>
  2.   <div id="mapContainer">
  3.     <div class="layermanager">
  4.       <div>图层管理</div>
  5.       <el-tree
  6.         :data="dataSource"
  7.         show-checkbox
  8.         node-key="id"
  9.         default-expand-all
  10.         check-on-click-node
  11.         @check-change="checkChange"
  12.         :expand-on-click-node="false"
  13.       >
  14.       </el-tree>
  15.     </div>
  16.   </div>
  17. </template>
  18. <script setup>
  19. import { ref, onMounted } from "vue";
  20. import { initCesiumMap } from "@/utils/cesium/index.js";
  21. import { useLayerManager } from "@/hooks/useLayerManager.js";
  22. const dataSource = ref([
  23.   {
  24.     id: "1",
  25.     label: "二维地图",
  26.     children: [
  27.       {
  28.         id: "1-1",
  29.         label: "天地图影像",
  30.         type: "wmts",
  31.         url: "https://t0.tianditu.gov.cn/img_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=img&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=你申请的key",
  32.       },
  33.       {
  34.         id: "1-2",
  35.         label: "天地图矢量",
  36.         type: "wmts",
  37.         url: "https://t0.tianditu.gov.cn/vec_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=vec&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=你申请的key",
  38.       },
  39.       {
  40.         id: "1-3",
  41.         label: "天地图矢量注记",
  42.         type: "wmts",
  43.         url: "https://t0.tianditu.gov.cn/cva_w/wmts?service=wmts&request=GetTile&version=1.0.0&LAYER=cva&tileMatrixSet=w&TileMatrix={TileMatrix}&TileRow={TileRow}&TileCol={TileCol}&style=default&format=tiles&tk=你申请的key",
  44.       },
  45.     ],
  46.   },
  47.   {
  48.     id: "2",
  49.     label: "三维地图",
  50.     children: [
  51.       {
  52.         id: "2-1",
  53.         label: "测试3dtiles模型",
  54.         type: "3dtiles",
  55.         url: "/3dtiles/test/tileset.json",
  56.       },
  57.     ],
  58.   },
  59. ]);
  60. const { addLayer, removeLayer} = useLayerManager();
  61. onMounted(() => {
  62.   initCesiumMap();
  63. });
  64. const checkChange = (data, isCheck) => {
  65.   // 判断是否为叶子节点
  66.   if (data?.children && data?.children?.length > 0) {
  67.     console.log("非子叶节点");
  68.     return;
  69.   } else {
  70.     if (isCheck) {
  71.       addLayer(data);
  72.     } else {
  73.       removeLayer(data);
  74.     }
  75.   }
  76. };
  77. </script>
复制代码

三、总结

至此图层管理的基础实现脉络已经梳理完毕,但是这些还远远不够,我们仍需关心以下几个题目:

所以说一个完备的模块是亿点点优化而来的。由于篇幅题目,遗留的几个题目我将会在之后的文章中逐步闭环。
再接再厉~

免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。




欢迎光临 ToB企服应用市场:ToB评测及商务社交产业平台 (https://dis.qidao123.com/) Powered by Discuz! X3.4