对mars3d框架研究中的感悟
感悟:之前学习cesium三维框架时没有具体的项目经验,总是感觉数据和组件的逻辑很乱,没有一个确定的规范,每次把项目框架化或者说各部分功能分离化,但最终还是会写到一起去。
但在mars3d中,我通过对框架的学习了解他们是如何将框架分离化的,大体上可以分为3部分:1、工具栏。2、cesium.viewer。3、用于将工具栏信息传递给cesium.viewer的js代码。
其中cesium.viewer唯一化,然后一个js代码和index.vue工具栏绑定组合成一个组件,其中可以有多个组件。
技术选型
- Vue3:开发框架熟悉
- Vite:开发环境
- TypeScript - 熟悉
TypeScript
基本语法 - Es6+ - 熟悉 es6 基本语法
- Ant Design Vue:UI 控件库
- ESlint:代码检查工具
- IconPark:UI 图标库
主要目录说明
mars3d-vue-example
└───src 主要项目代码
│ └───components vue组件代码【重要】
│ └───example 示例代码【重要】
│ └───misc 主要存放ts相关的模块定义
│ └───pages 页面入口
│ └───styles 样式文件
│ └───utils 工具方法
└───public 无需编译构建的静态资源【重要】
│ └───config 项目和功能的配置文件
│ └───img 图片资源
│ └───lib 示例依赖资源
│ └───temp 示例的公共基础代码
│───.eslintrc.js eslint配置文件
│─── package.json 项目配置信息
└─── vite.config.ts vite 配置文件
└─── index.html 列表页入口
└─── editor-vue.html 编辑器页面入口
与示例相关的3个主要目录是:src\example
、public
、src\components
(部分功能依赖)。
示例主目录
- 位置 src/example
- 项目中的每一个示例对应了本目录下的一个 map.js 文件,如果包含 ui 面板,需要创建一个 index.vue
- 一些情况下可以将 example 视为 public 下的一个目录
依赖资源
配置的依赖会在html中按配置顺序加载,注意配置时依赖之间的先后顺序。
- libs 依赖,公共的通用依赖,统一放在
public/lib/
目录下,并由includeLibs.js统一配置。 libs中通常会是一些开源的 js 库,如果示例只依赖 mars3d 相关资源,可省略libs字段。
{
"name": "天地图地形",
"main": "terrain/terrainTDT",
"hasPannel": true,
"libs": ["mars3d", "mars3d-tdt"],
"thumbnail": "b10_terrain_tdt.jpg"
}
通过 includeLibs.js 中的 isLocal 变量,手动控制使用本地资源,还是 CDN 资源
- resources 依赖,一些个性化的资源,在开发过程中提取封装的文件的依赖资源,比如只是单个示例本身使用的一些js、css文件。
{
"name": "POI兴趣点搜索(Cesium原生)",
"main": "control/cesium/geocoder",
"resources": ["cesiumControl.css"],
"thumbnail": "d10_geocoder.jpg"
}
同级目录下只需要配置文件名,不在同级目录的 需配置打包后文件位置的绝对路径如 /example/graphic/apply/typhoon/Typhoon.js
添加新页面
publi/config/thumbnail.json
[
{
"name": "测试演示开发",
"icon": "fa-globe",
"download": true,
"children": [
{
"name": "demo",
"details": "demo演示",
"children": [
{
"name": "demo1",
"thumbnail": "demo.jpg",
"main": "demo/demo1",
"hasPannel": true
}
]
}
]
},
{
"name": "快速开始",
"icon": "fa-globe",
"download": false,
"children": [
{
"name": "创建三维场景",
"details": "演示 new mars3d.Map() 构造场景时支持的一些参数",
"children": [
{
"name": "demo1",
"thumbnail": "demo.jpg",
"main": "map/create/demo1"
},
{
"name": "快速创建地球",
"thumbnail": "map-create-options.jpg",
"main": "map/create/options"
},
{
"name": "根据配置文件创建地球",
"thumbnail": "map-create-json.jpg",
"main": "map/create/json"
},
在src/example中添加页面
我这里添加了一个demo文件,有
src/example/demo/demo1/map.js
src/example/demo/demo1/index.vue
map.js:
import * as mars3d from "mars3d"
export let map // mars3d.Map三维地图对象
// 需要该接口来将js给cesium组件发送消息
export const eventTarget = new mars3d.BaseClass()
// 构造地图主方法【必须】
function initMap(options) {
// 创建三维地球场景
map = new mars3d.Map("mars3dContainer", options)
}
// 需要覆盖config.json中地图属性参数(当前示例框架中自动处理合并)
// export const mapOptions = {
// scene: {
// center: { lat: 30.468743, lng: 116.499464, alt: 67446, heading: 0, pitch: -45 }
// }
// }
// /**
// * 初始化地图业务,生命周期钩子函数(必须)
// * 框架在地图初始化完成后自动调用该函数
// * @param {mars3d.Map} mapInstance 地图对象
// * @returns {void} 无
// */
// export function onMounted(mapInstance) {
// map = mapInstance // 记录首次创建的map
// }
// /**
// * 释放当前地图业务的生命周期函数
// * @returns {void} 无
// */
// export function onUnmounted() {
// map = null
// }
export function loginfo(text) {
console.log(text)
}
export function drawdruis() {
// 画矢量图形
map.graphicLayer.startDraw({
type: "rectangle",
style: {
color: "#007be6",
opacity: 0.8,
outline: false
},
success: function (graphic) {
eventTarget.fire("drawgraphic", { graphicId: graphic.id })
}
})
}
index.vue:
<template>
<!-- 这些本质都是将组件放到原生cesium的viewer的表面上-->
<!-- 所以这部分的ui只负责些工具栏,和cesium主体没有直接交互,直接交互的是map.js -->
<mars-dialog visible="true" right="10" top="10">
<a-space>
<mars-input v-model:value="text" :allowclear="true" @change="onTextChange" />
<mars-button @click="onclickDraw">绘制</mars-button>
<mars-input v-model:value="returnmsg" :allowclear="true" @change="onTextChange" />
</a-space>
</mars-dialog>
</template>
<script setup lang>
import { ref } from "vue"
import * as mapwork from "./map.js"
// 数据区
const text = ref("传递给后台js的数据")
const returnmsg = ref("等待后台js数据")
const onTextchange = function () {
mapwork.loginfo(text.value)
}
// 点击画图之后,js就会画图然后将数据传入到eventTarget中,eventTarget一旦发现数据尝试,就会打印该数据
const onclickDraw = function () {
mapwork.loginfo(text.value)
mapwork.drawdruis()
}
mapwork.eventTarget.on("drawgraphic", function (e) {
console.log(e)
returnmsg.value = e.graphicId
})
</script>
单个示例的内部结构
每个示例都是一个单独的文件夹,均放在src\example
示例目录下的子目录,因为示例众多,建议多级目录来管理。
示例相关文件
示例目录下,一般有2个文件(地图业务与UI解耦):
-
map.js文件:涉及地图业务的操作均写在 map.js 中;
-
index.vue 文件:是一个Vue组件面板,涉及 UI 层面、和地图无关的操作均写在 index.vue 中;
内部构流程图
示例的内部构造处理流程图:
5.3. map.js
和index.vue
各自代码业务分离的原则
原则:
- 涉及地图业务的操作均写在 map.js 中
- 涉及 UI 层面、和地图无关的操作均写在 index.vue 中,vue 中不得使用 mars3d 和 Cesium 开头的类(组件中出除外)
如何区分?
- 删除 index.vue 时不影响地图本身业务,map.js 需要正常运行 !!!
- 删除 map.js 时,index.vue 需要正常展示 UI(除 mapWork 相关操作无响应外) !!!
阅读示例源码和调试学习
示例的目的是演示平台的每个功能点,可以按需求或兴趣去学习每一个示例,
- (1)学习中可以查询相关类的 API 文档
- (2)尝试修改源码中参数、方法等,来体验不同的呈现效果。
开发中常见问题
1. 局域网离线使用时注意事项
平台所有代码层面来说支持离线运行和使用的,但需要注意的是离线时的地图服务的相关处理。
如果局域网内有相关地形、卫星底图服务可以按内网服务类型和 URL 地址替换下config.json
或构造Map的代码中
的默认地形和底图。
如果局域网内没有相关服务,可以按下面处理:
- 修改 config.json 中
terrain
配置中,将已有的"show": true
配置,改为"show": false
- 修改 config.json 中
basemaps
数组配置中,将已有的"show": true
的图层,将该值改为"show": false
,并将单张图片或离线地图加上"show": true
,并修改相关 URL 地址。 - 您也可以参考教程发布三维数据服务进行部署离线地图服务,里面也有一些示例离线数据。
2. src\example\中增加的资源访问 404
因为新增静态资源涉及拷贝,需要重启服务 npm run serve